ng-change using previous value - javascript

I have the following input
<input style="margin-left:24px;" id="numOfRows" class="setupInput" size="10"
ng-model="rowVal" ng-init="rowVal = gameCtrl.getNumOfRows()" ng-change="gameCtrl.setNumOfRows({{rowVal}})">
The issue I'm running into is when I change the value in the box it's always 1 behind.
EX: current value = 6
Changed value to 3 - nothing happens
Changed value to 2 - {{gameCtrl.getNumOfRows()}} returns 3
Why is it lagging 1 update behind?

You may have couple of issues here:
1) Do not use interpolation ({{}}) in the ng-change expression.
i.e Change
ng-change="gameCtrl.setNumOfRows({{rowVal}})"
to
ng-change="gameCtrl.setNumOfRows(rowVal)"
2) Do not use ng-init for initialization, that is what controller is for, use your controller to initialize the value of rowVal.
3) Make sure you do not have old version of angular which had issues with ng-change/ng-model syncronization.
4) Assuming you are using controller As syntax (and previous statement is wrong) always try to bind properties to the controller instance, i.e use gameCtrl.rowVal. (It could be wrong because it is not clear from the context of the question whether it really should belong to the controller instance or is it inside ng-repeat or something the created the child scope).

Related

Angular performance with function expression

What are the performance implications of running a function within an Angular expression? For example:
<button ng-if="isValid()">Valid</button>
I'm assuming the function isValid() is being run for each digest loop, which occur many times per second. What options are available within Angular for improving the performance here?
One idea would be to run isValid() within a timeout function a few times a second, then setting a scope variable, so I can control the speed of the "digest". Are there other options people use?
I think if function body is just a simple logic comparison, won't affect performance, but if it gets complex, you can try profiling your code via chrome tools to see how much is performing,
Another alternative is also you can try binding the ng-if to a controller scope property instead and modify the value from other part of your controller's code, so maybe you can apply kind of debounce (https://github.com/shahata/angular-debounce) technique to avoid set the scope variable tons of times
It looks like you're checking if a form is valid before submitting. First of all, your ng-if on the button defeats the purpose of using the same function to conditionally apply a class, since it won't even be in the DOM if isValid() returns false.
You should be able to use ng-pattern, ng-change hooks on your inputs to determine validity as the user is entering form values. Then on your button you can apply the class based on the form's validity:
<button ng-class="{
'btn-success': myFormName.$valid,
'btn-danger': !myFormName.$valid
}" ng-bind="(myFormName.$valid) ? 'Valid' : 'Invalid'"></button>
If you want to suppress form submission, you can check validity of the form inside a function with ng-submit on the <form> tag:
<form name="myFormName" ng-submit="myFormName.$valid && mySubmitFunction()">
One way is to run the isValid function within an interval function, setting a simple scope variable within. For example:
$interval(function() {
scope.isvalid = scope.isValid();
}, 100);
This will run every 100ms, but is probably preferable to the number of calls the digest makes. You can also use $watch or $observe to reduce the number of calls.

Binding a Repeat Control to either a session or view scope variable

I have a repeat control which I have bound to a sessionScope variable and it works fine. I am trying to make the control a bit more extensible by dynamically binding it to either a viewScope or a sessionScope variable. I have added a Custom Property to the Control that holds the repeat and have called it scope. The problem is how to bind the repeat to the correct Scoped variable.
On the Repeat control I have selected JavaScript as the binding method and added this code:
switch (compositeData.scope){
case 'ss' :
return sessionScope.ssCat1
break;
case 'vs' :
return viewScope.vsCat1
break
}
I have checked and in my case compositeData.scope = 'ss' and the sessionScope.ssCat1 contains the correct data.
So am I missing something or is this even possible?
Is it not a better way to inject the repeat values in the custom control by a custom parameter
Name it repeat or something else with object as property type.
In your custom control you have
<xp:repeat id="repeat1" rows="30"
var="rowData" style="width:400px" value="#{compositeData.repeat}">
//do what ever you want with a rowData
</xp:repeat>

Angular -> ng-repeat filtered array variable

