why do two ng-repeats share same scope? - angularjs

I have two divs with ng-repeat of the same list. But when I change the content of one list item from first div, the second div receives the same value. I thought ng-repeats had isolated scopes? How can I archieve isolation of the ng-repeats?
edit:
"In most cases, directives and scopes interact but do not create new instances of scope. However, some directives, such as ng-controller and ng-repeat, create new child scopes and attach the child scope to the corresponding DOM element."
http://docs.angularjs.org/guide/scope
<div ng-controller="MyCtrl">
<button ng-click="newitem()">add item </button>
<div ng-repeat="item in list">
<input ng-model="item.name" placeholder="Name*">
<input ng-model="item.lastname" placeholder="Last name*">
<input ng-model="item.username" placeholder="Username(Email)*">
</div>
here is another div with ng-repeat of the same list. why do they share scope?
<br>
<div ng-repeat="item in list">
<input ng-model="item.name" placeholder="Name*">
<input ng-model="item.lastname" placeholder="Last name*">
<input ng-model="item.username" placeholder="Username(Email)*">
</div>
js:
var myApp = angular.module('myApp', []);
function MyCtrl($scope) {
$scope.list = [];
$scope.newitem = function () {
$scope.list.push({name: "", lastname: "", username: ""});
}
}
http://jsfiddle.net/LZKq2/

I'll try to explain this in a different way than the other answers, though all answers are basically saying the same thing.
When you attach the controller to the div element, it creates an instance of the controller with the $scope attached to it, to which you add an array. The moment that you attach the array to the first child div and use ng-repeat, you get a child scope that belongs to the array. the second child div is not creating a new array but re-using an existing array which now already has a scope. Thus, items added into the array via the first div are reflected in the second div. It could not possibly function in any other way, as you only have one variable that you are tracking across the $scope. If, however, you had 2 separate lists, say $scope.list and $scope.list2, these 2 arrays would each have their own scope. see http://jsfiddle.net/zTE5j/2/
<div ng-controller="MyCtrl">
<button ng-click="newitem()">add item </button>
<div ng-repeat="item in list">
<input ng-model="item.name" placeholder="Name*">
<input ng-model="item.lastname" placeholder="Last name*">
<input ng-model="item.username" placeholder="Username(Email)*">
</div>
here is another div with ng-repeat of the same list. why do they share scope?
<br>
<div ng-repeat="item in list2">
<input ng-model="item.name" placeholder="Name*">
<input ng-model="item.lastname" placeholder="Last name*">
<input ng-model="item.username" placeholder="Username(Email)*">
</div>
</div>
controller:
var myApp = angular.module('myApp', []);
function MyCtrl($scope) {
$scope.list = [];
$scope.list2 = [];
$scope.newitem = function () {
$scope.list.push({name: "", lastname: "", username: ""});
$scope.list2.push({name: "", lastname: "", username: ""});
}
}

The new scopes are child scopes so although the scope is new, the properties on the scope are inherited from the parent. For example, if the parent has a list of items, then child scopes inherit the same list of items. Think of it as "pointers to the parent list." When you modify item "x" in the first list, item "x" is in a list on the scope. That list is inherited from the parent list, so modifying "item x" is the same as modifying that item in the parent list. Because the second list inherits from the parent, it also has the same reference to "item x" and therefore it will reflect the update.
This is demonstrated in this fiddle: http://jsfiddle.net/jeremylikness/xdzxb/
Note both lists are affected when you edit an item, but the third list isn't because it was created as a copy:
$scope.listNew = $scope.list.slice(0);
You'll need to explicitly create copies if you want the repeats to act on different versions of the list.

ng-repeat do creates new scope according to angularjs.
but do you have toString in a plain object ?
var a={};
if (a.toString) alert('1');// ( where the hell this toString came from ?)
this will alert 1 !!!
this is because of prototype inheritance.
your inline repeat scope has its own scope which SEEK FOR ITS PARENT SCOPE for data ( if not found).
why do they share scope?
Becuase they are both seeing the same viewmodel object , $scope which is created by the controller.

Related

How we set to ng-model variable to parent controller in nested controllers scope

