Why ng-value and value attribute of input element hold different values - angularjs

The input field value doesn't get the assigned ng-value.The dragItms object holds the value to be bound for the given col key.
When isSaveTemplt = true, collection and columns gets updated after api call.
The functionality that I implement is such that dragItems object changes on clicking tabs and the said api is called for getting the data for tbody element.
<th ng-if="isSaveTemplt && collection.length>0 && columns.length>0" ng-
repeat="col in columns track by $index">
<input class="filterStyle" type="search" placeholder="search
by {{col | translate}}" st-search="{{col}}" ng-value={{dragItms[col]}} />
</th>

I didn't get your question clearly, Maybe more code and explanation is needed. Unfortunately I dont have enough reputation to comment. But what I feel is the issue of using ng-if. Whenever ng-if is used it creates a child scope. So any element inside has a different scope. To access parent scope use $parent. So you access will be something like this {{$parent.dragItms[col]}}. $parent is parent scope.

Related

Angular Js orderBy with input values

I'm having issues with the orderBy filter. The following code will order my initiative column just fine. When I type a value into the input, the filter automatically begins ordering the values just as I want it to.
However, if I type a value into the input that raises that character higher in the list the input will close out before I can finish typing that value.
If the value I type into the input drops the character lower down the list then that input does not close and allows me to finish typing my value.
Can anyone explain this behavior?
<tr ng-repeat="char in localChars | orderBy: '-initiative'">
<td>{{char.name}}</td>
<td ng-hide='show' ng-click='show = true'>{{char.initiative}}</td>
<td ng-show='show'>
<input ng-blur='initiative(char)' ng-model='char.initiative' type="text">
</td>
</tr>
Here is what I believe is the sequence of events:
User types in input changing the value of char.initiative.
This causes the list to reorder via the ng-repeat.
The scope of the ng-repeat is re-initialized causing the local show variable to lose its value (remember, ng-repeat has its own scope).
To fix this you'll need to use ng-model-options to control when the model value actually gets updated. You can choose to update on blur or use a debounce. Here is how to update on blur (my recommendation). You can find info on debouncing at the link.
<input ng-blur="initiative(char)"
ng-model="char.initiative"
ng-model-options="{ updateOn: 'blur' }"
type="text" />

When ng-init in ng-repeat is replays?

I have a simple ng-repeat to build a HTML list from a javascript array.
Each item can be moved using an input to get the new rank. This input is binded to a variable rank. This variable is initialized using the ng-init directive.
Code looks like this :
<li ng-repeat="item in ctrl.getItems()">
<div ng-init="rank = $index">
[$index: {{$index}}]
{{item}}<br/>
<label>
Move to
<input type="number" ng-model="rank"/>
</label>
<button type="button" ng-click="ctrl.moveItem($index, rank)">
Ok
</button>
</div>
</li>
At runtime, when I change the input value and click to the Ok button, function ctrl.moveItem is called and item is really moved in the ctrl.getItems() array.
So the ng-repeat is replayded and items appears in the new order.
BUT variable rank is not reinitialized and 2 items appears with the same rank.
The sample is here : https://jsfiddle.net/nlips/4ng34b7b/
My question is not so much about moving items in a list, but I need to understand how ng-init works in the context of ng-repeat.
I did not find anything on this subject in the AngularJS official documentation.
From AngularJS docs:
The ngInit directive allows you to evaluate an expression in the current scope.
Now. You are working with different scope.
You are using ngInit into the transcluded scope, overriding $scope.rank each time it repeats that portion of template.
If you want to persist your rank you should init it into the ngRepeat scope.
Try with:
<li ng-repeat="item in ctrl.getItems()" ng-init="rank = $index">
[$index: {{$index}}]
{{item}}<br/>
<label>
Move to
<input type="number" ng-model="rank"/>
</label>
<button type="button" ng-click="ctrl.moveItem($index, rank)">
Ok
</button>
</li>
EDITED ANSWER
Ok, i got it.
The ngInit expression is evaluated only when that part of template is going to be rendered into the DOM.
So, when the page is loaded for the first time your expression is fired and each rank is evaluated correctly.
But, when you make changes on an item that is already rendered, your template is not going to be rendered again, so your ng-init will not be fired.
If you want that ng-init to be executed again you have to remove the item from the DOM and then append it back, into the new position.
There are several alternatives to this approach, but i hope this clarifies what was going on.

Angularjs ng-model nested inside ng-repeat updates all

This should be simple. For some reason, when using ng-model inside of ng-repeat it updates all ng-models inside that repeat loop.
Here's the code. Any ideas?
http://plnkr.co/edit/iAgrPwwBMilCyeReeLt9?p=info
Thanks.
Interesting!!!
Problem is that you are resetting row.field with an object from "$scope.columns"
in
<td>
<select ng-model="row.field" ng-options="column.title for column in columns"></select>
</td>
Here, row.field is reset with an object from $scope.columns and if you change this object in one row, as the other rows also use the same object, they repeat the same value.
You can change this model to row.field.type as
<td>
<select ng-model="row.field.type" ng-options="column.type as column.title for column in columns" ng-change="resetRow(row.field)"></select>
</td>
and define $scope.resetRow in your controller to reset other properties based on the field type.
Here is the updated plunker.
I would love to have a feature in ng-options to return a copy of
the object selected instead of the object reference to solve your problem though.

Angular databinding as a function argument not working

<input type="text" value="{{codes[0].code}}" ng-click="newNumber(0)" />
<input type="text" value="{{codes[1].code}}" ng-click="newNumber({{codes[1].id}})" />
The first ng-click event fires in my controller just fine but the second one does nothing.
I tried concat'ing as well ... is there some other way I should do this?
ng-click
The value of ng-click is already evaluated as an angular expression. As such, you don't need the {{ }}. Read http://docs.angularjs.org/guide/expression for more information. Take a look at the second example, it will help clarify this.
ng-model
Also, ng-model should be used for data-binding. For example, take a look at this jsfiddle: http://jsfiddle.net/bCpW9/8/ and the notes below.
<li ng-repeat="code in codes">
This loops through the codes collection which was defined in the controller. It creates a <li> for each element in the codes collection.
<input ng-model="codes[$index].code" />
Inside each <li>, an <input> for the current code is created. Each input is bound to it's corresponding element in the codes array by setting ng-model to it. For instance, type a new code into the first input field. It automatically updates the corresponding code model with what you typed, as you can see to the right.
I hope that helps.

How to get the NgModelController for input fields inside a ngRepeat directive?

Normally, with a form and input fields, the form controller is published into the related scope under the form name attribute. And, the NgModelController is published under the input name attribute.
So for an input field with an ngModel directive, the NgModelController for the input field can be retrieved like $scope.myFormName.myInputFieldName
The question is how to do the same thing (get the NgModelController) for input fields inside the ngRepeat directive?
I would like to name the input fields using $index as part of the name so each template instance is uniquely named. This renders OK, so
<input name="foo_{{$index}}" ...
renders the instance with $index == 3 to
<input name="foo_3" ...
But trying to get the ngModelController via the published names does not work (it's undefined), e.g.:
$scope.myFormName.foo_3
A plunker showing this is here: http://plnkr.co/edit/jYDhZfgC3Ud0fXUuP7To?p=preview
It shows successfully getting the ngModelController for a 'plain' input element and calling $setValidity, and also shows failing to get the ngModelController for an input element inside an ngRepeat directive.
Copied the relevant section of code from the plunker below:
<div ng-repeat="element in elements">
<div ng-class="{error: form['foo_{{$index}}'].$invalid}">
<input name="foo_{{$index}}" ng-model="element.a" type="number">
<span ng-show="form['foo_{{$index}}'].$error.bar">ngRepeat bar invalid</span>
</div>
</div>
{{form.foo_0.$setValidity('bar', false)}}
#Flek is correct that the new child scopes that ng-repeat creates are the root of the problem here. Since browsers do not allow nesting of <form> elements, ngForm must be used when nesting forms, or when you want to do form validation inside ngRepeat.
See Pawel's answer on the google group thread, which shows how to use ng-form to create inner forms, and/or #blesh's SO answer.
If i understand your question correctly, you are trying to have access to the form elements created inside the ng-repeat.
Please have a look at this fiddle http://jsfiddle.net/EF5Jp/. Inside the button click handler you will have the access to the element with id myForm.foo_2. You can notice that the element is retrieved by myForm.foo_2 and not $scope.myForm.foo_2. Second thing is, changing the value using its scope and not using its value property like angular.element(element).scope().foo = 6;.

Resources