Angular -> ng-repeat filtered array variable - javascript

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.

Related

ng-change using previous value

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).

Using ng-repeat with directive causes flaw in watch

I have made a new directive that is basically a new carousel that extends the angular UI carousel bootstrap. This new carousel will display multiple divs in one frame. My new directive accepts any data in an array and a custom html template for each data.
However, if I use my carousel with a directive, there is a weird behaviour that I am seeing with watch inside the directive. Everything works fine, but the watch inside my directive is always getting the same value for newVal and oldVal. What I mean is this, here's my carousel code:
<slide ng-repeat="frame in ctrl.frames">
<div ng-repeat="divs in frame.divs">
<custom-directive data="divs"></custom-directive>
</div>
</slide>
and inside my customDirective controller, I watch the change of data like this:
$scope.$watch("ctrl.data", function(newVal, oldVal){
if (newVal !== oldVal) {
// data is updated, redraw the directive in my case
// however newVal is always the same as oldVal
}
})
newVal and oldVal is always the same.. I expected the initial state to be oldVal = undefined and newVal will be my new data. However, this is never the case. Data is passed as a two-way binding to carousel and to custom directive (using '=' operator inside the scope of each directive).
Why is this happening? I have investigated this for long and here's my findings:
If I don't use ng-repeat inside my carousel, this will work. oldVal will be undefined and newVal will be my data during the initial state. But why is ng-repeat causing this? I have read lots of article regarding golden rule of prototypical inheritance, that says ng-repeat will create new childScope that hides/shadows the parent, but that only happens to primitive object and I am passing an array to my data.
I need to use ng-repeat in my carousel directive.. so I need to know why ng-repeat is causing this.. any suggestions?
UPDATE:
Reproduced the problem in Plunkr here. As you can see, oldValue is always the same as newValue (I expected the oldValue to be undefined in the beginning)
When you register the $watch in your link function, Angular has already processed the bindings during the preLink phase, hence you will never see undefined the first time your watcher is executed (that initialiation call is the only moment on which oldVal and newVal are potentially the same. If the watcher was registered before the bindings resolution, the oldValue would be undefined)
If you really want to see it, you could override the compile phase and add a custom preLink method (the default link being the postLink).
But I really doubt that you want to do that. Why is it a problem to not have undefined the first time? You should try to explain the real problem you are facing.
Also, note that if divs that you pass to your directive is an array, you should use scope.$watchColleciton instead of scope.$watch in order to detect changes in the array elements instead of change of the whole array pointer.
I think the problem you are having is just a misunderstanding of how $watch works.
$watch is expected to initialize with equal values. See the documentation here. Specifically:
After a watcher is registered with the scope, the listener fn is
called asynchronously (via $evalAsync) to initialize the watcher. In
rare cases, this is undesirable because the listener is called when
the result of watchExpression didn't change. To detect this scenario
within the listener fn, you can compare the newVal and oldVal. If
these two values are identical (===) then the listener was called due
to initialization
So in other words, your check for if they are equal is so you don't detect the initial call
In your provided Plunker, if you need to do some initialization code, you can do two things:
You can check if they are equal in the $watch function, and if they are then that is the initial call with their initial values
Or, outside of that function in the link function, the values are their initial values there (since the link function is equivalent to post-link, which means scope values have already been linked) so you can put your code there
Forked your Plunker here. Notice I moved the alert outside of the $watch, and the value is still valid
EDIT:
The reason you see a difference when it is not in the ng-repeat and it is set up like your commented out code in the Plunkr is due to you adding data in a $timeout. When the page initially loads, below are what the two types render as:
<a1 prop="data[0]"></a1>
HTML looks as it was written. data=[]. directive element exists, calling link with data[0]=undefined. $watch called with prop=undefined
<!-- ngRepeat: element in data track by $index -->
HTML is simply a comment. waiting for data to be populated. No directive element exists, which means link is not called
When you add items to data after the timeout, they look like this:
<a1 prop="data[0]"></a1>
Same as above. data[0] is now defined so prop is defined
<div ng-repeat="element in data track by $index" class="ng-scope">
<a1 prop="element" class="ng-isolate-scope"></a1>
</div> (x3)
Page now has directive elements. calling link function on each with data now filled. $watch called with prop values linked

Subscribing to changes in siblings with Knockout.js

I'm using inheritance to implmenet a reuseable forms app.
Expression parameter can be dependent on one or more other numeric/constant ot other expression parameters (the relation is many to many so heirarchy wont work here). Once all my dependent parameters evaluated them self (calcualted if expression or validated if user input) I can now calculate my self.
I'm looking for a way to subscribe to my siblings but the problem is that during creation the sibling does not neccasarily exists yet.
I set up a small example on this Fiddle.
To see this not working in action lets change the values from the console
d.parameters()[0].value(10) // expecting parameter C to sum up to 20.
d.parameters()[2].value() // Nothing (this should also update ont he screen).
d.parameters()[1].value(20) // expecting parameter C to sum up to 30.
d.parameters()[2].value() // Still nothing.
So Ive tried sevral things but i think i'm barking up the wrong tree here.
Fetching the parent using ko.dataFor(document.body) or
fetching the object from the DOM using the same function.
I've played around with deferEvaluation as you can see in the code
to force KO to first create the parameters and then apply the
binding. seems to do nothing.
Tried creating a observableArray with the links to the params and a
computed based on the array but the problom remain.
Appreciate any help here.
Bonus question, without losing focus from the main one, somehing I cant explain going on on line 73.

Strange knockout js select option binding issue

Does anybody have a minute to take a look at a puzzling problem I'm having with Knockout JS and binding select lists?
The person's favourite color should be selected in the list by calling value: favColorId in the select list, rather than with the function-based call of value: favColorId(), something very strange is going on here, I've never used () in the past, it's also causing some other weird issues where it won't recall the value into the span (so changing the selected item does nothing).. I have tried recreating a simple sample as best I can demonstrating the issue.
http://jsfiddle.net/goneale/ph8Jw/
I have included my mapDictToArray() function but it simply converts a
javascript object into a key-value JS array. I wouldn't think that is
contributing to the problem.
Actually, that was part of the problem. The function returns a JavaScript array, not an observable array and therefor can't be used properly by Knockout. I've made the following changes to your code:
// The "mapDictToArray" makes a normal JS array, not a ko.observableArray();
// You can't simply "merge" a JS array with an observableArray(); you'll need
// some extra functionality for that: ko.utils.arrayPushAll()
// viewModel.colors(mapDictToArray(dict));
ko.utils.arrayPushAll(viewModel.colors(), mapDictToArray(dict));
// Apply the bindings *after* you've added the contents to the "colors" observable, in order to
// get the correct selected value
ko.applyBindings(viewModel);
That should do the trick (with the correct HTML without the () )!
JSFiddle.
UPDATE
I thought about my solution, but something wasn't correct. The only thing that was correct, was the part that you need to apply the bindings after you've added the contents of the colors observable. This is your fiddle, with that part moved down.
This works in your case, but you'll need to use the arrayPushAll method when there is already data inside the observableArray. This method merges, while you overwrite it when not using it (example with data inside the observable).

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