Reputation: 191749
I have various inputs (including directives that will be explained later) such as:
<input mask-value="ssn" validate="checkSsn"/>
<input mask-value="pin" validate="checkPin"/>
These properties are in the controller:
app.controller("Ctrl", ['$scope', function ($scope) {
$scope.ssn = "";
$scope.pin = "";
$scope.checkSsn = function () { /* validate $scope.ssn */ };
$scope.checkPin = function () { /* validate $scope.pin */ };
}]);
Finally, the maskValue
directive:
app.directive("maskValue", function () {
return function (scope, element, attrs) {
/* does some focus/blur magic and string replacement */
scope[attrs.maskValue] = this.value;
scope[attrs.validate]();
};
});
This works, but it seems like a misuse of Angular to me. Instead, it would make more sense to use an isolated scope as in:
scope: {validate: "&"}
Then I could use scope.validate()
instead of scope[attrs.validate]()
. However, with the isolated scope I can't update the corresponding value in the controller. Using {maskValue: "="}
does not work because this would try to update the maskValue
property of the parent rather than the specific value. I think that using {ssn: "="}
would work, but then I have to update a specific property of the controller rather than have it be set as a directive attribute which would make the directive inflexible. Apparently using $parent
is not recommended either.
How can I dynamically access properties of the controller inside of an isolated scope directive?
EDIT: I can't use ng-model=ssn
, etc. on the inputs because during the focus/blur events in mask-value
the input's actual value is changed. For example it may become *****####
, but I need to store the actual value #########
somewhere and the controller seems to be the appropriate place since the controller will use it for other reasons later.
Upvotes: 0
Views: 302
Reputation: 14104
I know you've already got an answer to your question, but I think it's worth mentioning that since you appear to be doing some validation, you could use Angular's built-in features to do that and still be able to use ng-model
. Here's an example:
app.directive("maskValue", function ($parse) {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, element, attrs, ngModel) {
if (!ngModel) return;
var validator = $parse(attrs.validator)(scope);
ngModel.$render = function() {
var hasFocus = document.activeElement == element[0];
if (ngModel.$valid || hasFocus) element.val(ngModel.$modelValue)
else element.val('#######');
};
element.bind('blur', function() {
ngModel.$setValidity('maskValue', validator(this.value))
ngModel.$render();
});
element.bind('focus', function() {
ngModel.$render();
});
}
};
});
This directive uses NgModelController in order to work along with ng-model
to control how the view is updated. In this contrived example, it'll simply render ######## when the element loses focus and if the validation function returns false. But when the control gets focused again, it'll render its real value so the user can change it. Notice that the property of the scope bound to the control remains intact while its view changes accordingly to the element state (valid or invalid). You can see a working example here.
Upvotes: 1
Reputation: 29536
<div ng-app=foo>
<div ng-controller=Ctrl>
{{ssn}}
<input mask-value="ssn" validate="checkSsn()">
app = angular.module("foo", []);
app.controller("Ctrl", ['$scope', function ($scope) {
$scope.ssn = "";
$scope.checkSsn = function () { console.log($scope.ssn); };
}]);
app.directive("maskValue", function () {
return {
scope: {
validate: "&",
maskValue: "="
},
link: function (scope, element, attrs) {
element.bind("change", function () {
scope.maskValue = this.value;
scope.$apply();
scope.validate();
});
},
};
});
EDIT:
they say it is better to pass the expression that you want to evaluate as an argument to $apply
because of some error handling peculiarities in Anguular:
var value = this.value;
scope.$apply(function () {
scope.maskValue = value;
});
Upvotes: 2
Reputation: 7729
I'm so sorry but why not just do that :
app = angular.module("foo", []);
app.controller("Ctrl", ['$scope', function ($scope) {
$scope.ssn = "";
$scope.validate = function () { console.log($scope.ssn); };
}]);
app.directive("maskValue", function () {
return {
link: function (scope, element, attrs) {
element.bind("change", function () {
scope.validate();
});
},
};
});
And HTML like that :
<div ng-app=foo>
<div ng-controller=Ctrl>
<input ng-model="ssn" mask-value />
</div>
</div>
Upvotes: 0