ng-disabled overrides custom directive behavior - javascript

I'm developing the AngularJS client. I've got a custom directive used as an attribute. The directive checks the access level of the element and sets it to be disabled if the current user is not allowed to use it.
The problem begins when the same element has ng-disabled attribute. In this case the ng-disabled sets the ability of the element, never mind of what I set in my custom directive.
For example I have a button that should be disabled in case the form is invalid. At the same time I'd like to use my custom directive in order to set the button to be disabled if the user doesn't have a permition to use it.
<button ng-disabled="myFrm.myCtrl.$invalid" my-custom-directive="controlName"/>
Inside myCustomDirective I check if the named control is allowed to be activated by the user. If not - I set the disabled attribute to the element. But in case myFrm.myCtrl.$invalid is false ng-disabled removes the disabled attribute and the button is enabled.
Is there any solution to this problem? How can I prevent from ng-disabled to perform its operation?

Does your example directive reflect how you applied your actual directive to the button element? I noticed that your example directive is not following the correct naming convention of making each upper case letter in the directive name a lower case letter with a leading hyphen. i.e. myCustomDirective should be applied to the button like so:
<button ng-disabled="myFrm.myCtrl.$invalid" my-custom-directive="controlName"/>
Otherwise your directive will not be complied or linked for that button.
The only other thing I could think of without seeing the actual code is the priority of the directive. According to the angular docs for the ngDisabled directive:
This directive executes at priority level 100.
By default custom directives have a priority of 0. As long as you have not changed this directive parameter your directive should be compiled after ng-disabled and linked after ng-disabled and therefore have the final say on whether or not the button is disabled or enabled.
-- EDIT --
You should add a watch to myFrm.myCtrl.$invalid as was suggested by another and reapply your directives rules whenever the value changes. You don't need to pass the whole controller into your directive though, you could simply use two way binding on myFrm.myCtrl.$invalid. I am not sure how $watch priority works yet. I am hoping that because your directive will be compiled first it will apply its watch on myFrm.myCtrl.$invalid after ngDisabled does and hopefully that means it will handle value changes of myFrm.myCtrl.$invalid after ngDisable does so that your directive has the final say on what rule is applied.

Finaly I've found a solution to my problem. Instead of watching ng-disabled variable I've totaly diconnected the ng-disabled from its variable. In order to do it I've set the ng-disabled to be allways true in my own directive. My directive is an attribute, so after I've tried some different ways to do it, the solution was to add in my directive constructor the following code:
this.isDisabled = "true";
this.attrs["ngDisabl​ed"] = this.isDisabled;
It's very very important to set the this.isDisabled to "true" as a string and not boolean. Otherwise it doesn't work.

Related

toggle boolean property in angular

I have a checkbox in my view
<input type="checkbox" ng-model="trackers[tracker._id].enable" ng-click="toggleTracker(tracker._id);"/>
And a function in my controller
$scope.toggleTracker = function(trackerId){
$scope.trackers[trackerId].enable = !$scope.trackers[trackerId].enable;
console.log($scope.trackers[trackerId].enable)
}
But each time I check the box the enable property does not change. What could be wrong with my code?
Since you have ng-model connected to the input it should change when you click. Then you also have the ng-click so it changes back to the first value.
Just remove the ng-click and your good.
You shouldn't use both ng-model and an ng-click function toggling the enable state. Maybe it overrides and negate the toggle because they are executed one after the other.
Just use the ng-model property and it should work.

Change select input the angular way

I have a select input with ng-change attribute. In my controller I use the hotkeys module and bind shortcut keys to a function that selects one of the options of the element. The reason I'm not setting the underlying model is that the element has ng-change that I wish to invoke. I tried setting selectedIndex, but that doesn't cause anything to change (not even the result of the underlying model) except the element. Tried to wrap in $apply and got an error. Tried to use $timeout with no delay parameter, and that didn't help either.

Is it possible to chain an event handler programatically in a directive?

