Study presents Directive

Learning purpose: To better understand how to use NG Directive.

Directive is probably one of the more complicated things in AngularJS. We usually think of them as instructions. AngularJS comes with built-in directives like ng-app and ng-Controller. One thing you can see is that AngularJS directives start with ng-.

So what exactly is Directive? My personal understanding is like this: encapsulate a section of HTML, JS together to form a reusable independent entity, specific specific functions. Let’s look at the general use of Directive in more detail.

AnguarJS Directive provides common definition formats and parameter descriptions

Look at the following code:

var myDirective = angular.module('directives'[]); myDirective.directive('directiveName'.function($inject) {
    return {
        template: '<div></div>'.replace: false.transclude: true.restrict: 'E'.scope: {},
        controller: function($scope, $element) {},complie: function(tElement, tAttrs, transclude) {
            return {
                pre: function preLink(scope, iElement, iAttrs, controller) {},post: function postLink(scope, iElement, iAttrs, controller) {}}; },link: function(scope, iElement, iAttrs) {}}; });Copy the code
  • Here, it directly returns an object, which contains many attributes, all of which are the definitions of the custom directive. The detailed meanings will be explained below.
  • Return Object parameter description
return {
    name: ' '.priority: 0.terminal: true.scope: {},
    controller: fn,
    require: fn,
    restrict: ' '.template: ' '.templateUrl: ' '.replace: ' '.transclude: true.compile: fn,
    link: fn
}
Copy the code

As shown above, the return object has a number of properties that define the directive.

Let’s explain what they do one by one.

  • name

    • Indicates the name of the current scope. The default value is used when declaring the scope. You do not need to manually set this property.
  • priority

    • Priority. When multiple directives are defined on the same DOM element, it is sometimes necessary to specify the order in which they are executed. This property is used to sort the directive before the compile function is called. If the priorities are the same, the order of execution is uncertain (as a rule of thumb, the higher priority is executed first, and the same priority is bound first and then executed).
  • teminal

    • Last group. If set to true, the current set of priorities will be the last set of directives to be executed, that is, directives with lower priorities will not be executed. The same priorities are still executed, but in an uncertain order.
  • scope

    • true
      • A new scope will be created for the directive. If multiple directives in the same element require new scopes, it will create only one scope. The new scope rule does not apply to the root template, because the root template tends to get a new scope.
    • {}
      • A new, independent scope is created, which differs from a regular scope in that it does not inherit from the parent scope through a prototype. This is very helpful for creating reusable components, which can effectively prevent reading or modifying the data of the parent scope. This single scope creates a hash set of local scope attributes derived from the parent scope. These local scope attributes are useful for aliasing template created values. A local definition is a hash mapping of a set of local Scope properties from which it comes.
  • controller

    • Controller constructor. The controller is initialized before the pre-linking step and allows other directives to be shared via the named require. This will allow directives to communicate with each other and enhance their behavior. The controller injects the following local objects by default:
      • $scope Scope that combines the current element
      • $element Specifies the current element
      • $attrs Attribute object for the current element
      • $transclude A translinking function prebound to the current scope
  • require

    • Request another controller, passed into the linking function of the current directive. Require requires passing in the name of a Directive Controller. If the controller for this name is not found, an error will be raised. The following prefixes can be added to the name:
      • ? Do not throw an exception. This makes the dependency optional
      • ^ Allow to find the controller of the parent element
  • restrict

    • A string containing a subset of the EACM that restricts directive to the specified declaration. If omitted, directive will only be allowed to be declared via attributes
      • E Element name:
      • A Attribute name:
      • Class C:
      • M:
  • template

    • If replace is true, the template content replaces the current HTML element with the attributes and classes of the original element. If replace is false, the template element is treated as a child of the current element.
  • templateUrl

    • Basically the same as template, but the template is loaded via the specified URL. Because template loading is asynchronous, all compilation and linking are suspended until the template loading is complete.
  • replace

    • If set to true, the template replaces the current element rather than being added as a child to it. (When true, the template must have a root node)
  • transclude

    • Compile the content of the element so that it can be used by Directive. It needs to be used with ngTransclude in the template. The advantage of Transclusion is that linking Function can get a Transclusion function pre-bound to the current scope. In general, build a widget and create a standalone scope. Transclusion is not a child of the standalone scope, but a sibling of the standalone Scope. This will allow the widget to have a private state, and transclusion will be bound to the parent scope. I didn’t understand the above paragraph. However, in the actual experiment, if myDirective is called and Transclude is set to true or a string contained in template, the compilation result will be inserted into the content of someTag. If the content of any is not wrapped in the tag, then the result someTag will have an extra span. If there was something else wrapped up, it will remain the same. But if transclude is set to ‘element’, the entire content of any will appear in someTag, wrapped by p.)
      • true/falseConvert the contents of this directive. (In this sense, the content is compiled and moved to the specified location)
      • 'element'Transform the entire element, including other directives of lower priority. (For example, compile the whole content, treat it as a whole (wrap P around it), and insert it in the specified place)
  • compile

    • Here is the compile Function, which is illustrated with examples below
  • link

    • Here is the Link function, which will be explained in detail in the following examples. This attribute is only used if the compile attribute is not defined.

About the scope

When the scope of directive is an object, it is necessary to explain more. Look at the following code:

scope: {
    name: '='.age: '='.sex: The '@'.say: '&'
}
Copy the code

There are some strange prefix symbols about the configuration of various attributes in this scope, such as =, @ and &. Then what are the specific meanings of these symbols? Take a look at the following code:

  • html
<div my-directive name="myName" age="myAge" sex="male" say="say()"></div>
Copy the code
  • javascript
function Controller($scope) {
    $scope.name = 'Pajjket';
    $scope.age = 99;
    $scope.sex = 'I'm a man';
    $scope.say = function() {
        alert('Hello, this is popup message ');
    };
}
Copy the code
We can see the general meaning of several modified prefixes:
  • =: The value of the property in the directive is the value of the property in the Controller corresponding to $scope
  • @Values in the: directive are literal/direct values in HTML
  • &The value in the: directive is the Controller property corresponding to the $scope property, but this property must be a function callback.
  • =or=expression/attr

Sets a bidirectional binding between the local scope property and the parent Scope property. If no attR name is specified, the local name is the same as the attribute name.

  • For example, if the widget’s scope is {localModel: ‘=myAttr’}, the localName in the Widget’s Scope property will map the parentModel of the parent scope. If parentModel changes at all, localModel changes, and vice versa. Bidirectional binding.

  • @ or @attr creates a binding of the local Scope property to the DOM property. Since the attribute value is always of type String, this value always returns a String. If the attribute name is not specified via @attr, the local name will be the same as the DOM attribute name. For example, the scope of the widget is defined as {localName: ‘@myattr ‘}. The localName of the Widget Scope property then maps to the true value of the “Hello” transform. When the value of the name attribute changes, the localName attribute of the Widget scope changes accordingly (one-way only, different from = above). Then the property is read from the parent scope (not from the component’s scope)

  • &or &attr provides a way to execute an expression in the context of the parent scope. If the attr name is not specified, the local name is the same as the attribute name.

    • Such as:


{localFn: ‘increment()’}, then the ISOLATE Scope Property localFn points to a function wrapped around increment(). In general, we want to pass data from the ISOLATE Scope to the Parent Scope through an expression. This can be done by passing a mapping of local variable keys to the Wrapper function of the expression. For example, if the expression is increment(amount), then localFn can be called as localFn({amount:22}) to specify the amount value.

Example of directive

The following examples are built around the parameters described above.

  • Directive declaration instance
// Customize the directive
var myDirective = angular.modeule('directives'[]); myDirective.directive('myTest'.function() {
    return {
        restrict: 'EMAC'.require: '^ngModel'.scope: {
            ngModel: '='
        },
        template: '<div><h4>Weather for {{ngModel}}</h4</div>'}; });/ / define the controller
var myControllers = angular.module('controllers'[]); myControllers.controller('testController'['$scope'.function($scope) {
        $scope.name = 'this is directive1'; }]);var app = angular.module('testApp'['directives'.'controllers'
]);

<body ng-app="testApp">
    <div ng-controller="testController">
        <input type="text" ng-model="city" placeholder="Enter a city" />
        <my-test ng-model="city" ></my-test>
        <span my-test="exp" ng-model="city"></span>
        <span ng-model="city"></span>
    </div>
</body>
Copy the code

The difference and relation between template and templateUrl

TemplateUrl is the same as the root template function, but templateUrl loads an HTML file. In the example above, we can see that the root of the template is an HTML tag, which can be annoying if there are many tags. You can change the template from the previous example.

myDirective.directive('myTest'.function() {
    return {
        restrict: 'EMAC'.require: '^ngModel'.scope: {
            ngModel: '='
        },
        templateUrl:'.. /partials/tem1.html'   // The contents of tem1.html are the contents of template in the previous example.}});Copy the code

The scope to redefine

// Directives. Js defines myAttr
myDirectives.directive('myAttr'.function() {
    return {
        restrict: 'E'.scope: {
            customerInfo: '=info'
        },
        template: 'Name: {{customerInfo.name}} Address: {{customerInfo.address}}<br>' +
                  'Name: {{vojta.name}} Address: {{vojta.address}}'
    };
});

Attrtest is defined in controller.js
myControllers.controller('attrtest'['$scope'.function($scope) {
        $scope.naomi = {
            name: 'Naomi'.address: '1600 Amphitheatre'
        };
        $scope.vojta = {
            name: 'Vojta'.address: '3456 Somewhere Else'}; }]);/ / in the HTML
<body ng-app="testApp">
    <div ng-controller="attrtest">
        <my-attr info="naomi"></my-attr>
    </div>
</body>
Copy the code

The running results are as follows:

Name: Naomi Address: 1600 Amphitheatre // Has a value because customerInfo defined Name: Address: // has no value because Vojta is not defined by scopeCopy the code

We will simply change the above directive.

myDirectives.directive('myAttr'.function() {
    return {
        restrict: 'E'.template: 'Name: {{customerInfo.name}} Address: {{customerInfo.address}}<br>' +
                  'Name: {{vojta.name}} Address: {{vojta.address}}'
    };
});
Copy the code
  • The running results are as follows:
Name: Address:
Name: Vojta Address: 3456 Somewhere Else
Copy the code

Since directive does not define a separate scope and customerInfo is undefined, the result is just the opposite.

The use of transclude

  • Transclude is similar to $().html() in jquery
myDirective.directive('myEvent'.function() {
    return {
        restrict: 'E'.transclude: true.scope: {
            'close': '$onClick'      // On-click ="hideDialog()" in the root HTML
        },
        templateUrl: '.. /partials/event_part.html'
    };
});

myController.controller('eventTest'['$scope'.'$timeout'.function($scope, $timeout) {
        $scope.name = 'Tobias';
        $scope.hideDialog = function() {
            $scope.dialogIsHidden = true;
            $timeout(function() {
                $scope.dialogIsHidden = false;
            }, 2000); }; }]);Copy the code
<body ng-app="phonecatApp">
    <div ng-controller="eventtest">
        <my-event ng-hide="dialogIsHidden" on-click="hideDialog()">
            Check out the contents, {{name}}!
        </my-event>
    </div>
</body>

<! --event_part.html -->
<div>
    <a href ng-click="close()">x</a>
    <div ng-transclude></div>
</div>
Copy the code
  • Note: The final structure of the HTML should look like this:
<body ng-app="phonecatApp">
    <div ng-controller="eventtest">
        <div ng-hide="dialogIsHidden" on-click="hideDialog()"> <span>Check out the contents, {{name}}! </span> </div> </div> </body>Copy the code
  • Check out the contents of the original HTML element! Insert into the template

    In, there will be another oneThe label.controller.link.compileThe relationship between

myDirective.directive('exampleDirective'.function() {
    return {
        restrict: 'E'.template: '

Hello {{number}}!

'
.controller: function($scope, $element){ $scope.number = $scope.number + "22222"; }, link: function(scope, el, attr) { scope.number = scope.number + "33333"; }, compile: function(element, attributes) { return { pre: function preLink(scope, element, attributes) { scope.number = scope.number + "44444"; }, post: function postLink(scope, element, attributes) { scope.number = scope.number + "55555"; }}; }}});/ / controller. Js added myController.controller('directive2'['$scope'.function($scope) { $scope.number = '1111'; }]);//html <body ng-app="testApp"> <div ng-controller="directive2"> <example-directive></example-directive> </div> </body> Copy the code
  • The result of the small example above is as follows:
Hello 1111 22222 44444 5555 !
Copy the code

As can be seen from the result, controller is run first, compile is run, link is not run. Now that we comment out the compile attribute, we get the following result: Hello 1111 22222 33333!

As can be seen from the result, controller runs first, link runs later, link and compile are incompatible. Generally, compile has a higher priority than Link.

reference

  • blog
  • website
  • sf
  • Imooc presents course