AngularJS: ng-include and the scopes - angularjs

Why if I can't set the value of $scope.myProperty of the controller ExampleCtrl from a html that inside of ng-include. But if I define $scope.myObj.myProperty and in the html I refer like ng-model="myObj.myProp" work fine?
Example:
<div ng-app="MyApp">
<div ng-controller='ExampleCtrl'>
<div ng-include="'#inlcude'"></div>
<!-- THIS DON'T WORK -->
<p><b>myProperty:</b>{{myProperty}}</p>
<!-- THIS WORK -->
<p><b>myObj.myProperty:</b>{{myObj.myProperty}}</p>
</div>
</div>
<!-- INCLUDE HTML -->
<div id='include'>
<input ng-model='myProperty' type='text' />
<input ng-model='myObj.myProperty' type='text' />
</div>
I understand thet ng-include create a new scope, but why from the html of include I don't see an simple propety of your parent scope?
Thanks

When the input writes to myProperty it will (at that point) create a property on the child scope. This child scope property hides/shadows the parent property of the same name. Any changes the child makes will be made on the child scope property. The parent scope can not see this new, child scope property – it does not know that this other property exists. It will find its own property when {{myProperty}} is interpolated.
When the other input writes to myObj.myProperty, it follows the prototype chain and writes to the property on the parent scope.
For a much more detailed answer (with lots of pictures), see What are the nuances of scope prototypal / prototypical inheritance in AngularJS?

Related

ng-keyup firing but not reading properly when used with an ng-if inside a directive template

I have a directive and it works fine in a way such that when I type something the search() scope function inside my directive fires and sets $scope.query with the input text.
here is the directive template
<div class="container">
<div class="system-filter-header">
<div class="no-gutter">
<div class="system-search-wrapper search-wrapper-width">
<i ng-click="search($evt)" class="fa fa-search"></i>
<input type="text" ng-keyup=search($evt) class="search pull-left suggesstions-styles"
ng-model="query" ng-attr-placeholder="Search...">
</div>
</div>
</div>
</div>
here is the scope function which gets triggered
$scope.search = function() {
console.log($scope.query.length)
}
But when I used an ng-if="true" in first line of template (true used for generalizing only, I want to do a different conditional check inside ng-if) such that,
<div class="container" ng-if="true">
still the search gets triggered but the console.log gives always 0 and it doesn't seem to update the $scope.query value as it stays as $scope.query = ''
throughout the typing.
EDIT
Here is a an example codepen with almost similar behaviour. The problem is with the searchBox directive and I have added ng-if=true to the template but searching doesn't work. When I remove the ng-if searching works fine.
Any reason for this?
Rule of thumb in AngularJS: your ng-model should always include a dot. Otherwise AngularJS directives that create child scopes (like ng-if or ng-repeat) will create a duplicate property on that child scope instead of the parent scope. Following the controllerAs convention completely mitigates this behavior.

Why ng-model in directive template not updating when value is changed from the controller

