У меня есть угловой контроллер:
function SignUpController($window, accountService) {
var vm = this;
vm.errors = null;
vm.user = null;
vm.submit = function (user) {
accountService.signup(user)
.then(function (response) {
$window.location.href = "/";
})
.catch(function (response) {
vm.errors = response.data.errors;
}
};
}
API возвращает список ошибок, если они существуют, следующим образом:
[
{code: "name", "message": "the name is required"}
]
Когда есть ошибки, я добавляю этот список в vm.errors. В HTML у меня есть:
<form name="form" ng-controller="SignUpController as vmsignup" ng-submit="vmsignup.submit(vmsignup.user)">
<input ng-model="vmsignup.user.email" name="vmsignup.user.email" type="text" />
<span class="error" validator="vmsignup.user.email"></span>
<!-- Other form fields -->
</form>
Директива просматривает vm.errors и заполняет все интервалы с помощью валидаторов с правильным сообщением об ошибке.
В моих контроллерах я всегда использую vm.errors, но в своем html я часто использую что-то еще, например vmsignup, поскольку у меня может быть несколько контроллеров.
Поэтому в этом примере директива должна иметь vmsignup.errors, чтобы она работала:
angular.module("app").directive("validator", validator);
validator.$inject = ["$parse"];
function validator($parse) {
var validator = {
link: link,
replace: false,
restrict: "A"
};
return validator;
function link(scope, element, attributes) {
scope.$watch("vmsignup.errors", function (errors) {
if (errors) {
var result = errors.filter(function (error) {
if (error.code == null)
return false;
var position = attributes.validator.lastIndexOf(".");
if (position > -1)
return attributes.validator.slice(position + 1).toLowerCase() === error.code.toLowerCase();
else
return attributes.validator.toLowerCase() === error.code.toLowerCase();
});
if (result.length > 0) {
element.show().text(result[0].message);
return;
}
}
element.hide();
});
}
Я бы хотел, чтобы директива имела что-то вроде:
scope.$watch("vm*.errors", ...
Полагаю, это невозможно, но должен быть способ решить это, нет?
Я думаю, что решение могло бы состоять в том, чтобы иметь новую директиву "валидацию", используемую в форме, чтобы сказать, что такое модель:
<form name="form" ng-controller="SignUpController as vmsignup" validation-model="vmsignup">
Тогда я буду использовать это в директиве validator:
scope.$watch(validator.model + ".errors", function (errors) {
Это хороший вариант? Это можно сделать? Кто-нибудь может мне помочь?
Пожалуйста, попробуйте подход ниже.
var app = angular.module('MyApp',[]);
app.controller('MyCtrl',function($scope){
$scope.errors =1;
$scope.change = function(){
$scope.errors = 2;
}
});
app.directive('myDirecitive',function(){
return {
restrict:'A',
link:linkFunction
}
function linkFunction(scope,element,atrrs){
scope.$watch(atrrs.varName,function(newval,oldval){
console.log("watcher called");
});
}
});
<body ng-app="MyApp", ng-controller="MyCtrl">
<h1>Sample Application</h1>
<input type="text" my-direcitive var-name="errors"/>
<button ng-click="change()">Watch</button>
</body>
Вы не можете использовать *
качестве подстановочного знака в выражении $watch
, но вы можете посмотреть, например, коллекцию с $watchCollection
...
Массив свойств:
$scope.$watchCollection('[prop1, prop2]', function(newValues, oldValues){
var newProp1 = newValues[0];
var newProp2 = newValues[1];
});
Массив функций, возвращающих значение:
$scope.$watchCollection([
function(){
return prop1;
}, function(){
return prop2;
}
], function(newValues, oldValues){
var newProp1 = newValues[0];
var newProp2 = newValues[1];
});
ОБНОВИТЬ
как сказал @Ambegodas, вы можете передать коллекцию errors
качестве параметра в свою директиву. Таким образом, ваш код станет:
angular
.module("app")
.directive("validator", validator);
validator.$inject = ["$parse"];
function validator($parse) {
var validator = {
link: link,
replace: false,
restrict: "A"
};
return validator;
function link(scope, element, attributes) {
scope.$watch(attributes.errors, function (errors) {
if (errors) {
var result = errors.filter(function (error) {
if (error.code == null) return false;
var position = attributes.validator.lastIndexOf(".");
if (position > -1) {
return attributes.validator.slice(position + 1).toLowerCase() === error.code.toLowerCase();
}
return attributes.validator.toLowerCase() === error.code.toLowerCase();
});
if (result.length > 0) {
element.show().text(result[0].message);
return;
}
}
element.hide();
}, true); // << objectEquality == true
}
}
РАЗЛИЧНЫЙ ПРЕДЛАГАЕМЫЙ ПОДХОД
Вы можете создавать проверки на стороне сервера для тех входных данных, которые нельзя проверить только при проверке на стороне клиента.
HTML:
<form name="form" novalidate ng-submit="vm.submit(vm.account)">
<div class="form-group" mt-show-errors>
<label class="control-label" for="username">Username</label>
<input type="text" mt-validate-username class="form-control" name="username" id="username" ng-model="vm.account.username" required>
<div ng-show="form.$submitted || form.username.$touched">
<span ng-show="form.login.$error.required" class="help-block">Username is required</span>
<span ng-show="form.login.$error.mtValidateUsername" class="help-block">Username already taken</span>
</div>
</div>
<!-- more inputs -->
</form>
ДИРЕКТИВА:
angular
.module('app.directives')
.directive('mtValidateUsername', mtValidateUsername);
function mtValidateUsername($timeout, MyAccountService) {
return {
require: 'ngModel',
scope: false,
link: function (scope, element, attrs, modelCtrl) {
var timeoutId;
modelCtrl.$parsers.push(function (inputValue) {
if (!inputValue) return '';
$timeout.cancel(timeoutId);
timeoutId = $timeout(function () {
MyAccountService.validateUsername(inputValue).then(function () {
modelCtrl.$setValidity('mtValidateUsername', true);
}).catch(function(){
modelCtrl.$setValidity('mtValidateUsername', false);
});
}, 1000); // 1 second throttle
return inputValue;
});
}
};
}
Вы можете использовать $ watchGroup для этой цели