Home  >  Article  >  Web Front-end  >  How to create custom directive in angularjs? Detailed process of creating custom instructions with angularjs

How to create custom directive in angularjs? Detailed process of creating custom instructions with angularjs

寻∝梦
寻∝梦Original
2018-09-07 15:04:121265browse

Documentation translated to angularjs.org. The documentation explains when you want to create your own directives in your AngularJS application, and how to implement them. Let us read this article together

What is a command?

At a high level, directives are markup on a DOM element (as attributes, element names, comments, and CSS classes) used to tell Angularjs's HTML Compiler ($compile) to attach specific behavior to this DOM element. (for example, via event listening), or even to transform a DOM element and its children.

Angularjs comes with a set of built-in implementations, like ngBind, ngModel, and ngClass. As well as the controllers and services you create, you can create your own directives for Angularjs to use. When Angularjs starts ( bootstraps) of your application, the HTML compiler traverses the DOM matching directives corresponding to DOM elements.

What does it mean to "compile" an HTML template? For AngularJS, "compilation" means attaching directives to the HTML to make it interactive. The reason we use the term "compile" is that the recursive process of attaching directives mirrors the process of compiling source code in compiled programming languages.

Directive matching
Before we start writing directives, we need to know when using a given directive, Angularjs How the HTML Compiler determines

Similar to the terminology used when an element matches a selector, we say that an element matches a directive when the directive is part of its declaration.

In the example below, we say that the <input> element matches the ngModel directive

<input> <!-- as an attr -->

The following <input> elements also match ngModel:

<input>

The following

<person>{{name}}</person>

Normalization (tentative translation normalization)

AngularJS normalizes the tag and attribute names of elements to determine which elements match which directives. We typically define (refer to) a directive by its case-sensitive camelCase canonicalized name (e.g., ngModel). However, since HTML is case-insensitive, we refer to instructions in the DOM by lowercase, usually using the dash (-) delimiter to separate different words (such as ng-model)

The normalization process is as follows:
1. Remove x-, data- at the beginning of element/attribute;
2. Convert -, _,: delimiters to camelCased camelCas
For example, the following forms are equivalent and are the same as ngBind Directives match:

  Hello <input> 


   
   
   
   
   

Directive type

A - attributes    <p> </p>
C - class name    <p> </p>
E - element name   <person></person>
M - comments    <!-- directive: person -->

Best Practice: Prefer using directives via tag name and attributes over comment and class names.Doing so generally makes it easier to determine what directives a given element matches.

Best Practice: Comment directives were commonly used in places where the DOM API limits the ability to create directives that spanned multiple elements (e.g. inside

elements) . AngularJS 1.2 introduces ng-repeat-start and ng-repeat-end as a better solution to this problem. Developers are encouraged to use this over custom comment directives when possible..

create directive

First let us discuss the API for registering directives. Like controllers, directives are also registered on modules. In order to register a directive, you need to use the module.directive API. module.directive accepts a standardized directive name, followed by a factory function. This factory function should return an object with different options to tell the $compile directive how it should behave when matched.

The factory function is only called once when $conpile matches the directive for the first time. You can instruct any initialization work here. This (factory) function is called using $injector.invoke which makes it injectable like a controller.

We'll go through some common directive examples and then dive into the different options and compilation process.

Best Practice: In order to avoid collisions with some future standard, it's best to prefix your own directive names. For instance, if you created a directive, it would be problematic if HTML7 introduced the same element. A two or three letter prefix (e.g. btfCarousel) works well. Similarly, do not prefix your own directives with ng or they might conflict with directives included in a future version of AngularJS.

As a follow-up example, we will use the my prefix (e.g. myCustomer).

Template-expanding Directive

Suppose you have a large template that represents customer information. This template is repeated multiple times in your code. When you change it in one place, you have to change it in several other places. This is a good opportunity to simplify your template using directives.

Let's create a directive that simply replaces its content with a static template:

Note that we have bindings in this directive. After $compile compiles and links

, it will try to match directives on the element's child elements. This means you can build directives within directives (nested directives). We'll see how to write an example later.

在上面的例子中,我们列出了模板选项(template attribute of return object in factory function),但随着模板大小的增长,这将变得令人讨厌。

Best Practice: Unless your template is very small, it's typically better to break it apart into its own HTML file and load it with the templateUrl option.

如果你熟悉ngInclude,templateUrl就像它一样工作。下面是使用templateUrl代替的相同示例:

templateUrl也可以是一个函数,它返回要加载和用于指令的HTML模板的URL。AngularJS将使用两个参数调用templateUrl函数:指令被调用的元素以及与该元素相关联的attr对象。

