In the first article of this series, we introduced AngularJS custom commands and some simple examples. In this article, we have gone through the independent scopes of AngularJS, and independent scopes are important when creating custom commands.
What is an independent scope?
By default, commands can directly access attributes in the parent scope. For example, the following command outputs the name and street attributes of a custom object based on the parent scope:
Angular. module ('ctictivesmodule'). Direve VE ('mysharedsize', function (){
Return {
Template: 'Name: {customer. Name} Street: {customer. street }}'
};
});
Although this can be done, in actual use, you must know a lot of information about the parent scope related to this command to ensure that the command works properly, or you can just use the ngInclude and HTML templates to do the same (as discussed in the previous article ). The problem with this is that if the parent scope changes a bit, this command may not be useful any more.
If you want to create a reusable instruction, you certainly cannot allow the instruction to depend on any attribute of the parent scope, but use an independent scope instead. This is a schematic diagram of comparing the shared scope and independent scope:
The diagram above shows that the shared scope allows the parent scope to extend down to the instruction. An independent scope cannot work in this way. An independent scope is like a wall in the circle of your commands. The parent scope cannot directly access the attributes of the directive content. It is like the following:
Create independent scopes in commands
It is easy to create an independent scope in the command: you only need to add a scope parameter to the command. As shown in the following code, this will automatically create an independent scope for the command.
Angular. module ('ctictivesmodule '). Direve VE ('myisolatedscope', function (){
Return {
Scope :{},
Template: 'Name: {customer. Name} Street: {customer. street }}'
};
});
Currently, the scope is independent. In the preceding example, the customer object from the parent scope will not be available in the instruction. When this command is used, the following output will appear in the next View (note that the values of name and street are not output ):
Name: Street:
Since the independent scope breaks the bridge between the communication with the parent scope, how should we handle the data exchange with the parent scope? We can use @, =, and & to define the scope. It seems a bit strange at this glance, but it will not be too bad if we are skilled in using it. The following describes how to use these symbols.
Local scope attributes
Independent scopes provide three alternative ways to interact with external scopes. These three methods are implemented by specifying different identifiers in the scope attribute of the instruction. The three identifiers are @, =, and &. Let's take a look at how they work.
@ Local scope attributes
@ Is used to read the string value outside the command. For example, a controller may define a name attribute on the $ scope object. You can use @ to read the value of this name in the instruction. The following figure further explains how to use it:
Define $ scope. name; in the controller;
$ Scope. name attributes must be readable in the instruction;
The command defines a local scope attribute name in the independent scope (note that this attribute name can be any name that meets the requirements and does not need to be the same as that in the external scope ). Use scope: {name;
@ Indicates that the new name attribute of the instruction is a string value from the external scope. If the value of this name in the external scope is modified, the value in the command will be automatically updated;
The View containing this command can bind a value to the command through the name attribute.
The following is an example of integration. Assume that the following controller is defined in an app:
Var app = angular. module ('ctictivesmodule', []);
App. controller ('customer', ['$ scope', function ($ scope ){
Var counter = 0;
$ Scope. customer = {
Name: 'David ',
Street: '1970 Anywhere St .'
};
$ Scope. customers = [
{
Name: 'David ',
Street: '1970 Anywhere St .'
},
{
Name: 'Tina ',
Street: '2017 Crest .'
},
{
Name: 'Michelle ',
Street: '2014 Main St .'
}
];
$ Scope. addCustomer = function (){
Counter ++;
$ Scope. customers. push ({
Name: 'new customer' + counter,
Street: counter + 'Cedar Point St .'
});
};
$ Scope. changeData = function (){
Counter ++;
$ Scope. customer = {
Name: 'James ',
Street: counter + 'Cedar Point St .'
};
};
}]);
The command creates an independent scope that allows you to bind the name attribute from an external scope:
Angular. module ('ctictivesmodule ')
. Directive ('myisolatedscopewithname', function (){
Return {
Scope :{
Name :'@'
},
Template: 'Name: {Name }}'
};
});
You can use this command as follows:
<Div my-isolated-scope-with-name = "{customer. name}"> </div>
Note how the value of $ scope. customer. name is bound to the name attribute in the independent scope of the instruction.
The code is output as follows:
Name: David
As mentioned above, when the value of $ scope. customer. name changes, the command automatically changes immediately. However, if the command internally modifies its own name attribute, the value of $ scope. customer. name in the external scope will not be changed. If you want to synchronize the values in the independent scope with those in the external scope, use = instead @.
If you want to make the name attribute in the directive independent scope different from the attribute bound to the view, you can use the following alternative syntax:
Angular. module ('ctictivesmodule ')
. Directive ('myisolatedscopewithname', function (){
Return {
Scope :{
Name: '@ someothername'
},
Template: 'Name: {Name }}'
};
});
In this case, the name attribute will be used inside the command, and someOtherName will be used in external data binding instead of name. The data binding syntax is as follows:
<Div my-isolated-scope-with-name some-other-name = "{customer. name}"> </div>
I generally prefer to make the attribute names in the independent scope consistent with the attribute names bound to the view, so I generally do not use this method. However, in some scenarios, system flexibility can be maintained. This is valid when @, =, and & define the local scope.
= Local scope attributes
@ It is convenient and practical when you only need to pass string values to the instruction, but there is nothing to do when you need to reflect the value change in the instruction to the external scope. When you need to create two-way binding between the independent scope of the instruction and the external scope, you can use the = character, as shown in the figure below:
Define $ scope. person object in the controller;
$ Scope. person object needs to be passed into the instruction by creating two-way binding;
Command to create a custom local independent scope attribute customer by using scope: {customer: '=;
= Indicates that objects in the local scope of the instruction must be bound in two-way. If the value of the attribute in the external scope changes, the value in the local scope of the instruction will also be automatically updated; if the value is modified in the instruction, the corresponding value in the external scope will also be modified;
The view template in the command can now be bound to the customer attribute of the independent domain.
The following is an example of using =:
Angular. module ('ctictivesmodule'). Direve VE ('myisolatedscopewithmodel', function (){
Return {
Scope :{
Customer: '=' // bidirectional data binding
},
Template: '<ul> <li ng-repeat = "prop in customer" >{{ prop }}</li> </ul>'
};
});
In this example, the command uses an object as the value of the customer attribute, and uses ngRepeat to traverse all the attributes of the customer object and finally output it to the <li> element.
Use the following method to pass data to the command:
<Div my-isolated-scope-with-model customer = "customer"> </div>
Note that when using a local scope attribute, you cannot use the attribute name directly (no double curly braces are required) as when using ). In the preceding example, the customer object is directly placed in the customer attribute. The command uses ngRepeat to traverse all attributes of the customer object and output them. The following content is output:
David
1234 Anywhere St.
& Local scope attributes
Before learning how to use it, you must first learn how to use the @ local scope attribute to pass a string value to the instruction, it also knows how to bind a directive to an object in an external scope through the = local scope attribute. The last local scope attribute is to bind an external function with the & character.
& The local scope attribute allows the instruction caller to pass a function that can be called internally by the instruction. For example, if you are writing a command, the end user clicks a button in the command and needs to trigger an event in the controller. You cannot hard-code the click event inside the instruction code, so that the external controller cannot know what happened inside the instruction. Triggering an event as needed can solve this problem well (using $ emit or $ broadcast), but the controller needs to know what the event name is, so it is not optimal.
A better way is to let the consumer of the command pass the command to a function that can be called as needed. Each time a command detects a specified operation (for example, when a user clicks a button), it can call the function that is passed to it. The consumer of this command has 100% of control, knows exactly what happened in the command, and authorizes the control function to pass in the command. Below is a simple diagram:
Define a function in the controller called $ scope. click;
$ Scope. click function needs to be passed in to the command, so that the command can call this function when the button is clicked;
Command to create a custom local scope attribute called action. Use scope: {action. In this example, action is just an alias for click. When an action is called, click is also called;
& The character is basically equivalent to: "Hey, give me a function and I can call it when some events occur in the command ";
The template in the instruction can contain a button. When a button is clicked, the action (reference of an external function) function will be called.
The following is an example of use:
Angular. module ('ctictivesmodule ')
. Directive ('myisolatedscopewithmodelandfunction', function (){
Return {
Scope :{
Datasource: '= ',
Action :'&'
},
Template: '<ul> <li ng-repeat = "prop in datasource" >{{ prop }}</li> </ul>' +
'<Button ng-click = "action ()"> Change Data </button>'
};
});
Note that the following code from the instruction template references the action local scope function and is called when the button is clicked.
<Button ng-click = "action ()"> Change Data </button>
The following is an example of using this command. Of course, we recommend that you give a shorter name to the command.
<Div my-isolated-scope-with-model-and-function
Datasource = "customer"
Action = "changeData ()">
</Div>
The changeData () function that is passed into the action attribute of the instruction is defined in the controller. The definition of the controller is the same as that in the previous article. The changeData () function is defined as follows:
$ Scope. changeData = function (){
Counter ++;
$ Scope. customer = {
Name: 'James ',
Street: counter + 'Cedar Point St .'
};
};
End
In this series of articles, you will see some key points, such as templates, independent scopes, and local scope attributes. To create an independent scope, you only need to add a scope attribute in the instruction definition and set the value to an object. Three local scope attributes are available:
@ Is used to pass a string value to the command
= Used to create a bidirectional binding object
& Allows the input of a function that can be called internally by the command
Translator's note
The value of the scope attribute can be a bool type. If the value is false, no independent scope is used. It is no different from not writing this attribute.
The attribute names defined in scope must be named in the camper mode, while those defined in the template must use the hyphen syntax. Assume that there is a command named datePicker, and some definitions of scope are as follows:
Scope :{
IsOpen: "= ",
CurrentDate: "= ",
OnChange :"&"
}
The usage in the view is as follows (assuming that the function and scope attributes in the quotation marks are defined in the controller ):
<Div date-picker
Is-open = "openState"
Current-date = "currentDate"
On-change = "dateChange ()"
> </Div>
'''
In addition, if some attributes in 'scope 'are optional (for example, in the above example, 'isopen' is set to false by default, and the command user can choose not to pass this attribute ), angularJS reports an error when using this command. That is to say, attributes defined by 'scope 'must be passed when calling the command (errors will be reported if the command is not passed, but the program running is not affected ). To solve this problem, add a question mark after the optional parameter '? 'Identify this property is optional. The modified instruction 'scope 'section is as follows:
'''Js
Scope :{
IsOpen: "=? ", // Pay attention to the question mark. It is optional to specify this parameter.
CurrentDate: "= ",
OnChange :"&"
}