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>
Related
Value is not binding with scope's variable if used inside uib-tabset.
Here in following example I tried to get $scope.message inside uib-tab and outside of it :
angular.module("app", ["ui.bootstrap"])
.controller("myctrlr", ["$scope", function($scope){
$scope.message = "my message ";
}]);
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/0.14.3/ui-bootstrap-tpls.min.js"></script>
<div ng-app="app" ng-controller="myctrlr" class="col-md-6 col-md-offset-3">
<uib-tabset>
<uib-tab heading="Static title">
<input type="text" ng-model="message" ng-change="change()" />
<br />
Inside uib-tab : {{ message }}
</uib-tab>
<uib-tab heading="Another title">
I've got an HTML heading, and a select callback. Pretty cool!
</uib-tab>
</uib-tabset>
Outside uib-tab : {{ message }}
</div>
I declared $scope.message and tried to bind it with the input element inside uib-tab. But when I changed it's value, the changes are not reflecting on outside of uib-tab.
jsfiddle link
Basically in angular, if you bind to a primitive the value of the variable is passed around, and not the reference to it, which can break 2-way binding. I'm guessing that the tabset directive creates its own scope, so the valueInScope variable defined in the controller loses its binding in the child scope of the tabset because its a primitive.
$scope.data = {message:'my message'}
Solution by Object
also you can use $parent.parentproperty to bind child scope. This will prevent the child scope from creating its own property.
Solution by using $parent
You can solve this issue by creating an object on the scope and then adding the property on object instead of the scope inside the controller.
$scope.obj = {message : 'my message'};
You can verify this in the below plunker link
http://plnkr.co/edit/3koAJnkOyf6hfGwO6AuD?p=preview
I am using ng-template in angularjs
<script type="text/ng-template" id="ng-wig/views/ng-wig.html">
<div class="ng-wig">
........
</div>
<script>
and i have a textarea using this ng-template.
<textarea ng-wig="content" ng-model="contents"></textarea>
but this ng-model cannot be accessed inside the controller.
please help.
As you are loading your textarea template inside ng-include, if you look at ng-include directive you will see that it does create a new scope which is prototypically inherited from the parent scope.
If you use any scope variable which are declared in controller won't be accessible inside the include div. In order to get the controller variable accessible inside ng-include the you must declare it as object like $scope.model= {} then you should declare the properties in it. like ng-mode="model.contents"
Markup
<textarea ng-wig="model.content" ng-model="model.contents"></textarea>
Controller
$scope.model = {};
Here you can find similar answer
There are several approaches apart from above by which you could solve this issue.
The other way around would be, you could use controller as approach in that way you can avoid. In that you need to use this inside a controller & use controller alias while showing variable on html like vm.contents here
Makrup
<div ng-controller="myCtrl as vm">
<div ng-include="'ng-wig/views/ng-wig.html'"></div>
</div>
Textarea
<textarea ng-wig="content" ng-model="vm.contents"></textarea>
Also you could point to the parent scope of ng-include which is nothing but controller scope just by doing $parent in your ng-model, this case your ng-model would be $parent.contents
Textarea
<textarea ng-wig="$parent.content" ng-model="$parent.contents"></textarea>
Note
Don't use this approach, use the 1st one which is more preferable.
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>
Once I realized how ng-model directive works and was absolutely confident about it's behaviour this example just blowed my mind
<html ng-app>
<head>
<link href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.2.1/css/bootstrap-combined.min.css" rel="stylesheet">
</head>
<body ng-init="names = ['Sam', 'Harry', 'Sally']">
<h1>Fun with Fields and ngModel</h1>
<p>names: {{names}}</p>
<h3>Binding to each element directly:</h3>
<div ng-repeat="name in names">
Value: {{name}}
<input ng-model="name">
</div>
<p class="muted">The binding does not appear to be working: the value in the model is not changed.</p>
<h3>Indexing into the array:</h3>
<div ng-repeat="name in names">
Value: {{names[$index]}}
<input ng-model="names[$index]">
</div>
<p class="muted">Type one character, and the input field loses focus. However, the binding appears to be working correctly.</p>
</body>
ng-repeat fiddle. Main problem is angular's behaviour for three different versions. I understand that ng-repeat creates a new scope for each array item, I suppose (not sure) it tracks only array reference and it's size (so, array items shouldn't cause $digest loop on change), but, what we've got here (watch just first example without $index using): for version 1.0.3 < we have expected behaviour: changing 'name' property with input will only change ngModelController's value (scope inheritance) and this is fair, 1.1.1 - well, what's going on there: we don't even get new values inside of input and! we don't rerender our items cause we don't lose the focus (and i really don't understand why $digest loop fires value replacement for this input as Artem has said), third version - 1.2.1: changing input values also changes 'name' value in the outer scope (as I understand ngModelController should inherit a scope created by ng-repeat directive). So, what really happens (and why) in all three examples?
Using Angular latest version (1.2.1) and track by $index. This issue is fixed
http://jsfiddle.net/rnw3u/55/
<div ng-repeat="name in names track by $index">
Value: {{names[$index]}}
<input ng-model="names[$index]">
</div>
So, I can change a model value from a child controller, but when the child controller is in ng-switch then it doesn't work, why? I created an example to demonstrate it.
One way to avoid this is to use the . in the model name, like bunnies.kills. Is this a bug or this is a feature ?
Using Angular 1.0.6
Using your code structure, in your child controllers you would need to change:
$scope.$parent.kills++;
to
$scope.$parent.$parent.kills++;
Explanation: MainCtrl's scope is the parent scope of SimpleParentCtrl, but the grandparent of Step1Ctrl and Step2Ctrl. As some others pointed out, ng-switch creates its own scope, and then your Step1Ctrl and Step2Ctrl each created a child scope of the ng-switch.
Note: Each time the 1 or 2 button is clicked, both the ng-switch and it's currently matched child controller get a new scope.
Also: In case you happen to be looking in the Angular source and wondering how the ng-switch directive creates its own scope without a scope property, the answer is that it does so manually in its link method via scope.$new(). The directives ng-include, ng-switch, ng-repeat, and ng-view all create new scope this way, either in the link method or the compile method's returned link function.
Resources:
https://github.com/angular/angular.js/wiki/The-Nuances-of-Scope-Prototypal-Inheritance
http://www.youtube.com/watch?v=ZhfUv0spHCY&feature=youtu.be&t=30m
ng-switch creates its own child scope, which is why #sh0ber's answer is one way to get it to work. In general, models should be referenced in controller scopes (hence reference objects), and not be not primitives. So using a . is a "best practice".
This is not a bug, but it is not a feature either. This is the way JavaScript prototypal inheritance works with primitives.
I would take a slightly different approach to this problem.
Rather than use $scope.$parent, I would recommend you move all of your bunny killing logic into a shared service/model.
Also, I would try to avoid referencing parent views/controllers. Referencing the parent can make it difficult to reuse your code and can be painful to debug as the project grows. It is okay for a parent to know about it's children but a child should know little to nothing about it's parent.
Here is an updated Plunk: http://plnkr.co/edit/PLDbfU8Fu7m59A42qdR6?p=preview
HTML
<body ng-controller="MainCtrl">
<p>
Dead bunnies: <strong>{{Elmer.deadWabbits}}</strong>
</p>
<div ng-controller="SimpleParentCtrl">
<button ng-click="Elmer.killTheWabbit()">Kill from simple parent gun</button>
</div>
<hr>
<div ng-switch="" on="step">
<div ng-switch-when="first" ng-controller="Step1Ctrl">
<button ng-click="Elmer.killTheWabbit()">Kill from 1 tab gun</button>
</div>
<div ng-switch-when="second">
<div ng-controller="Step2Ctrl">
<button ng-click="Elmer.killTheWabbit()">Kill from 2 tab gun</button>
</div>
</div>
</div>
<hr>
<p>
<button ng-click="changeStep('first')">1</button> <button ng-click="changeStep('second')">2</button>
</p>
</body>
JS
angular.module('plunker', []).
service("Elmer", [function() {
this.deadWabbits = 0;
this.killTheWabbit = function() {
this.deadWabbits++;
};
}]).
controller('MainCtrl', function($scope, Elmer) {
$scope.Elmer = Elmer;
$scope.step = 'first';
$scope.changeStep = function(name){
$scope.step = name;
};
}).
controller('SimpleParentCtrl', function() {}).
controller('Step1Ctrl', function() {}).
controller('Step2Ctrl', function() {});
One way to avoid this is to use the . in model name, like bunnies.kills. Is this a bug or this is a feature ?
This has been explained numberous times : https://github.com/angular/angular.js/wiki/The-Nuances-of-Scope-Prototypal-Inheritance
and in mhevery's video