Note: You do not currently have the ability to access scope variables from the templateUrl function, since the template is requested before the scope is initialized
注:(要访问socpe上的值,应该在post-link阶段).

How to create custom directive in angularjs? Detailed process of creating custom instructions with angularjs

When should I use an attribute versus an element?  Use an element when you are creating a component that is in control of the template.The common case for this is when you are creating a Domain-Specific Language for parts of your template. Use an attribute when you are decorating an existing element with new functionality.

用元素来使用myCustomer指令时明智的选择,因为你不用一些“customer”行为修饰一个元素,你定义一个元素核心行为作为一个costomer组建。(想看更多就到PHP中文网angularjs参考手册中学习)

隔离指令的Scope

我们以上的myCustomer指令很好,但是它有一个致命缺陷。我们只有在一个给定的scope下使用。

在其目前的实现上,我们应该需要去创建一些不同点控制器用来重用这个指令。
https://plnkr.co/edit/CKEgb1e...

这明显不是一个好的解决方案。

我们说项的是把指令内部的scope与外部scope(controller scope)分离,并且映射外部scope到指令内部scope。我们可以通过创建一个isolate scope来做。为此,我们可以使用指令的scope选项。

https://plnkr.co/edit/E6dTrgm...
看index.html文件,第一个元素绑定info属性值为naomi,它是我们已经暴露在我们的控制器上的scope。第二个绑定info为igor。

让我们仔细看看scope选项

//... 
scope: { customerInfo: '=info' },
//...

除了可以将不同的数据绑定到指令中的作用域外,使用isolated scope还有其他作用。

我们可以通过添加另一个属性vojta来展示,到我们的scope并尝试从我们的指令模板中访问它:
https://plnkr.co/edit/xLVqnzt...

请注意{{vojta.name}}和{{vojta.address}}为空,意味着它们未定义(undefined)。虽然我们在控制器中定义了vojta,但它在指令中不可用。

顾名思义,该指令的 isolate scope隔离了除显式添加到作用域的模型之外的所有内容:scope: {}散列对象. 这在构建可重用组件时很有用,因为它可以防止组件改变模型状态,除了显式传入。

Note: Normally, a scope prototypically inherits from its parent. An isolated scope does not. See the "Directive Definition Object - scope"section for more information about isolate scopes.

Best Practice: Use the scope option to create isolate scopes when making components that you want to reuse throughout your app.

创建一个操纵DOM的指令

在这个例子中,我们将建立一个显示当前时间的指令。每秒一次,它会更新DOM以反映当前时间。

想要修改DOM的指令通常使用link选项来注册DOM监听器以及更新DOM。它在模板被克隆之后执行,并且是放置指令逻辑的地方。

link接受一个带有一下签名的函数function link(scope, element, attrs, controller, transcludeFn) { ... }, 其中:

  • scope是一个Angularjs scope 对象

  • element 是一个此指令匹配的jqLite包装元素

  • attrs是一个具有标准化属性名称及其对应属性值的键值对的散列对象。

  • controller是指令所需的控制器实例或其自己的控制器(如果有的话)。确切的值取决于指令的 require属性。

  • transcludeFn是预先绑定到正确的包含范围的transclude链接函数。

For more details on the link option refer to the $compile API page.

In our link function, we want to update the displayed time every second, or a user changes the time format string bound to our command. We will use the $interval service to call the handler periodically. This is easier than using $timeout, but is also better for end-to-end testing, where we want to make sure all $timeouts are completed before completing the test. If the directive is removed, we also want to remove $interval, so we don't introduce a memory leak
https://plnkr.co/edit/vIhhmNp...

There are a few things to note here. Just like the module.controller API, function parameters in module.directive are dependency injected. So we can use $interval and dateFilter in the directive's link function.

We register an event element.on('$destroy',...). What triggers this $destroy event?

AngularJS publishes some special events. When a DOM node compiled with AngularJS's compiler is destroyed, it fires the $destroy event. Likewise, when an Angularjs scope is destroyed, it broadcasts a $destroy event to listening scopes.

By listening to this event, event listeners that may cause memory leaks can be removed. Listening events registered to scope and element will be automatically cleaned up when the DOM is destroyed, but if you registered a listener on the service, or registered a listener on a DOM node that has not been deleted, you must clean it up yourself, otherwise You risk introducing a memory leak.

Best Practice: Directives should clean up after themselves. You can use element.on('$destroy', ...) or scope.$on('$destroy', ...) to run a clean-up function when the directive is removed.

Create a directive that wraps other elements

We have seen that you can use isolate scope to pass a model to a directive, But sometimes you want to be able to pass in an entire template instead of a string or object. We say we want to create a "dialog box" component. The dialog box should have the ability to wrap any arbitrary content.

For this, we need to use the transclude option.