I'm Using a directive and from two way binding i'm changing a date object variable that is passed to the directive initially.
But when I do some changes to the date variable like
$scope.valueee = 1;
$scope.press = function(){
$scope.searchterm.setHours($scope.valueee++, 0, 0, 0);
if(!$scope.$$phase)$scope.$apply()
}
but it wont update the view with the ng-model in the template view inside the directive
'ng-model="term"'
Following is the code example
jsfiddle link
I think you are running into this issue when binding directly to a primitive:
https://github.com/angular/angular.js/wiki/Understanding-Scopes
Emphasis mine
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. New AngularJS developers often do not realize that ng-repeat, ng-switch, ng-view, ng-include and ng-if 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.)
This issue with primitives can be easily avoided by following the "best practice" of always have a '.' in your ng-models – watch 3 minutes worth. Misko demonstrates the primitive binding issue with ng-switch.
The plunker linked to above above shows your issue directly (source below):
javascript:
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
/*
ng-repeat generates new scopes which will be child scopes of the scope within
which they are generated. In other words, this scope is the parent scope for
the child scopes generated by the ng-repeat in this example. Child scopes
inherit things from their parent's scope.
*/
// The initial main image
var initialImg = "http://3.bp.blogspot.com/-z8kzafZYkfQ/UERf6IbjJJI/AAAAAAAAALE/qaAxqqawXpM/s1600/Cat+Pictures+1.jpg";
/*
A primitive holding the URL for the main image
This scope's child scopes will "shadow" this primitive, which basically means
they'll get their own copy that is initialy the same value. The child scopes
can only see their own copy though, so modifying the value in the child scope
does not affect the value in the parent scope.
*/
$scope.mainImgUrl = initialImg;
/*
An object holding the URL for the main image
This scope's child scopes will NOT get their own copy of this object.
Referencing main or main.imgUrl in the child scope will reference this object
on this scope (unless the child scope explicitly define its own "mainImg" object.)
*/
$scope.mainImg = { url: initialImg };
// Our 'thumbnail' images
$scope.images = [
"http://happy.fm/wp-content/uploads/2011/10/random-owl.jpg",
"http://www.superhumor.com/emoticonos/8761.gif"
];
});
html:
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>document.write('<base href="' + document.location + '" />');</script>
<link rel="stylesheet" href="style.css" />
<script data-require="angular.js#1.0.x" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js" data-semver="1.0.7"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<h1>ng-click inside ng-repeat setting value on parent scope</h1>
<p>
Example to illustrate the nuances of prototypical inheritance. See
this Angular Tutorial Step 10 question
and
Understanding Scopes
.
</p>
<h3>Using primitive:</h3>
<div class="example">
<img class="mainImg" ng-src="{{mainImgUrl}}" />
<p>This is the parent scope with the main image.</p>
<p>$scope.mainImgUrl == {{mainImgUrl}}</p>
<div class="thumbs">
<p>Thumbs generated with ng-repeat, with ng-click setting <strong>$scope.mainImgUrl</strong> (click on them to see what happens):</p>
<div class="thumbDiv" ng-repeat="img in images">
<img class="thumb" ng-src="{{img}}" ng-click="mainImgUrl = img" />
<p>This is a child scope generated by ng-repeat.</p>
<p>$scope.mainImgUrl == {{mainImgUrl}}</p>
</div>
</div>
</div>
<h3>Using object:</h3>
<div class="example">
<img class="mainImg" ng-src="{{mainImg.url}}" />
<p>This is the parent scope with the main image.</p>
<p>$scope.mainImg.url == {{mainImg.url}}</p>
<div class="thumbs">
<p>Thumbs generated with ng-repeat, with ng-click setting <strong>$scope.mainImg.url</strong> (click on them to see what happens):</p>
<div class="thumbDiv" ng-repeat="img in images">
<img class="thumb" ng-src="{{img}}" ng-click="mainImg.url = img" />
<p>This is a child scope generated by ng-repeat.</p>
<p>$scope.mainImg.url == {{mainImg.url}}</p>
</div>
</div>
</div>
</body>
</html>

Angular - Changing scope is not getting reflected

This is weird as it should be pretty straightforward. I will post my code first and then ask the question:
html -
<div ng-controller="myController" ng-switch on="addressCards">
<div>
{{addCustom}} // does not get changed
<div ng-if="addCustom === false">
{{addCustom}} // does get changed
<button type="button" class="btn btn-primary btn-icon-text" ng-click="addCustom = true">
<span class="icon icon-plus"></span>
click here
</button>
</div>
</div>
</div>
controller -
(function(){
'use strict';
angular.module('myApp')
.controller('myController',['$scope',myController]);
function myController($scope){
$scope.addCustom = false;
}
})();
So I simply introduced a scope variable - addCustom - in my controller and set it to false as default. This variable controls if a div is shown or not. I am also outputting the value of the scope on the html at 2 different locations. Please see above.
But when I change its value in an ng-click within this divs, its value is changing at the second location(within the div) but not the first one(outside the div). Because of this the div does not change state as well.
I am not able to figure what might be possibly wrong here. Can someone please help?
The thing happening is when you have ng-repeat,ng-switch and ng-if directive, angular creates child scope for those element wherever they are placed. Those newly created scope are prototypically inherited from there parent scope.
On contrast Prototypal Inheritance means?
If you have scope hierarchy, then parent scope property are accessible inside child scope, only if those property are object (originally object referenced is passed to child scope without creating its new reference). But primitive datatypes are not accessible inside child scope and if you looked at your code addCustom scope variable is of primitive dataType.
Lets discuss more about it.
Here you have myController controller which has addCustom scope variable of primitive type & as I said above ng-switch & ng-if directive are compiled they do create new child scope on that element. So in your current markup you have ng-switch on ng-controller="myController" div itself. For inner html it had created a child scope. If you wanted to access parent scope inside child(primitive type) you could use $parent notation before scope variable name. Now you can access the addCustom value by $parent.addCustom.
Here its not over when angular compiler comes to ng-if div, it does create new child scope again. Now inner container of ng-if will again have child scope which is prototypically inherited from parent. Unfortunately in your case you had primitive dataType variable so you need to use $parent notation again. So inside ng-if div you could access addCustom by doing $parent.$parent.addCustom. This $parent thing will solve your problem, but having it on HTML will make unreadable and tightly couple to its parent scope(suppose on UI you would have 5 child scope then it will look so horrible like $parent.$parent.$parent.$parent). So rather you should go for below approach.
Follow Dot rule while defining ng-model
So I'd say that you need to create some object like $scope.model = {} and add addCustom property to it. So that it will follow the prototypal inheritance principle and child scope will use same object which have been created by parent.
angular.module('myApp')
.controller('myController',['$scope',myController]);
function myController($scope){
$scope.model = { addCustom : false };
}
And on HTML you will use model.addCustom instead of addCustom
Markup
<div ng-controller="myController" ng-switch on="addressCards">
<div>
{{model.addCustom}} // does not get changed
<div ng-if="model.addCustom === false">
{{model.addCustom}} // does get changed
<button type="button" class="btn btn-primary btn-icon-text" ng-click="model.addCustom = true">
<span class="icon icon-plus"></span>
click here
</button>
</div>
</div>
</div>
Other best way to deal with such kind of issue is, use controllerAs pattern while using controller on HTML.
Markup
<div ng-controller="myController as myCtrl" ng-switch on="addressCards">
<div>
{{myCtrl.addCustom}} // does not get changed
<div ng-if="myCtrl.addCustom === false">
{{myCtrl.addCustom}} // does get changed
<button type="button" class="btn btn-primary btn-icon-text" ng-click="myCtrl.addCustom = true">
<span class="icon icon-plus"></span>
click here
</button>
</div>
</div>
</div>
From the Docs:
The scope created within ngIf inherits from its parent scope using prototypal inheritance. An important implication of this is if ngModel is used within ngIf to bind to a javascript primitive defined in the parent scope. In this case any modifications made to the variable within the child scope will override (hide) the value in the parent scope.
-- AngularJS ng-if directive API Reference
The rule of thumb is don't bind to a primitive, instead bind to an object.
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. New AngularJS developers often do not realize that ng-repeat, ng-if, 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.)1
This issue with primitives can be easily avoided by following the "best practice" of always have a '.' in your ng-models – watch 3 minutes worth. Misko demonstrates the primitive binding issue with ng-switch.1
Ng-if introduces a different scope. Try this as an attribute of your button:
ng-click="$parent.addCustom = false"
This will assure that you're accessing the same scope.
It's because of this that it's always good practice to use the ControllerAs syntax. All attributes are bound to the controller object and namespaced accordingly, meaning you never run in to this problem. I've updated your example using the ControllerAs syntax to demonstrate its use.
HTML
<div ng-controller="myController as vm" ng-switch on="addressCards">
<div>
{{vm.addCustom}}
<div ng-if="vm.addCustom === false">
{{vm.addCustom}}
<button type="button" class="btn btn-primary btn-icon-text" ng-click="vm.addCustom = true">
<span class="icon icon-plus"></span>
click here
</button>
</div>
</div>
</div>
Controller
(function(){
'use strict';
angular.module('myApp')
.controller('myController', [ myController ]);
function myController () {
var vm = this;
vm.addCustom = false;
}
})();
Here is an excellent article providing more detail about ControllerAs and it's advantages.
Both Classic Controller and Controller As have $scope. That's super important to understand. You are not giving up any goodness with either approach. Really. Both have their uses.

How to access forms of ng-included html's from the parent html?

I have step1.html which will be included in a parent.html as follows
<body>
<button ng-click='go(childForm)' value='Click'>
<div ng-include="'step1.html'"></div>
</body>
step1.html ,
<form name='childForm'>
...
</form>
inside go function, I am always getting undefined as the input parameter. I can understand that the parent html is rendered before the step1.html is included, so the value will be undefined. Is there any way to send the child form to the parent scope method?
ng-include creates a child scope so your form name is only being set on that child scope
To fix it, make the form name an object property and define that object in the parent controller
$scope.model={};
<form name='model.childForm'>
Now the model object exists in the ng-include child scope and ng-form will add the new childForm property to it

How do I share data between child directives in angular?

Lets say I have two directives parent-dir and child-dir
<div parent-dir>
<div child-dir>
<div child-dir>
</div>
<div parent-dir>
<div child-dir>
<div child-dir>
</div>
How do I share data between the first parent-dir directive and all of the child-dir directives within that element but isolated of the other parent-dir?
It depends on how you are setting up your directives. One way would be for the parent and child directives to create a new scope. Children scope automatically inherit the parent's scope and can specifically access the parent's scope with $parent. Siblings cannot [easily] access each others scope. For in your example, child-dir can access their parent-dir but parent-dir cannot access another parent-dir
You can create a new scope using the following in the object you return when defining your directives
scope: {}
Note: if you use scope: true, you will create an isolated scope which has the parent scope of rootScope and not its logical parent.

Resources