I have one ng-controller nested in another controllers scope.
I want to set scope variable in nested controller scope, to parent controller.
I have view:
<div ng-controller="MyCtrl">
<input type="text" ng-model="name"/>
<div ng-controller="CountryDataController">
<angucomplete
(...)
selectedObject="country"/>
</div>
</div>
which is part of the form.
Then on form submit i want to send ng-models from MyCtrl ( name,country) doing:
fields: {name: $scope.name,
country: $scope.country,
},
How can i tell angular, that selectedObject model belongs to MyCtrl, and not CountryDataController.
I tried
selectedObject="MyCtrl.country"
selectedObject="country[MyCtrl]"
but without effects.
selectedObject in angucomplete works like ng-model.
Also I don't want to rewrite logic from CountryDataController to MyCtrl, because in first i have fields for autocomplete and in second file uploading.
Is there any convention for this?
The answer is:
selectedobject="$parent.country"
You can use $parent, but if you move your HTML or eventually add another controller it between it will break.
The correct way to do that is to use the controller as syntax, as shown below:
<!-- use topCtrl to access this controller scope -->
<div ng-controller="MyCtrl as topCtrl">
<input type="text" ng-model="name"/>
<!-- use countryCtrl to access this controller scope -->
<div ng-controller="CountryDataController as countryCtrl">
<angucomplete
(...)
selectedObject="topCtrl.country"/>
</div>
</div>

AngularJS - ngmodel in ngrepeat not updating ('dotted' ngmodel)

i'm trying to draw radioBoxes with angular array, and after that get value of checked radio, but model don't change its value you, can anyone help me with this ?
HTML part
<div ng-app>
<div ng-controller="CustomCtrl">
<label ng-repeat="user in users">
<input type="radio" name="radio" ng-model="radio" value="{{user.name}}" /> {{user.name}}
</label>
<br/>
{{radio}}
<br/>
Save
</div>
</div>
Angular Part
function CustomCtrl($scope) {
$scope.radio = "John";
$scope.users = [
{"name" : "John", "Year" : 18},
{"name" : "Tony", "Year" : 19}
];
$scope.saveTemplate = function() {
console.log($scope.radio);
};
}
you can see example here - http://jsfiddle.net/hgf37bo0/2/
you need to set $scope.radio to be an object like this:
$scope.radio = {
name: 'John'
}
and then access it from html like so:
<input type="radio" name="radio" ng-model="radio.name" value="{{user.name}}" />
here's a working jsfiddle
You can read up on why this is necessary in this answer
from angularjs docs:
Scope inheritance is normally straightforward, and you often don't even need to know it is happening... until you try 2-way data binding (i.e., form elements, ng-model) to a primitive (e.g., number, string, boolean) defined on the parent scope from inside the child scope. It doesn't work the way most people expect it should work. What happens is that the child scope gets its own property that hides/shadows the parent property of the same name. This is not something AngularJS is doing – this is how JavaScript prototypal inheritance works.
...
Having a '.' in your models will ensure that prototypal inheritance is in play. So, use
<input type="text" ng-model="someObj.prop1">
rather than
<input type="text" ng-model="prop1">
If you really want/need to use a primitive, there are two workarounds:
Use $parent.parentScopeProperty in the child scope. This will prevent
the child scope from creating its own property. Define a function on
the parent scope, and call it from the child, passing the primitive
value up to the parent (not always possible)

ng-focus doesn't work with ng-include

I have a problem with ng-focus on ng-include elements. I use this part of code
<div ng-include="'template.tpl.html'" ng-controller="Ctrl"
ng-class="{'highlight':highlight==true,'nofocus':highlight==false}"></div>
and ng focus works fine when I have inside template.tpl.html regular DOM elements like:
<div class="form-group">
<label class="control-label">My label</label>
<div class="input-wrap">
<input type="text" name="Name" class="form-control"
ng-model="client.firstName" ng-disabled="displayMode != 'edit'"
ng-focus="highlight=true" ng-blur="highlight=false">
</div>
</div>
but, if I include another file in template.tpl.html it doesn't work anymore. For example if I apply in template.tpl.html the structure like below it won't work:
<div ng-include="'contacts/contacts-list.tpl.html'"></div>
Why ng-focus doesn't work on ng-include included in ng-include? Any tips on how to resolve this?
(Now that you have clarified your actual issue) As a continuation of the comment, ngInclude created a child scope of the provided scope, in your case child scope of child scope of Ctrl and well that does not matter since overall your wrapper is the Ctrl controller. But here you need to be careful while setting the scope property and expect the changes done from grandchild scope to be reflected in its grandparent scope. You have not even defined the properties in your controller, also even if you define them directly as scope.highlight = false the changes made to the property in the child or its child will not be reflected in the parent (though it will propagate down as a part of inheritance). You should remember scopes (except isolate scopes) are prototypically inherited, so in order to resolve this, you can use a property that holds the object reference which contains the property that needs to be modified, so that even if you change the value of property on that object from grandchild it will still reflect while accessing the same on the parent because they are all looking at the property on the same object reference
So in your controller initialize and object:-
.controller('MainCtrl', function($scope) {
$scope.settings = {};
});
Set the bindings accordingly in the main html:-
<div ng-include="'tmpl.html'" ng-controller="MainCtrl"
ng-class="{'highlight':settings.highlight,'nofocus':settings.highlight}"></div>
And in its grand child as well:-
<input type="text" name="Name" class="form-control"
ng-model="client.firstName"
ng-focus="settings.highlight=true" ng-blur="settings.highlight=false">
Plnkr
You wouldn't get a better explanation on angular scopes than this answer.

How to dynamically add input rows to view and maintain value in angularjs ng-model as array item

I have a situation where I want the user to dynamically add <input> rows when they want to enter a new item. As shown below, the user clicks the "Add Input Item" button and a new input element is added where the user may add text.
I'm trying to associate each input element value to the model $scope.items (an array) in app.js. In my index.html, I load the $scope.items using an ng-repeat.
What I do not understand is how I can get each input/row to automatically be added to & managed by the model defined as $scope.items.
Is it possible to map each input element to an ng-model that relates to the items array and, if so, how? Or, should directives be used in this situation?
The full code and runtime is on Plunker here (so that you can view the weird output).
Snippet from index.html:
<body ng-controller="MainCtrl">
<button ng-click="addInputItem()">+ Add Input Item</button>
<div ng-repeat="item in items">
<input ng-model="items" type="text" value="{{item}}" size="40">
</div>
<pre>items = {{items | json}}</pre>
</body>
The app.js code:
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.items = ['First Item', 'Second Item'];
$scope.addInputItem = function() {
$scope.items.push('');
};
});
The first problem is with the binding on the input boxes. Due to the ng-repeat surrounding the input box, each input box has an item value available to bind to, and that is what your ng-model should be. You shouldn't be assigning anything to the value attribute.
<input ng-model="item.value" type="text" size="40">
Making that change will at least get the input boxes to display the items. But typing in the boxes won't update the underlying array. That's the second problem.
The ng-repeat directive prototypically inherits the scope from your controller (read more about scope here). Since your items are primitive strings, the elements in your ng-repeat effectively get copies of the data from the controller, and typing in the boxes updates those copies, not the actual data in the controller.
To fix this, just make your items an array of objects instead of an array of strings.
$scope.items = [
{value:'First Item'},
{value: 'Second Item'}
];
Then you would bind your textboxes like this:
<input ng-model="item.value" type="text" size="40">
Here's a Plunker showing how it works.
This certainly is possible, check out this updated plnkr
I updated your array to be an array of objects (not strings) with a property of text and changed your inputs to this:
<input ng-model="item.text" type="text" size="40">

AngularJs multiple instances and nested controller

I got confused a bit about whether can we create multiple instance of controller and that to in nested form for eg -
<div ng-controller="abc">
<form ng-submit="call()">
<input type=text ng-model="content"/>
</form>
<div ng-controller = "abc">
<form ng-submit="call()">
<input type=text ng-model="content"/>
</form>
</div>
</div>
i just want to know that if i use the same model with other instance of controller, so model value would be same or different. Similar to static variable ?
i just want to know that if i use the same model with other instance
of controller, so model value would be same or different. Similar to
static variable ?
All declarations of ng-controller create a new instance of the controller. So, if you had two instances side by side, like this:
<div ng-controller="abc">
<input type=text ng-model="content"/>
</div>
<div ng-controller="abc">
<input type=text ng-model="content"/>
</div>
plunker
then, all of the $scope properties of each would be completely independent.
When a ng-controller is nested, then its scope inherits the parent controller's scope. So for this you'd expect that content refers to the same scope property:
<div ng-controller="abc">
<input type=text ng-model="content"/>
<div ng-controller="abc">
<input type=text ng-model="content"/>
</div>
</div>
plunker
However, since content is not defined directly in the controller something strange happens. If you fill in the parent input first. Both, inputs become bound to the same scope property. However, if you fill in the child input first, they are independent!
This can be confusing until you understand that Angular is being lazy when it creates the property on the scope. content is null at first on both scopes. It is only when it has a value that it will inherit.
So, what do you do if you want to keep things separate? Add an initial value to a $scope property inside the controller:
app.controller('abc', function($scope) {
$scope.content = '';
});
plunker
This way, each separate controller instance is initialized with its own content property.
Hope this helps.

Resources