What exactly does the transclude option do? transclude makes the contents of the directive accessible through this option to the scope of the external directive rather than the internal scope.

To illustrate this point, please look at the following example. Notice that we added a link function in script.js, redefining the name to Jeff. What do you think the {{name}} binding will achieve?

As usual, we thought {{name}} should be Jeff. But, what we see is Tobias.

The transclude option changes the way scopes are nested. It causes the contents of a transcluded directive to have any scope content outside the directive, rather than any internal scopes. In doing so, it makes the content accessible to the external scope.

Please note that if the directive does not create its own independent scope, then the scope in scope.name = 'Jeff' will refer to the outer scope and we will see Jeff in the output.

This behavior makes sense for directives that encapsulate something, because otherwise, you have to pass in each model you want to use separately. If you have to pass in every model you want, then you can't really use arbitrary content, right?

Best Practice: only use transclude: true when you want to create a directive that wraps arbitrary content.

Next, we want to add buttons to this dialog box , and allows users using the directive to bind their own behavior to the dialog box.

We want the function we pass to run by calling it from the scope of the directive, but that it will run in the context of the registered scope.

We have seen before how to use =attr in the scope option, but in the example above, we use &attr instead. The & binding allows a directive to trigger the evaluation of an expression within a primitive scope at a specific point in time. Any legal expression is allowed, including one containing a function call. As such, & binding is ideal for binding callback functions to directive actions.

When the user clicks x in the dialog, the directive's close function is called, thanks to ng-click. This close call on the isolated scope will actually evaluate the expression hideDialog(message) in the context of the original scope, causing the hideDialog function in the Controller to be run.

It is usually expected to pass in data from the isolate scope to the parent scope through an expression. This can be accomplished by passing a map of local variable names and values ​​​​to the expression wrapper function. For example, the hideDialkog function accepts a message to display when the dialog is hidden. This is indicated by the directive call close({message: 'closing for now'}). Then the local variable message will be accessed (is available) within the on-close expression.

Best Practice: use &attr in the scope option when you want your directive to expose an API for binding to behaviors .

Create a directive that adds an event listener

Previously, we used link functions to create directives that manipulate their DOM elements. Building on this example, let's make a directive that reacts to events on its element.

For example, what if we want to create a directive that allows the user to drag an element?

创建一个通信的指令

你可以组建任何指令通过模板使用他们。

有时,你需要一个由指令组合构建的组件。

想象你想要有一个容器,其中容器的内容对应于哪个选项卡处于活动状态的选项卡。

myPane指令有require选项值为^^myTabs. 当指令使用此选项,&compile将抛出一个错误除非特定的controller被找到。 ^^前缀表示该指令在其父元素上搜索控制器。(^前缀将使指令在自身元素或她的父元素上寻找控制器;又没任何前缀,指令将值操作自身)

所以这个myTabs contoller从哪里来的?指令可以特定一个controllers通过使用 controller选项。如你所见,myTabs指令使用了此选项。就像ngController,此选项附加一个控制器到指令的模板上。

如果需要从模板中引用控制器或绑定到控制器的任何功能,则可以使用选项controllerAs将控制器的名称指定为别名。该指令需要定义要使用的此配置的范围。这在指令被用作组件的情况下特别有用。

回头看myPane的定义,注意到link函数的最后一个参数:tabCtrl。当指令需要控制器时,它将接收该控制器作为其link函数的第四个参数。利用这一点,myPane可以调用myTabs的addPane函数。

如果需要多个控制器,则指令的require选项可以采用数组参数。发送给链接函数的相应参数也将是一个数组。

angular.module('docsTabsExample', [])
.directive('myPane', function() {
  return {
    require: ['^^myTabs', 'ngModel'],
    restrict: 'E',
    transclude: true,
    scope: {
      title: '@'
    },
    link: function(scope, element, attrs, controllers) {
      var tabsCtrl = controllers[0],
          modelCtrl = controllers[1];

      tabsCtrl.addPane(scope);
    },
    templateUrl: 'my-pane.html'
  };
});

明的读者可能想知道链接和控制器之间的区别。基本的区别是控制器可以暴露一个API,并且链接函数可以使用require与控制器交互。

Best Practice: use controller when you want to expose an API to other directives. Otherwise use link.

总结

到此我们已经看了大多数指令的用法,每一个样例演示了一个创建你自己指令的好的起始点。

你可能深入感兴趣于编译过程的解释可以在这里获得compiler guide.

$compile API 有一个全面的指令清单选项以供参考。

最后就是本篇文章到这结束了(想看更多就到PHP中文网angularjs学习手册中学习),有问题的可以在下方留言提问

The above is the detailed content of How to create custom directive in angularjs? Detailed process of creating custom instructions with angularjs. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn