In my AngularJS app, I am looping through an array object and send the value to a radio input as value. The overall idea is that the user selects a radio box and the value with be back part of $routeParams. Unfortunately, the variable {{ modelSelected }} does not seemed to be available outside of the ng-repeat. Why? The first never shows the variable {{ modelSelected }}.
Does AngularJS creates a ScopeChild within the ng-repeat?
Link to my jsfiddle example
<html ng-app>
<body ng-init="models = [{name:'Sam'},{name:'Harry'},{name:'Sally'}]">
<h3> Selected {{ modelSelected }} shown outside paragraph</h3>
<div ng-repeat="model in models">
<p>{{ model.name }} -
<input ng-model="modelSelected" type="radio" name="patient" value="{{ model.name }}" required>
</p>
<h3> Selected {{ modelSelected }} shown inside paragraph</h3>
</div>
</body>
</html>
When dealing with primitives or using bindings generally try to prefer using a . in the model property that is being bound. So that when a child scope is created prototypical inheritance will carry down the properties (which are reference types) down to the children. (using $parent almost always is considered as a hack). In your case the property modelSelected is a primitive (even if it is present on the parent scope) and ng-repeat creates a child scope which does not finds this property (since inheritance does not carry it down even if it were present) and creates a new property on its scope, so the changes made to that are not seen to the outer scope.
Initialize a property on the scope as an object, selection={} in your scope (in your example it would be the outerscope). And bind the model as ng-model="selection.modelSelected
Ex:-
<body ng-init="models = [{name:'Sam'},{name:'Harry'},{name:'Sally'}]; selection={}">
<h3> Selected {{ selection.modelSelected }} shown outside paragraph</h3>
<div ng-repeat="model in models">
<p>{{ model.name }} -
<input ng-model="selection.modelSelected" type="radio" name="patient" value="{{ model.name }}" required>
</p>
<h3> Selected {{ selection.modelSelected }} shown inside paragraph</h3>
</div>
</body>
Demo
Read: Understanding Scopes
Yes. It does create a new scope that inherits the parent scope. Use $parent.modelSelected to access the parent scope's variable.
<body ng-init="models = [{name:'Sam'},{name:'Harry'},{name:'Sally'}]">
<h3> Selected {{ modelSelected }} shown outside paragraph</h3>
<div ng-repeat="model in models">
<p>{{ model.name }} -
<input ng-model="$parent.modelSelected" type="radio" name="patient" value="{{ model.name }}" required>
</p>
<h3> Selected {{ modelSelected }} shown inside paragraph</h3>
</div>
</body>
The {{modelSelected}} would not be available outside the ng-repeat because of how scope works in AngularJS (see https://docs.angularjs.org/guide/scope)
Scopes are arranged in hierarchical structure which mimic the DOM structure of the application
So since the <h3> element you are trying to access the {{modelSelected}} object from is outside the scope of the iterator, the object is not available.
Related
$index value not increment inside ng-if block.
<div ng-repeat="household in census.households track by $index">
<div ng-if="household.ask_for_tobacco">
<input id="tobacco_{{$index}}" type="checkbox" ng-model="member.smoker">
<label for="tobacco_{{$index}}"></label>
</div>
</div>
In above block, I am using ng-repeat and ng-if inside it. I am following condition ng-if and setting element id with $index. Element ID is being set 0 every time, rather than incremented to 1,2,3...
If ng-if removed, it works without fail. So anyone has used and faced same issue. Please give me solution.
Your problem is that ng-if creates its own scope which then interferes with your access to the scope of the iteration that is created by ng-repeat.
Because you can't use ng-show, my suggestion is that you apply a filter to your ng-repeat like this:
<div ng-app="app" ng-controller="MyCtrl">
<div ng-repeat="household in households | filter: { ask_for_tobacco: true }">
<input id="tobacco_{{$index}}" type="checkbox">
<label for="tobacco_{{$index}}">index {{ $index }} id = {{ household.id }}</label>
</div>
</div>
See working example here: http://jsfiddle.net/m25afg12/9/
you can use $parent.$index
late though for ans :D
I am building an Angular form that needs repeatable form elements inside an ngRepeat.
<form name="form">
<div ng-repeat="x in [1,2,3,4]">
<input name="something_{{$index}}" ng-model="hi" required>
<div ng-messages="form.something_{{$index}}.$error">
<ng-message="required">This is required</ng-message>
</div>
</div>
<pre>{{form | json: 4}}</pre>
</form>
Angular now supports dynamically declared input names so that you don't have to do something like:
<div ng-repeat="x in [1,2,3,4] ng-form="repeated-form"></div>
And you can use {{$index}} inside the ngRepeat to declare items dynamically. But this doesn't seem to work with ngMessages, which throws an error when I try to bind the index into it.
i.e. this:
<div ng-messages="form.something_{{$index}}.$error">
throws this:
Error: [$parse:syntax] Syntax Error: Token '{' is an unexpected token at column 16 of the expression [form.something_{{$index}}.$error] starting at [{{$index}}.$error].
How can we dynamically declare which property on the form to watch, if ng-messages can't watch the form value that is declared with its {{$index}}?
PLNKR: http://plnkr.co/edit/4oOasbtffTgKqmxcppUG?p=preview (check console)
ng-messages="form['something_' + $index].$error"
Should work. I generally wouldn't put {{ }} in any of the ng directives because most of the ng directives execute with priority level 0 (including the {{ }} directive, ngBind). Also, the ng directives all use $evaluate on their argument, so they look at variable values in the scope by default.
Priority 0 for multiple directives on the same element means that Angular can't guarantee which directive will be applied first. Because of that, it is generally best to avoid using ngDirectives together as behavior can vary. ngIf is an exception as it executes with priority 600 (which is why directives aren't evaluated for an ng-if element not currently in the DOM no matter what).
<div ng-repeat="x in [0,1,2,3]">
<input name="something_{{$index}}" ng-model="hi" required>
<div ng-messages="form['something_' + $index].$error">
<ng-message="required">This is required</ng-message>
</div>
</div>
http://plnkr.co/edit/k5nzkpkJwSuf5dvlMMZi?p=preview
I have the following
<table id="socialMediaContainer" class="socialMediaContainer" style="width: 100%;">
<tbody>
<tr ng-repeat="row in detailCollection.ChannelsInfo"
ng-controller="WhiteLabelSitesCtrl">
<td><input id="txtSocialName" type="text" class="socialName"
placeholder="Name" ng-disabled="ViewMode" maxlength="250"
value="{{row.SocialChannelName}}" /> </td>
<td><input id="txtSocialURL" type="text" class="txtLabel socialURL"
placeholder="URL" ng-disabled="ViewMode" maxlength="250"
value="{{row.SocialChannelURL}}" />
</td>
<td class="DragnDropIcon"></td>
<td>
<a class="orange " ng-show="ViewMode">Upload</a></td>
</tr>
</tbody>
</table>
and I have another button outside the ng-repeat that updates the ViewMode variable, but this is not working inside the ng-repeat neither for the ng-show not the ng-disabled. what am i missing here?
The problem seems to be, that you need to move ngController directive to the table level (at least): it can't be on the same element with ngRepeat if the later iterated over the array defined in controller.
<table ng-controller="WhiteLabelSitesCtrl" ... >
<!-- ... -->
</table>
Demo: http://plnkr.co/edit/tu4TLmWIxdcYaiEd7whn?p=preview
ng-repeat creates a childscope for each item in the repeater.
Thus viewmode will be a primitive value on that child scope and therefore as a primitive will lose inheritance binding with the parent scope.
If you declare it as an object property in the controller scope however it will then be a reference to that parent object.
$scope.mode ={ViewMode: false}
html example
<a class="orange " ng-show="mode.ViewMode">Upload</a></td>
Try to pass object instead variable inside ng-repeat scope. Instead ViewMode,
declare in your controller:
$scope.model = {};
$scope.model.ViewMode = false;`.
And your binding must be like this: <a class="orange " ng-show="model.ViewMode">. After that all be work fine.
Read this article to understand why it happens: https://github.com/angular/angular.js/wiki/Understanding-Scopes
The button outside of the ng-repeat is not going to be nested in the same controller. The variable that it's modifying probably is not the same one that ViewMode under the WhiteLabelSitesCtrl is looking at.
When you point to this ng-controller, that controller will be activated with a new scope associated with it
<div ng-repeat ng-controller="WhiteLabelSitesCtrl">
<div ng-show="someValue"></div>
</div>
When you reference this controller again on another tag, it won't reference the existing controller as you might be expecting, it'll actually do the exact same thing... it will create the controller, and create a new scope for it- completely separate from the original one.
<div ng-controller="WhiteLabelSitesCtrl">
<button ng-click="someValue = !someValue"></button>
</div>
I have this code
http://plnkr.co/edit/aycnNVoD96UMbsC7rFmg?p=preview
<div data-ng-app="" data-ng-init="names=['One']">
<input type="text" ng-model="names[0]">
<p>Looping with ng-repeat:</p>
<ul>
<li data-ng-repeat="name in names">
<input type="text" ng-model="name"> {{ name }}
</li>
</ul>
</div>
When i change value of name[0] in the first input box it changes values of the second input box.
But when i change value of name[0] in the second input box it does not change value of the first input box. Why?
It works if you bind your second input to : names[$index]
<input type="text" ng-model="names[$index]"> {{ name }}
This is due to ng-repeat creating a child scope, so the reference to name inside the ng-repeat is different to that original one in the names array, see here:
New AngularJS developers often do not realize that ng-repeat,
ng-switch, ng-view and ng-include all create new child scopes, so the
problem often shows up when these directives are involved. (See this
example for a quick illustration of the problem.)
Regarding as to why this happens, when you bind the input to name in names inside the ng-repeat, you are creating a new property on the new child scope created by the ng-repeat called name, and thus the ng-model of the textbox created by the ng-repeat is referencing a different name to that of the actual 0th element of the names array. As others have pointed out, if you use names[$index] you are implicitly referencing the 0th element of the names array, thus NOT creating a new name property on the child scope created by the ng-repeat. An angular best practice is not to have ng-models bound to primitives, rather objects, Sandy has mentioned in his answer if you bind to an object you will overcome this, and the 2 other posters have answered this by using $index to refer to the 0th element of the names array. This is one of the nucances of scope inheritance in angular.
A couple more handy links:
Here and here.
Just wanted to give my bit on this. Somewhat related to your problem as I see.
<body>
<div data-ng-app="" data-ng-init="names=[{value:'One'}, {value:'Two'}]">
<p>Looping with ng-repeat:</p>
<ul>
<li data-ng-repeat="name in names">
<input type="text" ng-model="name.value"> {{ name }}
</li>
</ul>
</div>
</body>
Instead of binding the array item directly to the control, I would prefer to create an object of the array and then bind value of each item. This way we can avoid reference problems.
A working prototype jsfiddle
Hope it helps.
You need to provide $index in your ng-model.
<li data-ng-repeat="name in names">
<input type="text" ng-model="names[$index]"> {{ name }}
</li>
You are binding ng-model="names[0]". So it means that you are binding value on first index of names array.
So when we write ng-model="names[$index]" in ng-repeat it means that all values will be bound accordingly into array. $index is an iterator offset of the repeated element.
names[0] = 'One'
names[1] = 'Two'
and so on!
I am new to angularjs and trying following html template. But angularjs only replace the first {{ fragment.id }} expression in text area with the value. All the other {{ fragment.id }} expressions are left unmodified in generated html code.
<div class="input-box" ng-repeat="fragment in project.fragments">
<textarea id="code-{{ fragment.id }}" class="code-input" ng-model="content">{{ fragment.content }}</textarea>
<button class="btn btn-xs btn-primary" style="margin-top:5px;" ng-click="execute({{ fragment.id }})"><i class="glyphicon glyphicon-play"></i> Run</button>
<script type="text/javascript">
applyCodeMirror({{ fragment.id }});
</script>
<div id="result-{{ fragment.id }}" ng-model="result"></div>
</div>
I also have a another question is it possible to use angularjs expression to generate JavaScript code like I have done above.
A few things (the code above is concerning)
Use ng-model to bind $scope variables to inputs - your textarea becomes: <textarea id="code-{{ fragment.id }}" class="code-input" ng-model="fragment.content"></textarea>
{{}} is used to output Angular $scope variables to the view - you would not use {{}} in JavaScript code to get Angular variables, so the <script>{{fragment.id}}</script> is invalid. If you want to use the Angular $scope variables outside Angular (not recommended, but we all have our reasons) - use angular.element(elem).scope() to get an instance of the scope (where elem is an element within the ng-controller declaration)
Angular directives (like your ng-click) do not need {{}} to get $scope variables - your ng-click becomes: ng-click="execute(fragment.id)"