First, the problems encountered
The problem occurs when you use AngularJS to nest controllers. Because each controller has its corresponding scope (equivalent scope, control scope), so the controller nesting, also means the scope of nesting. What happens if the Model of the same name is in two scopes at this time? How does the child scope update the Model in the parent scope?
The problem is typical, for example, if the current page is a product list, then you need to define a Productlistcontroller
function Productlistcontroller ($scope, $http) { $http. Get ('/api/products.json ') . Success (function (data) { $scope. productlist = data; }); $scope. selectedproduct = {};} |
You probably saw it. In Scope, a selectedproduct model is defined, indicating that a certain product is selected. This will get the product details, and the page through the AngularJS in the $routeProvider
Automatic Update, pull the new page template, the template has a Productdetailcontroller
function Productdetailcontroller ($scope, $http, $routeParams) { $http. Get ('/api/products/' +$ Routeparams.productid+ '. Json ') . Success (function (data) { $scope. selectedproduct = data; });} |
Interesting thing happened, there is also a selectedproduct, how will it affect the productlistcontroller in the selectedproduct?
The answer is no effect. In Anuglarjs lining scope does inherit objects from the parent scope, but when you try two-way data binding on the base data type (string, number, Boolean), you will find some strange behavior, Inheritance doesn't work as you think. The property of the child scope hides (overrides) the same name property in the parent scope, and changes to the child scope property (form Element) do not update the value of the parent scope property. This behavior is actually not AngularJS specific, and JavaScript itself is the prototype chain that works like this. Developers often don't realize that ng-repeat, Ng-switch, Ng-view and ng-include all create their new sub-scopes, so there are often problems with these directive.
Ii. Methods of Settlement
The solution is to not use the basic data type, but always add a point to the Model.
Use <input type= "text" ng-model= "SOMEOBJ.PROP1" > to replace <input type= "text" ng-model= "Prop1" > |
Isn't it a pit-daddy? The following example is a very clear expression of the wonderful phenomenon I want to express.
App.controller (' Parentcontroller ', function ($scope) { $scope. parentprimitive = "Some primitive" $ Scope.parentobj = {}; $scope. Parentobj.parentproperty = "some value";}); App.controller (' Childcontroller ', function ($scope) { $scope. parentprimitive = "This is not modify the parent" $scope. Parentobj.parentproperty = "This would modify the parent";}); |
View Demo Demo Online
But I do really very very much need to use the original data types such as string number what to do? 2 Methods--
- Used in child Scope
$parent.parentPrimitive
. This will prevent child scopes from creating its own properties.
- In the parent scope, define a function that lets the child scope call to pass the parameters of the original data type to the father, thus updating the properties in the parent scope. (not always possible)
Third, JavaScript's prototype chain inheritance
After the spit is over, let's take a closer look at JavaScript's prototype chain. This is important, especially when you go from server-side development to the front-end, you should be familiar with the classic class inheritance, let's review.
Assume that the parent class Parentscope has the following member properties astring, Anumber, Anarray, AnObject, and Afunction. Subclass Childscope Prototype inherits the parent class Parentscope, so we have:
If a child scope attempts to access a property defined in Parentscope, JavaScript will first look in the child scope, and if it does not, find its inherited Scope to get the property, and if the inherited prototype object Parentscope does not have that attribute, then the following Continue in its prototype, from the prototype chain up to reach Rootscope. Therefore, the following expression results are ture:
childscope.astring = = = ' Parent string ' childscope.anarray[1] = = = 20childscope.anobject.property1 = = ' Parent Prop1 ' Childscope.afunction () = = = ' Parent output ' |
Let's say we execute the following statement
childscope.astring = ' Child string ' |
Instead of being queried, the prototype chain adds a new attribute astring to the childscope. This new property hides (Overrides) the same name attribute in Parentscope. This concept is important when we discuss ng-repeat and ng-include.
Let's say we do the following:
Childscope.anarray[1] = ' childScope.anObject.property1 = ' child Prop1 ' |
The prototype chain was queried because the objects Anarray and AnObject were not found in Childscope. They are found in the Parentscope and the values are updated. No new attributes have been added to the Childscope, and no new objects have been created. (Note: In JavaScript, array and function are objects)
Let's say we do the following:
Childscope.anarray = [555]childscope.anobject = {name: ' Mark ', Country: ' USA '} |
The prototype chain has not been queried, and the child Scope has added two new object attributes, which hide (overwrite) The same name object property in Parentscope.
Should be able to summarize
- If the Childscope.propertyx is read and the Childscope has a property Propertyx, the prototype chain is not queried.
- If Childscope.propertyx is set, the prototype chain is not queried.
One last case,
Delete Childscope.anarraychildscope.anarray[1] = = = + //True |
We removed the attribute from Childscope, and when we access the property again, the prototype chain is queried. Deleting an object's properties causes the attributes from the prototype chain to emerge.
Iv. Scope Inheritance of AngularJS
- Create a new Scope and prototype inheritance: Ng-repeat, Ng-include, Ng-switch, Ng-view, ng-controller, directive with
scope: true
, Directive withtransclude: true
- Creates a new Scope, but does not inherit: directive with
scope: { ... }
. It creates a separate Scope.
Note: By default directive does not create a new Scope, which is the default parameter scope: false
.
Ng-include
Let's say that in our controller,
$scope. myprimitive = $scope. MyObject = {anumber:11}; |
HTML is:
<script type= "Text/ng-template" id= "/tpl1.html" > <input ng-model= "Myprimitive" ></script> <div ng-include src= "'/tpl1.html '" ></div> <script type= "text/ng-template" id= "/tpl2.html" > <input ng-model= "Myobject.anumber" ></script><div ng-include src= "'/tpl2.html '" ></div> |
Each ng-include generates a child scope, and each child scope inherits the parent scope.
Input (such as "77″") to the first input text box, the child scope obtains a new Myprimitive property, overwriting the parent scope with the same name property. This may not be the same as what you expected.
The input (such as "99″") to the second input text box does not create a new property in the child Scope, because tpl2.html binds the model to an object property, and the prototype inherits at this point, Ngmodel looking for an object MyObject and found it in its parent Scope.
If we don't want to change the model from the number base type to the object, we can rewrite the first template with a $parent:
<input ng-model= "$parent. Myprimitive" > |
Input (such as "22″") to this text box will not create a new property. The model is bound to the property of the parent scope (because $parent is a property of the child scope that points to its parent scope).
For all scopes (prototype-inherited or non-inherited), Angular always records the parent-child relationship (that is, the inheritance relationship) through the $parent of the scope, the $ $childHead, and the $childTail property, which is simplified without drawing these properties.
In the absence of a form element, another method is to define a function in the parent Scope to modify the base data type. Because of the prototype inheritance, the child Scope ensures that the function can be called. For example
$scope.setmyprimitive = function (value) { $scope. myprimitive = value in parent Scope;} |
View DEMO. Refer to StackOverflow.
Ng-switch
Ng-switch's prototype inherits the same as Ng-include. So if you need to bind a basic type of data two-way, use a $parent, or change it to an object object and bind to the property of the objects, prevent child scopes from overriding the properties of the parent scope.
Refer to AngularJS, bind scope of a switch-case?
Ng-repeat
Ng-repeat is a little different. Suppose in our controller:
$scope. myarrayofprimitives = [one by one], $scope. myarrayofobjects = [{num:101}, {num:202}] |
There is also HTML:
<ul> <li ng-repeat= "num in myarrayofprimitives" > <input ng-model= "num" > </li> <ul><ul> <li ng-repeat= "obj in myarrayofobjects" > <input ng-model= "Obj.num" > </li><ul> |
For each item,ng-repeat to create a new scope, each scope inherits the parent scope, but the value of Item is also assigned to the new scope (the new property's name is the variable name of the loop). The source code of Angular Ng-repeat is actually this:
Childscope = scope. $new (); Child scope prototype inherits parent Scope ... Childscope[valueident] = value; Create a new Childscope property |
If item is an underlying data type (like Myarrayofprimitives), its value is copied to a new child scope property. Changing the child Scope property value (for example, with Ng-model, that is num
) does not change the array that the parent scope refers to. So in the first ng-repeat above, each sub-scope obtains num
properties independent of the myarrayofprimitives array:
This kind of ng-repeat is different from what you expected. In Angular 1.0.2 and earlier versions, entering a text box will change the value of the gray lattice, which is only visible in the child Scope. After Angular 1.0.3+, the input text will no longer have any effect. (Refer to the explanation on StackOverflow) we want the input to change the myarrayofprimitives array, not the attributes in the sub Scope. For this we must change the model to an array of objects (array of objects).
So if item is an object, then a reference to the original object (not a copy) is assigned to the new Child Scope property. Changing the value of the child scope property (using Ng-model, or obj.num) also alters the object referenced by the parent scope. So the second ng-repeat above can be expressed as:
That's what we want. Entering into a text box changes the value of the gray lattice, which is visible in both the parent scope and the child scope.
Refer to difficulty with Ng-model, ng-repeat, and inputs as well as ng-repeat and databinding.
Ng-controller
Nesting with ng-controller results in the same way as ng-include and Ng-switch are normal prototype inheritance. So the same is not to repeat the same thing. However, "Two controllers use $scope inheritance to share information is considered a bad practice" (from here), you should use the service to share data between controllers.
If you really want to share the data through inheritance, there is nothing special to do, and child scope can directly access all the properties of the parent scope. Refer to Controller load order differs when loading or navigating.
Directives
This is to be discussed in the context of the situation.
scope: false
The default –directive does not create a new Scope, so there is no prototype inheritance. This may seem simple, but also dangerous, because you would think that directive created a new property in Scope, but actually it only used an existing property. This is not good for writing reusable modules and components.
scope: true
– Directive creates a new child scope and inherits the parent scope. If there are multiple directive on the same DOM node to create a new scope, only a new scope is created. Because of the normal stereotype inheritance, as with Ng-include, Ng-switch, you should pay attention to the two-way binding of the underlying type data, and the child scope property overrides the parent scope property with the same name.
scope: { ... }
– At this point directive creates a separate scope with no prototype inheritance. This is a good choice when writing reusable modules and components because directive does not inadvertently read and write to the parent scope. However, sometimes such directives often require access to the properties of the parent scope. The object hash is used to establish a two-way binding (using ' = ') or one-way binding (using ' @ ') between this independent scope and the parent scope. There is also an expression ' & ' used to bind the parent Scope. These all derive from the parent scope to create a local scope property. Note that the HTML attribute is used to establish the binding, and you cannot refer to the property name of the parent Scope in the object hash, you must use an HTML property. For example<div my-directive>
Andscope: { localProp: ‘@parentProp‘ }
It is not possible to bind the parent attribute parentprop to the standalone scope, you must specify this:<div my-directive the-Parent-Prop=parentProp>
Andscope: { localProp: ‘@theParentProp‘ }
。 In a separate scope__proto__
A Scope object (orange-yellow object) is referenced, and the $parent of the independent scope points to the parent scope, so although it is independent and does not inherit from the parent scope prototype, it is still a child scope.
In the following figure, we have<my-directive interpolated="{{parentProp1}}" twowayBinding="parentProp2">
Andscope: { interpolatedProp: ‘@interpolated‘, twowayBindingProp: ‘=twowayBinding‘ }
。
At the same time, suppose directive in its link function.scope.someIsolateProp = "I‘m isolated"
Note: In the link function, use theattrs.$observe(‘attr_name‘, function(value) { ... }
To get the property values that are replaced with the ' @ ' symbol in the standalone Scope. For example, in the link function there isattrs.$observe(‘interpolated‘, function(value) { ... }
The value will be set to 11. (scope.interpolatedProp
In the link function is undefined, insteadscope.twowayBindingProp
defined in the link function because the ' = ' symbol is used)
More reference http://onehungrymind.com/angularjs-sticky-notes-pt-2-isolated-scope/
transclude: true
– At this point directive creates a new "transcluded" child scope, inheriting the parent scope. So if the content in the template fragment, such as those that will replace Ng-transclude, requires two-way binding on the base type data of the parent scope, using the $parent, or the properties of the model object, prevent the child scope property from overriding the parent scope property.
Transcluded and independent scope (if any) are siblings, each scope $parent point to the same parent scope. When both scope and standalone scope exist in the template, the standalone scope property $ $nextSibling will point to scope in the template.
For more information about the transcluded scope, refer to AngularJS, the binding not working in directive with transcluded scope.
In, assuming that directive is the same as in the previous diagram, it's just moretransclude: true
To view the online DEMO, the example has a showscope () function that can be used to check the independent scope and its associated transcluded scope.
Summarize
A total of four scopes:
- Common prototype inheritance of Scope--ng-include, Ng-switch, ng-controller, directive with
scope: true
- Generic prototype inherits the Scope but copy assignment--ng-repeat. Each ng-repeat loop creates a new child scope, and the child scope always gets the new property.
- Independent isolate scope--directive with
scope: {...}
. It is not a prototype inheritance, but ' = ', ' @ ' and ' & ' provide a mechanism to access the parent Scope property.
- Transcluded scope--directive with
transclude: true
. It also follows prototype inheritance, but it is also the brother of any isolate scope.
For all Scope,angular, the parent-child relationship is always recorded through the $parent of the Scope, $ $childHead, and the $childTail property.
Http://www.lovelucy.info/understanding-scopes-in-angularjs.html
Reference links
Http://stackoverflow.com/questions/14049480/what-are-the-nuances-of-scope-prototypal-prototypical-inheritance-in-angularjs
In-depth understanding of AngularJS's Scope (RPM)