I've been trying to figure out a way to trigger the validation routines on an input element when a button is clicked in the same form. I've been looking at a few different ways of doing this. What seems to be the least convoluted is to create a directive that modifies an input button to fire the $validate method on the target form element. I've set this up without too much trouble but I've gotten blocked at how to modify the ngClick event handler so that it triggers the $validate while leaving the original HTML-defined ngClick intact.
I was attempting to use the directive template function to extract the original ngClick method and chain it to the new ngClick function defined in the directive. This started to turn into a mess quite quickly and I'm concerned about how brittle it might be.
Is there a way to intercept the ngClick handler in a directive and to still have the original functionality intact?
Alternately, I'm open to suggestions about how to fire the validation routines on the input field when the button is clicked with minimal involvement of the controller layer.
This is a classical example of an XY-question (if not a double-XY-question).
You don't need to "chain event handlers" (whatever you mean by that). Neither do you need to, I think, trigger the validation manually just because you are validating against external data.
Validation in Angular just runs - and it is not meant to be triggered other than by changing the data.
To add your own custom validator you need to create a directive (which it seems like you did). In that directive you probably need to specify what you are validating against, like an array of strings against which you want to check for duplicates.
Let's say, for simplicity, that you want to validate against another value in the ViewModel. Suppose, this how it would be used:
<input ng-model="bar">
<form name="form1">
<input ng-model="foo" not-equal-to="bar">
</form>
<span ng-show="form1.$error.notEqualTo">error: foo is equal to bar</span>
So, you need to create a directive notEqualTo that adds a validator to the ngModel.$validators pipeline. This directive also needs to $watch for changes to bar and re-set the validity:
app.directive("notEqualTo", function(){
return {
require: "ngModel",
scope: {
notEqualTo: "="
},
link: function(scope, element, attrs, ngModel){
// register "notEqualTo" validator
ngModel.$validators.notEqualTo = function(modelValue){
return validate(modelValue, scope.notEqualTo);
};
// rerun validation on changes to scope.notEqualTo
scope.$watch("notEqualTo", function(){
ngModel.$setValidity("notEqualTo",
validate(ngModel.$modelValue, scope.notEqualTo));
});
function validate(one, other){
return one !== other;
}
}
};
});
plunker

Angular ng-change vs ng-model performance

Is there any performance improvement in using ng-change in an input instead of ng-model?
I pressume that when ng-model is used in an input, then a "$watch" (or similar) is made by angular in a variable, and that adds work load.
But if ng-change is used, then the variable (model) can be updated when needed, code can be executed only when this variable is changed by the input.
This works assuming that only one input can change a variable.
Example follows:
using ng-model
<input type="text" ng-model="ElTexto">
<div ng-show="ElTexto"></div>
using ng-change
html
<input type="text" ng-change="elTexto()">
<div ng-show="ElTexto"></div>
js
$scope.elTexto(){
$scope.ElTexto = true;
}
ng-change requires ng-model, so you can't choose between the two. You must use ng-model, and can also use ng-change if you wish.
Be aware that the two are VERY different. ng-model will keep your input's value and its backing model in perfect sync; ng-change will indicate that the user is interacting with the input. If you care about the value they're changing, like if you're doing autocomplete, just use ng-model and have all your code share the same variable. If you specifically want to take an action when the input changes, regardless of what the value is, then you can use ng-change for that.
ng-change will have batter performance then ng-model. As in every digest cycle ng-model will be evaluated while ng-change will be evaluated on change of input.

AngularJS, how do I make the UI dependent on which field has focus?

I have an AngularJS page with several form inputs.
When the some of the inputs have focus, I want to change other, arbitrary, aspects of the page.
For example, when the user is in the 'stock code' input, I want to display the list of popular stock codes. When they are in the 'qty' field I want to show in-stock quantities and lead times.
Is there a variable which contains the 'current' input (the one which has focus), or do I need to revert to jQuery's onFocus. (It seems a little primitive now.)
How about make a directive? You could set a variable whenever the user leaves/enters the input, and detect that: http://jsfiddle.net/TZnj2/
Be sure to read the directive guide if you're confused as to what's happening: http://docs.angularjs.org/guide/directive
Also I use scope.$apply in that directive, here's an explanation of what that does: http://docs.angularjs.org/api/ng.$rootScope.Scope#$apply
ngFocus and ngBlur are built-in directives:
<input ng-focus="hasFocus = true" ng-blur="hasFocus = false" type="text">
<p ng-show="hasFocus">The field has focus!!</p>
Try demo on jsfiddle

Categories

Resources