The instruction Practice Guide in Angularjs (i) describes how to isolate the scope of an instruction. The second part will undertake the last article to continue to introduce. First, we will see how to access the properties of the parent scope from within the directive, using the isolation scope. Next, we will discuss how to choose the correct scope for the instruction based on the controller function and the transclusions. The end of this article is to practice the use of instructions through a full Notepad application.
Data binding between the isolation scope and the parent scope
Generally, the scope of the quarantine directive can be a lot of convenience, especially if you are working on multiple scope models. But sometimes in order for the code to work correctly, you also need to access the properties of the parent scope from within the directive. The good news is that angular gives you enough flexibility to have a selective way of passing in the properties of the parent scope by binding. Let's revisit our HelloWorld directive, whose background color changes as the user enters the name of the color in the input box. Remember when we used the quarantine scope for this command and it didn't work? Now, let's get it back to normal.
Let's say we've initialized the angular module that the app's variable is pointing to. So our HelloWorld instructions are as shown in the following code:
App.directive (' HelloWorld ', function () {return
{
scope: {},
restrict: ' AE ',
replace:true,
Template: ' <p style= ' Background-color:{{color}} ' >hello world</p> ',
link:function (scope, Elem, attrs {
elem.bind (' click ', Function () {
elem.css (' background-color ', ' White ');
Scope. $apply (function () {
Scope.color = "white";
});
};
Elem.bind (' MouseOver ', function () {
elem.css (' cursor ', ' pointer ');
});
}
;}
;
The HTML tag that uses this instruction is as follows:
<body ng-controller= "Mainctrl" >
<input type= "text" ng-model= "color" placeholder= "Enter a Color"/>
The code above is now not working. Because we use an isolated scope, the {{color}} expression inside the instruction is quarantined within the scope (not the parent scope) within the instruction. But the ng-model instruction in the outer input box element points to the color attribute in the parent scope. So, we need a way to bind the two parameters in the isolation scope and the parent scope. In angular, this data binding can be implemented by adding attributes to the HTML element in which the instruction is located and by configuring the corresponding scope property in the directive definition object. Let's take a look at several ways to establish data binding.
Select one: use @ To implement one-way text binding
In the following instruction definition, we specify the attribute color in the isolation scope to the parameter colorattr on the HTML element on which the instruction resides. In the HTML tag, you can see that the {{color}} expression is assigned to the COLOR-ATTR parameter. When the value of an expression changes, the color-attr parameter changes as well. The value of the color property in the isolation scope is also changed accordingly.
App.directive (' HelloWorld ', function () {return
{
scope: {
color: ' @colorAttr '
},
....
The rest of the configurations
};
The updated HTML markup code is as follows:
<body ng-controller= "Mainctrl" >
<input type= "text" ng-model= "color" placeholder= "Enter a Color"/>
We call this method a single binding because in this way you can only pass the string (using the expression {}}) to the parameter. When the properties of the parent scope change, the value of the property in your isolation scope model changes. You can even monitor the scope property changes within the command and trigger some tasks. However, the reverse delivery does not work. You cannot change the value of the parent scope by manipulating the isolation scope property.
Note the point:
When isolating the scope attribute and the name of the instruction element parameter, you can set the scope binding in a simpler way:
App.directive (' HelloWorld ', function () {return
{
scope: {
color: ' @ '
},
....
. The rest of the configurations
};
The HTML code for the corresponding instruction is as follows:
Choose two: Use = implement bidirectional binding
Let's change the definition of the directive to the following:
App.directive (' HelloWorld ', function () {return
{
scope: {
color: ' = '
},
...
.. The rest of the configurations
};
The corresponding HTML modifications are as follows:
<body ng-controller= "Mainctrl" >
<input type= "text" ng-model= "color" placeholder= "Enter a Color"/>
Unlike @, this gives you the ability to specify a real scope data model for a property, rather than a simple string. This allows you to pass simple strings, arrays, and even complex objects to the isolation scope. Bidirectional binding is also supported. Whenever the parent scope property changes, the corresponding attribute in the isolation scope changes, and vice versa. As before, you can also monitor changes to this scope property.
Select three: Use & Execute functions in parent scope
Sometimes it is necessary to invoke the functions defined in the parent scope from the isolation scope. In order to be able to access the functions defined in the external scope, we use &. For example, we want to invoke the SayHello () method from within the instruction. The following code tells us what to do:
App.directive (' SayHello ', function () {return
{
scope: {
sayhelloisolated: ' & '
},
....
The rest of the configurations
};
The corresponding HTML code is as follows:
<body ng-controller= "Mainctrl" >
<input type= "text" ng-model= "color" placeholder= "Enter a Color"/>
<say-hello sayhelloisolated= "SayHello ()"/>
</body>
This plunker example makes a good interpretation of the above concept.
The difference between the parent scope, the child scope, and the isolation scope
As a angular novice, you may find it confusing to choose the right instruction scope. By default, the directive does not create a new scope, but rather inherits the parent scope. But in many cases, this is not what we want. If your command uses the properties of the parent scope heavily, or even creates new attributes, it can contaminate the parent scope. It is not a good idea to have all the instructions using the same parent scope, because anyone can modify the properties in this scope. Therefore, the following principle may help you to choose the right scope for your instructions.
1. Parent scope (SCOPE:FALSE) – this is the default. If your directive does not manipulate the attributes of the parent Scoe, you do not need a new scope. In this case, the parent scope can be used.
2. Sub Scope (Scope:true) – This creates a new scope for the instruction, and the prototype inherits from the parent scope. You should create a new scope if the attributes and methods in the scope of your directive are not related to other directives and to the parent scope. In this way, you also have the properties and methods defined in the parent scope.
3. Isolation scope (scope:{}) – It's like a sandbox! When you create a directive that is self-contained and reusable, you need to use this scope. You will create many scope properties and methods in the instructions that are used only within the instruction and will never be known to the outside world. If so, the scope of isolation is a better choice. The scope of quarantine does not inherit the parent scope.
Transclusion (embedded)
Transclusion is the way to let our instructions contain arbitrary content. We can delay extraction and compile these embedded content under the correct scope, and eventually put them in the location specified in the instruction template. If you set transclude:true in the instruction definition, a new embedded scope is created, and its prototype inherits the child parent scope. If you want your command to use a quarantine scope, but it contains content that can be executed in the parent scope, transclusion can also help.
Let's say we sign up for one of the following directives:
App.directive (' Outputtext ', function () {return
{
transclude:true,
scope: {},
Template: ' <div Ng-transclude></div> '
};
It uses the following:
<div output-text>
<p>hello {{name}}</p>
</div>
Ng-transclude indicates where the embedded content is to be placed. In this example, the DOM content <p>hello {{name}}</p> is extracted and placed inside the <div ng-transclude></div>. One important point to note is that the corresponding property of the expression {{name}} is defined in the parent scope, not the child scope. You can do some experiments in this plunker example. If you want to learn more about scope, you can read this article.
Transclude: The difference between ' element ' and transclude:true
Sometimes I want to embed the instruction element itself, not just its content. In this case, we need to use transclude: ' element '. Unlike Transclude:true, it includes elements marked with ng-transclude instructions in the instruction template. Using transclusion, your link function obtains a link function called transclude, which binds the correct instruction scope and passes in another function that has a copy of the embedded DOM element. You can do this in the transclude function, such as modifying an element copy or adding it to the DOM. Directives like Ng-repeat Use this method to repeat the DOM element. Take a closer look at the Plunker, which uses this method to copy the DOM elements and change the background color of the second instance.
It is also necessary to note that when using transclude: ' element ', the elements of the instruction are converted to HTML annotations. So if you combine transclude: ' element ' and replace:false, then the instruction template is essentially added to the innerhtml of the annotation--that is, nothing happened! Conversely, if you choose to use replace:true, the instruction template replaces the HTML annotation, and everything will work if you wish. Using Replade:false and Transclue: ' element ' is sometimes useful, such as when you need to repeat a DOM element but do not want to keep the first element instance (it will be converted to annotations). To this and puzzled students can read the StackOverflow on the discussion, the introduction of the more clear.
Controller functions and require
If you want to allow other instructions to interact with your instructions, you need to use the controller function. For example, in some cases, you need to combine two instructions to implement a UI component. Then you can add a controller function to the instruction in the following way.
App.directive (' outerdirective ', function () {return
{
scope: {},
restrict: ' AE ',
Controller: function ($scope, $compile, $http) {
//$scope is the appropriate scope for the directive
This.addchild = function ( nesteddirective) {
//This refers to the controller
Console.log (' Got of the message from nested directive: ' + nested Directive.message);};};
This code adds a controller named Outerdirective to the instruction. When another instruction wants to interact, it needs to declare its reference to the controller instance of your instruction (require). This can be done in the following ways:
App.directive (' innerdirective ', function () {return
{
scope: {},
restrict: ' AE ',
require: ' ^ Outerdirective ',
link:function (scope, Elem, Attrs, controllerinstance) {//the Fourth argument is the
Controller instance you require
Scope.message = "Hi, Parent directive";
Controllerinstance.addchild (scope);};
The corresponding HTML code is as follows:
<outer-directive>
<inner-directive></inner-directive>
</outer-directive>
Require: ' ^outerdirective ' tells angular to search for controller in the element and its parent element. The controller instance that is found will be passed into the link function as the fourth parameter. In our example, we send the scope of the embedded instruction to the father instruction. If you want to try this code, open the Plunker in the browser console. At the same time, the last part of this angular official document gives a very good example of instruction interaction and is well worth reading.
A notepad application
In this section, we use the angular instruction to create a simple Notepad application. We'll use HTML5 's localstorage to store our notes. The final product here, you can sneak peek.
We'll create an instruction that shows the Notepad. The user can view the note records that he or she has created. When he clicks on the Add New button, Notepad enters the editable state and allows new notes to be created. When you click the Back button, the new notes are automatically saved. The notes are saved using a factory class called Notefactory, which uses the localstorage. The code in the factory class is very straightforward and understandable. So we'll focus on the code for the instruction.
First step
We start with the Register Notepad instruction.
App.directive (' Notepad ', function (notesfactory) {return
{
restrict: ' AE ',
scope: {},
link:function (Scope, Elem, attrs) {
},
templateurl: ' templateurl.html '
};
Here are a few things to note:
Because we want the instructions to be reusable, we choose to use the quarantine scope. This instruction can have many properties and methods that are not associated with the outside world.
This instruction can be used in the form of attributes or elements, which is defined in the Restrict attribute.
Now the link function is empty this instruction gets the instruction template from the templateurl.html
Second Step
The following HTML forms the template for the instruction.
<div class= "Note-area" ng-show= "!editmode" >
<ul>
<li ng-repeat= "Note in Notes|orderby: ' ID '" >
<a href= "#" ng-click= "Openeditor (note.id)" >{{note.title}}</a>
</li>
</ul >
</div>
<div id= "editor" ng-show= "EditMode" class= "Note-area" true "contenteditable=" NoteText "></div>
<span><a href=" # "ng-click=" Save () "ng-show=" EditMode ">Back</a> </span>
<span><a href= "#" ng-click= "Openeditor ()" ng-show= "!editmode" >add note</a>< /SPAN>
Several important points to note:
The Title,id and content are encapsulated in the note object.
Ng-repeat is used to traverse all notes in notes and to sort in ascending order by automatically generated ID attributes.
We use a property called EditMode to indicate which mode we are in now. In edit mode, the value of this property is true and editable div nodes are displayed. Users enter their own notes here.
If EditMode is false, we are in view mode, showing all notes.
The two buttons are also displayed and hidden based on editMode values.
The Ng-click command is used to respond to a button's Click event. These methods are added to the scope together with EditMode.
The editable Div box is bound to the NoteText and holds the text entered by the user. If you want to edit an existing note, the model initializes the div with its text content.
Third Step
We create a new function called restore () in scope to initialize the various controllers in our application. It will be invoked when the link function executes, and it will be invoked when the Save button is clicked.
Scope.restore = function () {
scope.editmode = false;
Scope.index =-1;
Scope.notetext = ';
};
We create this function inside the link function. EditMode and NoteText have explained this before. Index is used to track the notes currently being edited. When we create a new note, the value of index is set to 1. When we edit an existing note, it contains the ID value of the object.
Fourth Step
Now we're going to create two scope functions to handle edits and save operations.
Scope.openeditor = function (index) {
Scope.editmode = true;
if (index!== undefined) {
Scope.notetext = notesfactory.get (index). Content;
Scope.index = index;
} else {
scope.notetext = undefined;
}
};
Scope.save = function () {
if (scope.notetext!== ') {
var note = {};
Note.title = scope.noteText.length > 10? Scope.noteText.substring (0) + ' ... ': scope.notetext;
Note.content = Scope.notetext;
Note.id = Scope.index!=-1? Scope.index:localStorage.length;
Scope.notes = Notesfactory.put (note);
}
Scope.restore ();
};
There are a few things to note about these two functions:
Openeditor prepare for the editor. If we are editing a note, it gets the contents of the current note and updates the content to the editable div by using Ng-bind.
If we are creating a new note, we will set the notetext to undefined so that when we save the notes, the corresponding listener is triggered.
If the index argument is undefined, it indicates that the user is creating a new note.
The Save function stores notes by using Notesfactory. After the save is complete, it refreshes the notes array so that the listener can monitor changes to the list of notes to be updated in a timely manner.
The Save function call calls Restore () after resetting the controllers, allowing you to enter view mode from edit mode.
Fifth Step
When the link function executes, we initialize the notes array and bind a KeyDown event for the editable Div box to keep our Nodetext model synchronized with the content in the Div. We use this notetext to save our notes.
var editor = Elem.find (' #editor ');
Scope.restore ();
Initialize our app controls
scope.notes = Notesfactory.getall ();
Load notes
editor.bind (' KeyUp keydown ', function () {
scope.notetext = Editor.text (). Trim ();
});
Sixth step
Finally, we use our instructions in HTML just as you would with other HTML elements, and then start taking notes.
Summarize
One important point to note is that anything you can do with jquery, we can do it with angular instructions and use less code. So before using jquery, consider whether we can do the task in a better way without DOM manipulation. Try using angular to minimize the use of jquery.
To take a look at our notebook apps, the ability to delete notes was deliberately omitted. Readers are encouraged to experiment and implement this function themselves. You can go up and down from GitHub to this demo source code.