Как правильно применять область, поэтому заказ не имеет значения

99
9

Я пытаюсь написать unit test для директивы, которая соответствует значению для другого поля ввода. Проблема заключается в том, что если я определяю элемент, который нужно сопоставить, перед элементом, в котором применяется директива, он работает нормально, иначе он терпит неудачу.


Он отлично работает, когда шаблон


tpl = '<input name="verifyNewPassword" ng-model="verifyNewPassword" type="password"/>';
tpl += '<input name="newPassword" ng-model="newPassword" type="password" equals-to="userForm.verifyNewPassword"/>';

и он не работает, когда шаблон


tpl = '<input name="newPassword" ng-model="newPassword" type="password" equals-to="userForm.verifyNewPassword"/>';
tpl+='<input name="verifyNewPassword" ng-model="verifyNewPassword" type="password"/>';

вот моя директива


.directive('equalsTo', function() {
return {
require: 'ngModel',
link: function(scope, elm, attrs, ctrl) {
var sc = scope;
scope.$watch(attrs.ngModel, function() {
var eqCtrl = scope.$eval(attrs.equalsTo);
console.log('Value1: ' + ctrl.$viewValue + ', Value2: ' + eqCtrl.$viewValue);
if (ctrl.$viewValue===eqCtrl.$viewValue || (!!!ctrl.$viewValue && !!!eqCtrl.$viewValue)) {
ctrl.$setValidity('equalsTo', true);
eqCtrl.$setValidity('equalsTo', true);
} else {
ctrl.$setValidity('equalsTo', false);
eqCtrl.$setValidity('equalsTo', false);
}
});
}
};
})

вот мой тестовый код:


describe('Unit: Testing Directives', function() {
var elm, scope;

beforeEach(function() {
module('mctApp');

inject(function($rootScope, $compile) {
scope = $rootScope.$new();
});
});

function compileDirective(tpl) {
if(!tpl) {
tpl = '<input name="newPassword" ng-model="newPassword" type="password" equals-to="userForm.verifyNewPassword"/>';
tpl += '<input name="verifyNewPassword" ng-model="verifyNewPassword" type="password"/>';
}
tpl = '<form name="userForm">' + tpl + '</form>';

inject(function($compile) {
var form = $compile(tpl)(scope);
});

scope.$digest();

}

it('must be valid form as both values are equal', function() {
scope.newPassword = 'abcdef';
scope.verifyNewPassword = 'abcdef';
compileDirective();
expect(scope.userForm.$valid).toBeTruthy();
});
});

спросил(а) 2021-01-25T15:46:06+03:00 4 месяца, 4 недели назад
1
Решение
77

Во-первых, есть лучшее решение UX для вашего модельного сценария, чем синхронизация, проверяющая оба входа. Первое поле (пароль) должно быть проверено только на основе ограничений вашего пароля, и только второе поле (подтверждение пароля) должно быть проверено на равенство с паролем. Это помогает пользователю оставаться разумным при выборе как действительного, так и повторного ввода пароля. (Другими словами, подтверждение пароля является единственным доменом второго ввода. Если вы испортили подтверждение, это не приведет к неожиданному аннулированию предыдущего ввода, который был до этой точки, действительным.)


<input type="password" ng-model="password" required ng-pattern="/^(?=.*\w)(?=.*\W)/">
<input type="password" ng-model="passwordConfirmation" equal-to="password">

Этот подход не только поможет вашему UX, но также поможет избежать необходимости прямого взаимодействия между несвязанными элементами (что редко является хорошей идеей в AngularJS).


Во-вторых, вы приближаетесь к проблеме в стиле Angular. Входы являются частью представления, которое представляет собой просто представление модели и средств для взаимодействия с ней. Таким образом, вместо проверки на другой вход, вы должны проверить его на соответствие модели. Это возможно, теперь, когда вам не нужна директива для путаницы с другим входом.

.directive('equalTo', ['$parse', function ($parse) {
return {
require: 'ngModel',
compile: function (element, attrs) {
var getOtherValue = $parse(attrs.equalTo);

return function link ($scope, $element, $attrs, ngModelCtrl) {
ngModelCtrl.$validators.equalTo = function (value) {
return (value === getOtherValue($scope));
};
};
}
};
}])


(используйте $parsers и $setValidity вместо $validators, если использовать версию AngularJS < 1.3)


Тестирование директивы будет ветерок, так как вам просто нужно настроить модель.


В качестве побочного элемента, даже если вы решите, что действительно хотите проверить оба входа одновременно, вам лучше проверить их по отношению к соответствующему значению модели (используйте директиву equalTo на обоих входах) вместо принудительного прямая связь между диспетчерами сестры.

ответил(а) 2021-01-25T15:46:06+03:00 4 месяца, 4 недели назад
77

Думаю, вам нужно использовать $timeout при использовании области. $timeout берет на себя обработку $digest, поскольку он применяет область действия после выполнения $digest.


Вот как я использовал область применения.

$timeout(function(){
$scope.$apply()
}

В тестах вы можете использовать $timeout.flush() для синхронного сброса очереди отложенных функций.

ответил(а) 2021-01-25T15:46:06+03:00 4 месяца, 4 недели назад
78

Тест завершился неудачно, потому что, когда изначально запускается watch, $viewValue для equals-to ngModelController - NaN, поэтому для корректности поля установлено значение false и форма становится недействительной.


http://plnkr.co/edit/OZVmogR6GT2pIKUrHpuX?p=preview


Когда вы смотрите объект в области, который назначает входы ngModel, - который уже установлен на "abcdef" - ​​часы вызывается только один раз. Если вы посмотрите на ngModel.$viewValue на вход, который вы сравниваете вместо этого, это гарантирует, что начальное состояние всегда будет правильным, независимо от порядка входов в DOM.


Я также утверждаю, что имеет смысл смотреть на это значение, так как оно сравнивается.


.directive('equalsTo', function() {
return {
require: 'ngModel',
link: function(scope, elm, attrs, ctrl) {
var sc = scope;
scope.$watch(attrs.equalsTo + '.$viewValue', function() {
var eqCtrl = scope.$eval(attrs.equalsTo);
console.log('Value1: ' + ctrl.$viewValue + ', Value2: ' + eqCtrl.$viewValue);
if (ctrl.$viewValue===eqCtrl.$viewValue || (!!!ctrl.$viewValue && !!!eqCtrl.$viewValue)) {
ctrl.$setValidity('equalsTo', true);
eqCtrl.$setValidity('equalsTo', true);
} else {
ctrl.$setValidity('equalsTo', false);
eqCtrl.$setValidity('equalsTo', false);
}
});
}
};
})

http://plnkr.co/edit/OZVmogR6GT2pIKUrHpuX?p=preview


Примечание


Если вы используете angular 1.3+, возможно, стоит рассмотреть новый конвейер проверки подлинности как более элегантный способ решить ту же проблему.

ответил(а) 2021-01-25T15:46:06+03:00 4 месяца, 4 недели назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема