I am trying to inheritance some properties from main controller but is not working as I expected.When I change values for data in main controller it update values for subcontroller too . Once I focus on subcontroller then is start working as I excepted .I have a counter in main controller which I want to store number enter by user but when user enter count for main controller it changes count of subcontroller .Why its update child Class.
How inheritance works in angularjs
<body ng-app="myapp">
<div ng-controller="MainController">
<label>Count for main controller</label>
<input type="text" ng-model="count">
<div ng-controller="subController">
<label> count for sub controller</label>
<input type="text" ng-model="count">
</div>
{{count}}
</div>
</body>
<script>
var myapp=angular.module('myapp',[]);
myapp.controller('MainController',function($scope){
$scope.count=0;
//other properties
});
myapp.controller('subController',function($scope){
//other properties
});
</script>
AngularJS use prototypical inheritance in Controller ,that's why when change value in base class it will change in child class too. Child is access properties from base class directly.
When we focus on child controller it create its own copy property and disconnected from property in base class. there is no way to stop this.
How ever we can force base properties to updated as child class update that properties Buy assigning object to $scope rather then properties directly.
<body ng-app="myapp">
<div ng-controller="MainController">
<label>Count for main controller</label>
<input type="text" ng-model="val.count">
<div ng-controller="subController">
<label> count for sub controller</label>
<input type="text" ng-model="val.count">
</div>
{{val.count}}
</div>
</body>
<script>
var myapp=angular.module('myapp',[]);
myapp.controller('MainController',function($scope){
$scope.val={count:0}
//other properties
});
myapp.controller('subController',function($scope){
//other properties
});
Now on updating child property it will update parent property too and will remain connected.
Related
I am binding an ng-model to an input, but the value of the variable it's bound to is not being updated outside of the div where that directive is declared:
<div input-field
ng-if="startTypes.selected.value == 'LocalDate' || startTypes.selected.value == 'LocalDateTime'">
<input id="date" type="text" ng-model="date" input-date>
<label for="date">Date</label>
Date inner scope: {{date}}
</div>
Date outer scope: {{date}}
When selecting a new date, ony the inner date is updated. The outer one remains with the old value (which might be either undefined or not depending if I declared it in the controller, it doesn't matter).
I am using angular-materialize, I am not sure if this is the source of the issue but it doesn't make sense because it is a specific framework for angular to work with the CSS framework materializecss.
This is the component I am using.
Edit:
I have tried declaring date in the controller as $scope.date = new Date() and indeed the current date is loaded in the date picker. However when a date is selected and the model changes, it's only updated locally (inner scope), while in the outer scope the old value remains.
As ng-if creates a child scope which is Prototypically inherited from its current scope while inserting inner template to DOM, hence in this case ng-model getting created inside ng-if's child scope. So what happening is while creating a child scope it carries the primitive datatype values & reference(object) datatypes values to child scope, thats why you can see the outer scope date is getting value inside ng-if date field(only first time). But when you update the value in date you will not see the value gets updated to outer scope. Because the way child scope has create primitive type value not carry their references, where as objects are carried with their references. So you can create a object like $scope.model = {} & then define a property into it, that will work. Because object are carried with their references to child scope, updating inner object would sync the outer object as well(they both are same). This rule is called as Dot Rule by which you can fix your issue.
$scope.model = {};
$scope.model.date = new Date();
More convenient way to avoid such kind of scope hierarchy is using controllerAs pattern while using controller on HTML. In this case you shouldn't be using $scope instead you will bind all the properties to controller function context (this). Thereafter when using controller you can use alias of controller to get the values of controller like ng-controller="myCtrl as vm"(here vm is alias of controller which has all information binding to this)
HTML
<div input-field
ng-if="vm.startTypes.selected.value == 'LocalDate' || vm.startTypes.selected.value == 'LocalDateTime'">
<input id="date" type="text" ng-model="vm.date" input-date>
<label for="date">Date</label>
Date inner scope: {{vm.date}}
</div>
Date outer scope: {{vm.date}}
You should use an object with a property when binding to ngModel.
$scope.form = {
date: new Date()
};
ngIf like ngRepeat directive creates its own $scope.
So you could use ng-show instead of ng-if in this specific case.
From docs:
The ngIf directive removes or recreates a portion of the DOM tree
based on an {expression}. If the expression assigned to ngIf evaluates
to a false value then the element is removed from the DOM, otherwise a
clone of the element is reinserted into the DOM.
Example:
angular.module('app', [])
.controller('mainCtrl', function($scope) {
$scope.testa = false;
$scope.testb = false;
$scope.testc = false;
$scope.testd = false;
});
<!DOCTYPE html>
<html ng-app="app">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.7/angular.min.js"></script>
</head>
<body ng-controller="mainCtrl">
Test A: {{testa}}
<br /> Test B: {{testb}}
<br /> Test C: {{testc}}
<br /> Test D: {{testd}}
<br />
<div>
testa (without ng-if):
<input type="checkbox" ng-model="testa" />
</div>
<div ng-if="!testa">
testb (with ng-if):
<!-- if you don't use $parent testb isn't updated -->
<input type="checkbox" ng-model="$parent.testb" />
</div>
<div ng-show="!testa">
testc (with ng-show):
<input type="checkbox" ng-model="testc" />
</div>
<div ng-hide="testa">
testd (with ng-hide):
<input type="checkbox" ng-model="testd" />
</div>
</body>
</html>
angular.module('myApp',[])
.controller('Parent',['$scope',function($scope){
//define nothing
}]).controller('Child',['$scope',function($scope){
$scope.user={username:''};//define an object user
}])
<div ng-app="myApp">
<div ng-controller="Parent">
{{user}}<!--print nothing-->
<div ng-controller="Child">
{{user}} <!--print an object-->
</div>
</div>
</div>
How to deal with the problem above without using the inject of $rootScope
Based on your comment...
i want the parent user print the value of child user without using $rootScope
... you can't accomplish this because the parent scope cannot inherit from the child scope as it is currently defined. View controllers inherit from their parent controllers based on their node structure.
<parent-node>
{{user.name}}
<hr/>
<child-node>
<!-- user inherit from parent node's scope by default -->
{{user.email}}
</child-node>
<another-child-node>
<!-- user inherit from parent node's scope by default -->
{{user.someOtherData}}
</another-child-node>
You either need to define the user on your parent scope like so:
.controller('Parent',['$scope',function($scope){
$scope.user = {};
}])
.controller('Child',['$scope',function($scope){
}])
... or you can handle this via a directive depending on the end User-eXperience.
This is a problem that returns when people use $scope without reading up on the scopes documentation.
This is why I always tell people not to use $scope to bind objects / functions to. Well this and maintainability.
CONTROLLERS:
angular.module('myApp',[])
.controller('Parent',['$scope',function(){
//define nothing
}])
.controller('Child',['$scope',function(){
this.user = {username:''}; //define an object user
}]);
TEMPLATE:
<div ng-app="myApp">
<div ng-controller="Parent as parentCtrl">
{{ parentCtrl.user }} <!-- undefined -->
<div ng-controller="Child as childCtrl">
{{ childCtrl.user }} <!-- object -->
</div>
</div>
</div>
I'm trying to build a WebComponent where you can edit items in an array, with the Polymer javascript framework. Model to DOM bindings work OK, but DOM to Model doesn't - simplified example:
<polymer-element name="rep-test">
<template>
<template repeat="{{item in items}}">
<input type="text" value="{{item}}" placeholder="changes don't work!">
</template>
<button on-click="{{add}}">Add</button>
{{items}}
</template><script>
Polymer({
ready: function() { this.items = [] },
add: function() { this.items.push('') },
itemsChanged: function() { console.log(this.items) } // debug
})
</script>
</polymer-element>
<rep-test></rep-test>
The items are correctly displayed in the input elements, but when I change the value inside an input, changes are not reflected to the model (items). The binding works only in one direction.
Is there any way to make the binding bidirectional, so that when a change occur in the DOM, it is copied in the model ?
I've seen this todo demo which achieves this effect, but it does so with custom events associated with items changes. This obviously works but I'm looking for a more declarative way of doing this with bindings.
Since changes in array’s elements are not reflected to itemsChanged, I would suggest you to listen on the input changes:
<!-- ⇓⇓⇓⇓⇓⇓⇓⇓⇓ -->
<input type="text" on-change="{{ itemChanged }}"
value="{{item}}" placeholder="changes don't work!">
[...]
<!-- inside script -->
itemChanged: function(e) {
console.log(e.path[0].value)
}
Below is the link to the working example: http://plnkr.co/edit/sZYHeMuAVB0G1muHhFNK?p=preview
Here is an example of bidirectional binding: as you change the values in the input fields model is updated:
Plunk
Follow data changes:
<br>
{{testData.employees[0].firstName}}
<br>
{{testData.employees[3].firstName}}
<br><br>
<template repeat="{{person in testData.employees}}">
{{person.firstName}}
<input type="text" value="{{person.firstName}}">
<br>
</template>
I'll reference this post because it explains how this works better then I can:
"...if you change the data values, the new values are NOT available to all other instances - because the instance variables are just copies of the referenced strings. By using an object with data properties, as in the edited version above, and only ever reading from and assigning to the data properties of that object rather than overwriting the object itself, changed values are shareable between instances."
I'm creating a form in HTML using ng-repeat to generate the form elements from an object in the scope. I also use that object to generate other elements outside of the ng-repeat.
A simplified example looks like this in HTML:
<div ng-app="App">
<div ng-controller="Ctrl">
<div class="block1">
<form ng-repeat="(key, value) in test">
<label>{{key}}</label>
<input ng-model="value" />
<p>{{value}}</p>
</form>
</div>
<div class="block2">
<p>
{{test.a}}
</p>
<p>
{{test.b}}
</p>
</div>
</div>
</div>
and this in JS:
angular.module('App', []);
function Ctrl($scope) {
$scope.test = {
a:"abc",
b:"def"
}
}
In this example, the text in block2 is set to the initial values of test.a and test.b. The input values and <p> values inside of the loop are also set to the initial value.
When I modify the values within the inputs, the <p> values inside of the ng-repeat block update correctly, but the <p> tags in block2 fail to update.
Why is this the behavior? Does ng-repeat create its own isolated scope? If so how can I get the controller level scope to update? Also, could somebody explain the thinking behind this behavior and any advantages it provides?
JSFiddle Showing the problem
ng-repeat creates a child scope for each repeated item. As a result you are trying to pass a primitive to child scope which won't create a reference to parent. When you pass objects however, you pass the original object reference.
From the mouth of one of the fathers of Angular:
Always have a dot in ng-model
This is a great video regarding Angular Best Practices given by Angular creator (2012/12/11). Go to minute 31 for well explained detail of this exact situation
Modify data to array of objects:
$scope.test = [{ val:"abc",key:'a'}, {val:"def",key:'b'} ]
Then in repeater:
<form ng-repeat="item in test">
<label>{{item.key}}</label>
<input ng-model="item.val" />
<p>{{item.val}}</p>
</form>
DEMO
try this:
angular.module('App', []);
function Ctrl($scope) {
$scope.test = [
{label:"a", value:"abc"},
{label:"b", value:"def"}
]
}
and
<div ng-app="App">
<div ng-controller="Ctrl">
<div class="block1">
<form ng-repeat="o in test">
<label>{{o.label}}</label>
<input ng-model="o.value" />
<p>{{o.value}}</p>
</form>
</div>
<div class="block2">
<p>
{{test[0].value}}
</p>
<p>
{{test[1].value}}
</p>
</div>
</div>
</div>
Angularjs uses the fact that objects are passed by reference. So, if you pass a object to a function and change the object inside the function, the object outside also changes.
Look at this updated JSFiddle
I'm having a problem in AngularJS where my parent controller and child controller are sharing the same model. In this examples, there is recursive comments:
<div ng-controller="ParentController">
<label>Comment</label>
<textarea name="comment_text" ng-model="comment_text"></textarea>
<input type="submit" value="Leave Comment" ng-click="sendComment($event)" />
<div class="replies">
<div class="areply" ng-controller="ChildController">
Someone said: blah blah blah
<label>Reply</label>
<textarea name="comment_text" ng-model="comment_text"></textarea>
<input type="submit" value="Leave Comment" ng-click="sendComment($event)" />
</div>
</div>
</div>
1st question....am I doing it wrong? And my second is there a way to make sure the parent and child scopes with ng-model do not affect each other?
Child controller scopes are prototypical descendants of their parent controllers' scopes. It's an intentional feature of Angular. The child scope has access to the parent's state, but not the other way around.
If you don't want this behaviour there are a couple of options - either don't use nested controllers (you can use custom services to share data where you need to, which is more test friendly and therefore considered best practice), or, more simply, just call the parent and child scope fields different names.
The code that you show is clearly too abstract to give you a more concrete answer, but it's important to notice that a child controller extends the behaviour of its parent (as a child class with its parent in OOP).
Therefore, a same variable should play the same role in a child controller and in its parent. If you want a different behaviour, you should use another name.
Why not just make your child text area use a different model, and have sendComment take an argument for which comment to accept?
<textarea name="comment_text" ng-model="comment_text"></textarea>
<input type="submit" value="Leave Comment" ng-click="sendComment($event, comment_text)" />
...
<textarea name="comment_text" ng-model="child_comment_text"></textarea>
<input type="submit" value="Leave Comment" ng-click="sendComment($event, child_comment_text)" />
Otherwise, you could create a directive that uses an isolate scope. Try a tutorial that goes over isolate scopes and directives, or whatch a screencast, such as those from egghead.io.
I would take joews second answer and not use nested Controllers, you don't need to. It's best practice to keep these separate. Generally each "page" would have it's own controller that dictates that pages functionality. You can use services and other angularjs resources to make sure you aren't rewriting code all the time.