angular directive data binding happens after ng-change - angularjs

Guys I'm experimenting something.
template
<input my-checkbox type="checkbox" ng-model="object.isChecked" ng-change="triggerChange()" ng-click="triggerClick()">
directive my-checkbox (written in coffeescript)
angular.module('myApp')
.directive('myCheckbox', ()->
return {
restrict: 'A'
replace: true,
template: """
<div>
<input type="checkbox" ng-model="ngModel" ng-change="ngChange()" ng-click="ngClick()">
</div>
"""
scope: {
ngChange: "&"
ngClick: "&"
ngModel: "="
}
}
)
Observation
When you check the checkbox, function triggerChange() fires but, object.isChecked value doesn't change. Then function triggerClick() fires with object.isChecked value changes.
I'm wondering, is it true, the data binding "=" happens after ng-change?

See: ngModelOptions
Allows tuning how model updates are done. Using ngModelOptions you can specify a custom list of events that will trigger a model update and/or a debouncing delay so that the actual update only takes place when a timer expires; this timer will be reset after another change takes place.
For example, try this:
<input type="text" ng-model="term" ng-change="fn(term)" ng-model-options="{debounce: 750}" />

Related

Delayed bind on ngModel

I am trying to create a typeahead directive that does not bind the typed text to the model while typing.
This is as such no problem, but I would like to use the ngModel directive for my binding so I am able to use something similar to
<input type="text" ng-model="model.field" typeahead="sourceForTypeahead" />
instead of my current approach which works as a charm
<input type="text" ng-model="tmpFieldForInput" typeahead="sourceForTypeahead" typeahead-model="model.field" />
I can't figure if it is possible to change the "target" of ng-model internally in the directive so I get the typed input, and then is able to set the external model when an result from the source is selected.
Use ngModelOptions to specify when you'd like to bind the input text to the model:
<input type="text" ng-model="myModel" ng-model-options="{ updateOn: 'blur' }">
<p>Hello {{myModel}}!</p>
There are different events you can trigger on, but in this case, the text will only be bound to the model once the end-user leaves focus from the field.
Additional resources: https://docs.angularjs.org/api/ng/directive/ngModelOptions
Like #lux has mentioned, the right way to go about it is to use ng-model-options
But in your case, the ideal solution would be to do wrap your input in a form and bind on submit:
<form name="myForm">
<input type="text" ng-model="myModel" ng-model-options="{ updateOn: 'submit' }">
<p>Hello {{myModel}}!</p>
<input type="submit" value="Submit">
</form>
This will bind the value to the model only when you click your Submit button. This of course can be put anywhere you please.
I found a solution after looking into an old version of the checkbox-list module.
The solution is to change the ngModel attribute on compile time and make it point to an internal property in the directive and then compile in the postlink method.
I have updated the plunker for others to see the prototype: http://plnkr.co/edit/LbHH2pJGX1Iii8ZqqSie?p=preview
(Stack requires me to post code - so here is the )
app.directive('typeahead', ['$parse', '$compile', function ($parse, $compile) {
return {
priority: 1000,
terminal: true,
scope: {
source: '=typeahead',
ngModel: '='
},
compile: function(tElement, tAttrs) {
tElement.removeAttr("typeahead");
tElement.attr("ng-model", "searchTerm");
return function(scope, element, attrs) {
$compile(element)(scope);
// all my logic here

Passing value to ng-model in custom directive

Right, I'm probably missing something fairly obvious here but here goes. I have a bit of HTML from bootstrap that I want to reuse so I wanted to make it into a custom directive.
<label class="toggle">
<input ng-model='model' type="checkbox" class="toggleInput">
<div class="track">
<div ng-show="test" class="toggle-label on">
{{onText}}
</div>
<div ng-show="!test" class="toggle-label off">
{{offText}}
</div>
<div class="handle"></div>
</div></label>
snippet of html I want to use in my pages that Angular will recompile into the above:
<toggle on-text="On" off-text="Off" ng-model="myModelName"></toggle>
My directive is as follows:
.directive('toggle', function() {
return {
restrict: 'AE',
templateUrl: 'views/toggle.view.html',
replace: true,
scope: {
onText: '#',
offText: '#',
ngModel : '=',
},
};
});
However, when looking at the html markup the ng-model attribute has not changed to 'myModelName' and still shows just 'modal' so hasn't updated.
What am I doing wrong?
Thanks all
Try replacing
<input ng-model='model' type="checkbox" class="toggleInput">
with
<input ng-model='{{ngModel}}' type="checkbox" class="toggleInput">
because you want to bind the value of the incoming model name into the ng-model attribute of the template inside the directive.
Figured it out.... used compile in the link function to dynamically add the model to the elements I needed:
return $compile($('input.toggleInput',element).attr('ng-model', model))(scope);

Angular.js - ng-change not firing when ng-pattern is $invalid

I am using ng-pattern to validate some form fields, and I am using ng-change with it to watch and process any changes, however ng-change (or $scope.$watch) will only fire when the form element is in the $valid state! I'm new to angular, so I don't know how to solve this issue, although I suspect a new directive is the way to go.
How can I get ng-change to fire in both $invalid and $valid form element states, with ng-pattern still setting the form element states as before?
Html:
<div ng-app="test">
<div ng-controller="controller">
<form name="form">
<input type="text" name="textbox" ng-pattern="/^[0-9]+$/" ng-change="change()" ng-model="inputtext"> Changes: {{ changes }}
</form>
<br>
Type in any amount of numbers, and changes should increment.
<br><br>
Now enter anything that isn't a number, and changes will stop incrementing. When the form is in the $invalid state, ng-change doesn't fire.
<br><br>
Now remove all characters that aren't numbers. It will increment like normal again. When the form is in the $valid state, ng-change will fire.
<br><br>
I would like ng-change to fire even when the the form is $invalid.
<br><br>
form.$valid: <font color="red">{{ form.$valid }}</font>
</div>
</div>
Javascript:
angular.module('test', []).controller('controller', function ($scope) {
$scope.changes = 0;
$scope.change = function () {
$scope.changes += 1;
};
});
I have created a working JS Fiddle which shows the problem I am having.
http://jsfiddle.net/JAN3x/1/
By the way, this angular issue also seems to be relevant:
https://github.com/angular/angular.js/issues/1296
You can change the behavior of your input by using ng-model-options.
Just add this attribute to your input and the ng-change event will fire:
ng-model-options="{allowInvalid: true}"
see: https://docs.angularjs.org/api/ng/directive/ngModelOptions
you just need to add
ng-model-options="{ updateOn: 'default' , allowInvalid:'true'}"
this indicates that the model can be set with values that did not validate correctly instead of the default behaviour.
Edit This was answered when ng-model-options was not available. Please see the top-voted answer.
you can write a simple directive to listen input event.
HTML:
<input type="text" name="textbox" ng-pattern="/^[0-9]+$/" watch-change="change()" ng-model="inputtext"> Changes: {{ changes }}
JS:
app.directive('watchChange', function() {
return {
scope: {
onchange: '&watchChange'
},
link: function(scope, element, attrs) {
element.on('input', function() {
scope.$apply(function () {
scope.onchange();
});
});
}
};
});
http://jsfiddle.net/H2EAB/
Inspired by the Li Yin Kong ingenious solution :
His solution has an issue concerning the ndModel update (see the comments of his post).
My fix essentially changes the scope type of the directive. It lets directive access to controller scope (and methods)
Then, watch-change directive does not need an "instruction to eval" (change()) anymore, but only the "name of the controller method to call" (change).
And to get the new value of the input in this function, I pass the context (this = the input itself). So I can get the value or any property of it.
This way, we don't care about ngModel updates (or if the form is invalid, which was another issue of the initial solution : ngModel is deleted if form is invalid)
HTML :
<input type="text" name="textbox" ng-pattern="/^[0-9]+$/" watch-change="change" ng-model="inputtext">
JAVASCRIPT :
app.directive('watchChange', function() {
return {
restrict : 'A',
link: function(scope, element, attrs) {
element.on('input', function(){
scope[attrs.watchChange](this);
})
}
};
});
DEMO : http://jsfiddle.net/msieurtoph/0Ld5p2t4/

AngularJS evaluate $rootScope variable in directive template

I have a directive that creates an input field.
I need to set the ng-model attribute of this input field to the value of a $rootScope
variable.
The reason behind this is that I want the input field to be in the layout, and bind to different models depending on what page is loaded.
I thought I'd set this global variable in each controller and access it in the Directive.
ATM the variable is hard coded
App.run(function($rootScope){
$rootScope.mymodel = 'search.name';
})
And the Directive
Directives.directive('inputFilter', function(){
return{
restrict: 'E',
replace:true,
controller: function($scope, $rootScope){
console.log($scope.mymodel);
console.log($rootScope.mymodel)
},
template: '<input class="filter" type="text" ng-model="mymodel" placeholder="Nach filtern">'
}
});
It gets rendered as
<input class="filter ng-pristine ng-valid" type="text" ng-model="mymodel" placeholder="Filter">
and the text inside the input field is the value of mymodel variable. The console.log shows
search.name
search.name
Could anyone please shed some light on this issue?
What I think you want is
template: '<input class="filter" type="text" ng-model="'
+ $rootScope.mymodel + '" placeholder="Nach filtern">'
Fiddle.
Note that you will need to inject $rootScope into your directive:
Directives.directive('inputFilter', function($rootScope) {

How to make angular-ui inputs cooperate with angularjs attributes (particularly ng-readonly)?

I want to achieve read/write access control for select input using angularjs and angular-ui (particularly ui-select2 feature).
Scenario is simple: by using ng-readonly attribute I can control whether given input value can be changed by user or not.
<input id="clientShortName" class="span4" type="text" ng-readonly="readOnly" ng-model="client.shortName" />
<input ui-select2="{ tags: sometags}" id="clientTagsSelection" class="span4" type="text" ng-readonly="readOnly" ng-model="client.tagsSelection"/>
<input type="button" value="Edit" ng-click="readOnly = !readOnly"/>
This works fine for standard angularjs but when I'm trying to use inputs defined by angular-ui it doesn't work (doesn't change the read/write state of input).
Full scenario is covered here: http://plnkr.co/edit/pKs4Tq
Unfortunately the AngularUI ui-select2 directive has no integration with the angularJS ng-readonly directive.
One way for you to overcome this is to create your own directive and watch for changes on the readOnly property, like this:
app.directive('csReadonly', function() {
return {
restrict: "A",
link: function(scope, iElement, iAttrs, controller) {
scope.$watch(iAttrs.csReadonly, function(readonly) {
iElement.select2(readonly ? 'disable' : 'enable');
});
}
}
});
And use it like this:
<input ui-select2="{ tags: sometags }" cs-readonly="readOnly" ... />
Plunker: http://plnkr.co/edit/LBFDg2
The advantage of the approach is that, if in the future AngularUI decides to include support for the ng-readonly, you will only have to replace cs- with ng- and you're done.

Resources