Я пытаюсь написать пользовательскую директиву, которая проверяет поле ввода на соответствие другим значениям, которые также должны быть доступны внутри директивы. Я делаю это, используя изолированную область видимости с переменными области видимости. В частности, я хотел бы сравнить клиентскую цену продукта (т. е. его чистую цену) с покупной ценой, и если разница отрицательна (за исключением того, что цена клиента установлена на 0), я бы хотел, чтобы клиент - ввод цены (и окружающая ее форма) недействителен. Вот моя директива:
export class CheckMarkupDirective implements ng.IDirective {
public static create(): ng.IDirective {
return {
restrict: "A",
require: "ngModel",
scope: {
netPrice: "<",
markupAmount: "<"
},
link: (scope: ng.IScope, element: ng.IAugmentedJQuery, attrs: ng.IAttributes, ngModelCtrl: ng.INgModelController) => {
let netPrice: number;
let markupAmount: number;
scope.$watchGroup(["netPrice", "markupAmount"], (newValues, oldValues) => {
[netPrice, markupAmount] = newValues;
if (markupAmount >= 0) {
ngModelCtrl.$setValidity("markup", true);
} else {
ngModelCtrl.$setValidity("markup", netPrice === 0);
}
ngModelCtrl.$validate();
});
}
};
}
}
И вот как я использую его внутри div ng-form, окруженного тегом формы:
<input type="text" id="customer-price" name="customerPrice"
ng-model="ctrl.product.customerPrice"
ng-change="ctrl.customerPriceChangeDetected()"
check-markup markup-amount="ctrl.product.markupAmount"
net-price="ctrl.product.netPrice" />
Это работает после моды, но проблема в том, что часть проверки кажется «неправильной по времени», что означает, что если я ввожу значение, которое приводит к тому, что «разметка» становится отрицательной в первый раз, тогда устанавливается значение формы $invalid к ложному. Но когда следующий ввод будет отрицательным, проверка сработает. Я думаю, что моя проблема в том, что я делаю много вычислений между разными шагами, но мне трудно понять, что вызывает такую неверную проверку. Думаю, я бы хотел, чтобы кто-то с более глубоким знанием механики Angular JS посмотрел и сказал мне, если я делаю что-то явно неправильное. Заранее спасибо и извините, если мое описание немного расплывчато.
Редактировать: Думаю, я бы также включил методы, которые запускаются при ng-change:
public customerPriceChangeDetected(): void {
this.setNetPriceFromCustomerPrice();
this.setMarkup();
this.changeDetected();
}
private setNetPriceFromCustomerPrice(): void {
let customerPrice = this.product.customerPrice;
let vatRate = this.product.vatRate;
let netPrice = (customerPrice / (1 + vatRate));
this.product.netPrice = parseFloat(accounting.toFixed(netPrice, 2));
}
private setMarkup(): void {
let purchasePrice = this.product.purchasePrice;
let markupAmount = this.product.netPrice - purchasePrice;
this.product.markupAmount = markupAmount;
this.product.markupPercent = markupAmount / purchasePrice;
}
public changeDetected(): void {
let isValid = this.validationService ? this.validationService.isValid : false;
this.toggleSaveButton(isValid);
}
Геттер службы проверки в основном возвращает form.$valid и отлично работает для всех моих других пользовательских валидаторов.
Редактировать 2: Добавлен снимок экрана, показывающий, что для окружающего тега ng-form свойство $invalid, по-видимому, установлено как минимум в true:
Изменить 3. Вот транспилированный код JS:
var CheckMarkupDirective = (function () {
function CheckMarkupDirective() {
}
CheckMarkupDirective.create = function () {
return {
restrict: "A",
require: "ngModel",
scope: {
netPrice: "<",
markupAmount: "<"
},
link: function (scope, element, attrs, ngModelCtrl) {
var netPrice;
var markupAmount;
scope.$watchGroup(["netPrice", "markupAmount"], function (newValues, oldValues) {
netPrice = newValues[0], markupAmount = newValues[1];
if (!markupAmount || !netPrice)
return;
if (markupAmount >= 0) {
ngModelCtrl.$setValidity("markup", true);
}
else {
ngModelCtrl.$setValidity("markup", netPrice === 0);
}
//ngModelCtrl.$validate();
});
}
};
};
return CheckMarkupDirective; }());
... и вот урезанная версия моего html:
<form autocomplete="off" class="form-horizontal" role="form" name="productDetailsForm" novalidate data-ng-init="ctrl.setForm(this,'productDetailsForm')">
<div data-ng-form="section2">
<div class="form-group">
<label for="purchase-price" class="col-sm-4 control-label">Purchase price</label>
<div class="col-sm-4">
<input type="text" class="form-control" id="purchase-price" name="purchasePrice"
data-ng-model="ctrl.product.purchasePrice"
data-ng-change="ctrl.purchasePriceChangeDetected();"
data-decimal="Currency" />
</div>
</div>
<div class="form-group">
<label for="vat-rate" class="col-sm-4 control-label">VAT rate</label>
<div class="col-sm-4">
<select class="form-control" id="vat-rate"
data-ng-model="ctrl.product.vatRate"
data-ng-change="ctrl.vatRateChangeDetected()"
data-ng-options="vatRate.value as vatRate.text for vatRate in ctrl.vatRates"></select>
</div>
</div>
<div class="form-group" data-has-error-feedback="productDetailsForm.section2.customerPrice">
<label for="customer-price" class="col-sm-4 control-label">Customer price</label>
<div class="col-sm-4">
<input type="text" class="form-control" id="customer-price" name="customerPrice"
data-ng-model="ctrl.product.customerPrice"
data-ng-change="ctrl.customerPriceChangeDetected();"
data-decimal="Currency"
data-check-markup
data-markup-amount="ctrl.product.markupAmount"
data-net-price="ctrl.product.netPrice" />
<invalid-feedback item="productDetailsForm.section2.customerPrice"></invalid-feedback>
<validation-feedback type="markup" item="productDetailsForm.section2.customerPrice" data-l10n-bind="ADMINISTRATION.PRODUCTS.NET_PRICE.INVALID"></validation-feedback>
</div>
<div class="col-sm-4">
<div class="form-group" style="margin-bottom: 0;">
<label for="net-price" class="col-lg-5 col-md-5 col-sm-5 col-xs-5" style="font-weight: normal; margin-top: 7px;">
<span data-l10n-bind="ADMINISTRATION.PRODUCTS.NET_PRICE"></span>
</label>
<label class="col-lg-7 col-md-7 col-sm-7 col-xs-7" style="font-weight: normal; margin-top: 7px;">
<span id="net-price">{{ ctrl.product.netPrice | currency }}</span>
</label>
</div>
</div>
</div>
<div class="form-group" data-has-error-feedback="productDetailsForm.section2.markup">
<label for="markup-amount" class="col-sm-4 col-xs-4 control-label">Markup</label>
<div class="col-sm-8 col-xs-8">
<label id="markup-percent" class="control-label" data-ng-class="{'text-danger': ctrl.product.markupPercent < 0}">
{{ ctrl.product.markupPercent * 100 | number: 2 }}%
</label>
<label id="markup-amount" class="control-label" data-ng-class="{'text-danger': ctrl.product.markupAmount < 0}">
({{ ctrl.product.markupAmount | currency }})
</label>
</div>
</div>
</div>
I've put breakpoints inside the watch in the directive and for some weird reason the watch doesn't seem to trigger the first time I enter a new value into the customer-price input. Instead I find myself directly inside the changeDetected() method. I'm really confused now. I think the problem has something todo with the ng-change directive triggering before the validation. I probably has a faulty logic there which results in the isValid check of my validation service triggering before the directive has had time to actually alter the validity.
$validate()
. Кроме того, ng-change не вызывается, когда поле помечено как недопустимое, потому что ng-model не будет обновляться при недопустимом значении. Поэтому попробуйте вывести вне формы значениеcustomerPrice/markupAmount/netPrice
, чтобы увидеть, обновляется ли значение. - person Walfrat   schedule 25.12.2016