Below I will put link to my example in Plunker to show you my problem.
In ng-repeat I have used array(filtrowane) to store data from filter result, also I have added ng-change on input for showing the value of filtrowane.length in console. Variable filtrowane is defined in controller, and after typing something It`s showing different values in console and in the view, can you tell me why?
PS. run your web browser console and you will see the difference.
example Plunker
When you change value, first ng-change is called, then $digest starts which will update filtrowane.
If you use such notations:
item in filtrowane = (tablica | filter:search)
do not use filtrowane in controller. Html should be view, so you should not declare any variables there, which you use in model (js).
Look here: http://plnkr.co/edit/QAHlbuZqWX4szglvMP87?p=preview
This code does same as yours, but filtering is done in javascript. A bit more complicated, but things are more clear. (it is also better for big arrays)
Use ng-blur instead of ng-change
<input type="text" ng-model="search" ng-blur="sprawdzFiltr()">
This is due to the ng-change is probably executed before the filter is applied, so the old value will be taken.

Angular ngModel binding on first level method of $scope

This is not exactly a question with some code, but more for my understandings. So please forgive me if this isnt appropriate here..
I was playing with a simple checkbox with an ngModel assigned:
<input type="checkbox" ng-model="someFlag"/>
I expect this to bind the boolean of the checkbox to $scope.someFlag (if controller and everything else configured properly).
And yes, it does work. But there are times where I found that this is not working. Case: When I try to do something when the someFlag changes (for example in a $watch) the value is not really binded.
But then I came accross something a collegua at work mentioned once:
Use a wrapper object
Doing it like that works now without any problems:
<input type="checkbox" ng-model="wrapperObject.someFlag"/>
Watching $scope.wrapperObject.someFlag works as expected.
Now the question: Why??
When ngModel directive is executed, it reads attribute's ng-model value (in this case, "someFlag") and saves it in it's local function scope (not to confuse to angulars $scope). But since a boolean in javascript is a primitive, you cannot pass it by reference, only by value. That means only the $scope.someFlag's value (true or false) get's copied to ngModel, and not the way of accessing and modifying $scope.someFlag from ngModel.
When you use wrapper object, it is passed by reference, meaning, it is the same thing both in $scope.wrapperObject and in ngModel's local function scope, because it points to the same memory address behind the scenes.
I'm not sure if this explanatory enough, but when working with primitive values in angular, you must keep in mind the difference between passing by reference and by value and that when primitive's change, other parts of application might not know this.
In my understanding you want to pass Boolean value if check box checked to controller
try this:
In your html:
<input type="checkbox" ng-model="value"
ng-true-value="YES" ng-false-value="NO"> <br/>
in your controller:
$scope.value is gives Boolean value//
This is because scope inheritance. A nice article about angularjs scope and inheritance
I guess the $watch logic you mentioned might have been triggered from inside a ng-repeat or a ng-if. Remember angularjs creates a new scope variable for each object inside ng-repeat and ng-if.
<input type="checkbox" ng-model="someFlag"/> //This is a primitive $scope.someFlag
<input type="checkbox" ng-model="obj.someFlag"/> //This is an object $scope.obj.someFlag
<div ng-repeat="opt in options">
Here a new child scope - $scope.somechild is created for the opt variable.
So if primitive variable someFlag is referenced here it will be taken as $scope.child.someFlag. so any update here will not update the parent $scope.someFlag.
But if an object obj.someFlag is referenced here by object inheritance the compiler will try to find obj.someFlag in child - $scope.child as it is not present it will search in parent $scope as it is present this will now refer to $scope.obj.someFlag and any modification done here is done in the actual parent scope.
This is part of the mantra: "there should always be a dot in your model" by Misko Hevery, one of the fathers of Angular. You can, and probably should, watch this video: https://www.youtube.com/watch?v=ZhfUv0spHCY&feature=youtu.be&t=32m51s
ngModel is a directive that does two-way-data-binding. Binding to a primitive (Boolean in this case) will make the setter to set it on the current scope, rather than on the scope that's defined which could interfere with other scopes.
In your scenario, in a parent scope we have your $scope.someFlag = true. In a child scope we then have your input:
<input type="checkbox" ng-model="someFlag"/>
That will work initially but once the user changes the value, the someFlag will be created on the child scope and the bindings will read and write from that value from now on.
I hope this is, somehow, clear.
Note that this only happens with two-way-data-binding directives and not regular ones such as ngDisabled or ngHide

Setting and getting bootstrap radio button inside angular repeat loop

I am trying to set the default button and get the current selected value.
The example without the repeat loop works.
Here is my plnkr:
http://plnkr.co/edit/t9CefA5bhLZs3RASmEUG?p=preview
It is based on this example:
http://plnkr.co/edit/LFj4inY9TLYZs9z7yHCr?p=preview
Thank you!
So, there were 2 things going on in your plunker.
Firstly the btn-radio attribute doesn't need interpolation ({{}}) you can (and should) provide an expression directly. So just write btn-radio="company.id" instead of btn-radio="{{company.id}}".
Secondly, you must know that the ng-repeat directive creates a new scope. This is a very, very common conceptual issue people have with AngularJS so I would encourage you to read https://github.com/angular/angular.js/wiki/The-Nuances-of-Scope-Prototypal-Inheritance
Coming back to you particular problem you can either change you ng-model expression to point to a parent scope (ng-model="$parent.radioModel") or bind to an object property (ng-model="radioModel.id").
Here is a working plunk with the second approach:
http://plnkr.co/edit/uGAxaRlPFK6sD4tRjGXX?p=preview

Categories