在AngularJS中,学习自定义指令: 一种实用的方法

分享于 

28分钟阅读

Web开发

  繁體

介绍

在我的前一篇文章中,我试图解释rtc的基本知识,从而让你开始。 在本文中,我将解释AngularJS的核心概念,即指令。 首先,我们将看到什么是指令,然后我将试着解释如何使用我为你准备的一些示例构建自定义指令。 这里外,在本文中,我将更多地关注实际的示例和代码,而不是编写强大的定义和重大术语。

指令

AngularJS中的指令是AngularJS为我们提供增强HTML元素功能的HTML元素的属性。 AngularJS提供许多内置指令 比如 ng,ng控制器,ng重复,ng-model,ng绑定等等。 因此,在HTML标签上使用ng应用标记一个AngularJS应用程序的开始。 AngularJS还提供了编写定制指令的特性,以根据我们的需要扩展HTML元素的功能。 我们可以扩展html模板的能力来做任何我们能想象到的事情。

为什么使用指令

指令帮助我们制作可以重用组件,这些组件可以在 Angular 应用程序中使用。

指令语法

让我们来看看定义指令的基本伪代码。

angularModuleName.directive('directiveName', function() {
 return {
 restrict: String,
 priority: Number,
 terminal: Boolean,
 template: String or Template Function,
 templateUrl: String,
 replace: Boolean or String,
 scope: Boolean or Object,
 transclude: Boolean,
 controller: String or
 function(scope, element, attrs, transclude, otherInjectables) {.. . },
 controllerAs: String,
 require: String,
 link: function(scope, iElement, iAttrs) {.. . },
 compile: return an Object OR
 function(tElement, tAttrs, transclude) {
 return {
 pre: function(scope, iElement, iAttrs, controller) {.. . },
 post: function(scope, iElement, iAttrs, controller) {.. . }
 }
 // orreturnfunction postLink(...) {.. . }
 }
 };
});

指令基本上返回一个定义对象,其中包含一组键和值对。 这些 key-value 对决定指令的行为。 好的语法可能对你很危险,但不是因为大多数的键是可选的,有些是互斥的。 让我们试着在现实生活中理解这些 key-value 对的含义。

  • 限制:

    用于指定指令在DOM中的使用方式。 指令可以用作属性( A )。元素( E )。class(C) 或者注释( M )。 这些选项可以单独使用或者组合使用。 可选的方法如果不指定选项,则默认值为属性。

    例如:

    作为属性:<div custom-directive/>

    作为元素: <custom-directive></custom-directive>

    作为类: <div class="custom-directive"/>

    作为评论:<--custom-directive-->!

  • 优先级:

    它指定了AngularJS调用的优先级。 优先级高于其他指令的指令将首先调用。 默认值为 0.

  • 终端:

    我从没使用过这个选项,但文档说它用于告诉 Angular 停止调用任何更高优先级的指令。 这是可选的。

  • 模板:

    它为指令指定了 inline HTML模板。

  • TemplateUrl:

    还可以使用包含模板代码的文件的URL加载指令的模板。 如果使用了 inline 模板,则不使用这里选项。

  • 替换:

    它可以设置为 true 或者 false。 指令的模板被追加到使用指令的父元素中。 如果设置了 false,或者它可以将父元素替换为 true。

  • 范围:

    它指定指令的范围。 根据你所建立的指令的要求,这是非常重要的选择。 可以设置指令范围的方法有三种:

  • 指令元素DOM的现有范围。 如果作为默认值设置为 false,则指令范围将与使用指令的DOM元素的范围相同。
  • 从控制器的封闭范围继承的新作用域。 在这个作用域中设置一个新的范围,将从父DOM元素范围中删除所有属性,或者我们可以说它将从封闭指令的控制器范围中删除。
  • 一个独立的范围,它不会从它的父作用域中任何内容。 这种类型的范围通常用于生成可以重用组件。
  • 目前,除非你看到所有的选项,否则 上面 语句和点可能对你没有太大意义。 不用担心,我将在后面的章节中使用示例来清除所有的点。

  • Transclude:

    就像前面解释的,指令可以将它的内容/模板替换或者附加到父DOM元素,但是使用Transclude选项也可以将该指令的模板中的原始内容移动为。

  • 控制器:

    你可以指定使用这里选项用于指令的控制器 NAME 或者控制器函数。

  • ControllerAs:

    可以使用选项将备用 NAME 提供给控制器。

  • 要求:

    它指定要使用的其他指令的NAME。 如果指令依赖于另一个指令,则可以使用这里选项指定相同的NAME。

  • 链接:

    它用于定义一个函数,用于编程地修改模板DOM元素实例,例如添加事件侦听器,设置数据绑定。

  • 编译:

    它可以用于定义一个函数,该函数可以用于以编程方式修改针对指令的各个副本的特性的DOM模板。 它还可以返回链接函数来修改结果元素实例。

示例 1: 创建第一个指令

让我们使用 上面 语法编写第一个指令:

var ngCustomDirectivesApp = angular.module('ngCustomDirectivesApp')
ngCustomDirectivesApp.directive('customLabel', function () {
 return {
 restrict: 'EA',
 template: '<div class="jumbotron"><h2>My First Directive</h2><p>This is a simple example.</p></div>' }
})

说明:

与其他 Angular 应用程序一样,首先创建 Angular MODULE,然后定义指令函数,该函数只返回带有两个属性的对象: " restrict'还有'template"如你所见,对于我使用的restrict 属性,这意味着该指令可以用作DOM的元素或者属性。 template 属性包含一个字符串,其中我定义了指令的结构。 这个指令将呈现一个包含标题和段落的div。 这里要注意的是指令的命名约定,你可以看到我遵循了驼例命名约定,因为 Angular 将分解 NAME。 因这里,customLabel 将成为自定义标签和在我们的HTML模板上,我们将使用自定义标签 NAME。 你可能已经注意到所有的AngularJS预先定义的指令都带有一个ng-前缀。 在DOM指令中将被用作:

<custom-label></custom-label>

或者

<divcustom-label></div>

输出:

注:我已经为div使用了 jumbotron 类,因为我已经为应用程序包含。

示例 2: 使用链接函数

在本例中,我将创建一个简单的'像按钮'指令,它将使用它的链接函数以编程方式绑定指令实例元素上的单击事件,以执行。

指令定义:

ngCustomDirectivesApp.directive('likeButton', function () {
 return {
 restrict: 'EA',
 templateUrl: '../partials/likebutton.html',
 replace: true,
 link: function (scope, element, attrs) {
 element.bind('click', function () {
 element.toggleClass('like');
 })
 }
 }
})

模板:

<buttontype="button"class="btn-sty"><spanclass="glyphicon glyphicon-thumbs-up"></span></button>

css:

.like {
 background-color: blue;color: white;}
.btn-sty {
 border-radius: 12px;}

输出:

说明:

前面的示例一样,这个指令的定义是相同的,除了在这个指令中编写 inline 模板。 让我们试着理解链接函数。 如图,第三部分是访问指令实例元素的范围,第二个是元素,第二个是元素的作用域,第三个是指令元素的属性,第三个是指令元素的属性,第三个是元素。 回到我们的代码,在链接函数中我抓住了元素,必须绑定我在其中切换类的click事件。

示例 3: 了解作用域

在本例中,我将尝试解释指令的范围。 所有指令都有与访问方法和数据 inside 相关的范围,我在最后一个示例中讨论了模板和链接函数。 除非明确指定并使用它的父作用域作为自己的作用域,否则指令不会创建自己的作用域。 如前所述,作用域属性的值决定如何创建作用域并使用 inside 指令。 有三个不同的值被设置为指令的作用域属性。 这些值可以是 true。false 或者 {}.

范围:false

当作用域设置为 false 时,指令将使用它的父范围作为它的作用域,这意味着它可以访问和修改父范围。 如果父级修改它的数据范围,则更改将反映在指令范围内。 同样,如果指令将尝试修改父范围的数据,因为父和指令都可以访问相同的范围。

指令定义:

ngCustomDirectivesApp.controller('dashboardController', function ($scope) {
 $scope.dataFromParent = "Data from parent";
})
ngCustomDirectivesApp.directive('parentScope', function () {
 return {
 restrict: 'E',
 scope: false,
 template: '<input type="text" ng-model="dataFromParent" style="border:1px solid red;"/>' }
}) 
<div ng-controller="dashboardController"> <input type="text" ng-model="dataFromParent" style="border:1px solid green"/>
 <parent-scope></parent-scope></div>

输出:

说明:

首先我创建了一个控制器并定义了范围变量 dataFromParent。 接下来,我创建了一个指令,并将它的作用域设置为 false。 模板中我只创建了一个输入框,它通过 ng-model 绑定到 dataFromParent。 然后我创建了一个父 div,它的控制器是我在第一步中定义的控制器。 在这个div中,我创建了一个输入框,它也绑定到了 dataFromParent,然后在同一div中使用该指令作为父范围。 如果更改任何输入框的值,更改将反映在另一个输入框中,因为两个输入框都访问同一个 dataFromParent。 简而言之,当范围设置为 false 时,控制器和指令使用相同的范围对象。 因此对控制器和指令的任何更改都将同步。

范围:true

当作用域设置为 true 时,创建一个新作用域并分配给指令和范围对象,prototypically继承自它的父作用域。 因此,在此情况下,对新范围对象所做的任何更改都不会被反射回父作用域对象。 但是,因为新范围继承自父范围,所以父范围中所做的任何更改都将反映在指令范围内。

ngCustomDirectivesApp.controller('dashboardController', function ($scope) {
 $scope.dataFromParent = "Data from parent";
})
ngCustomDirectivesApp.directive('inheritedScope', function () {
 return {
 restrict: 'E',
 scope: true,
 template: '<input type="text" ng-model="dataFromParent" style="border:1px solid red;"/>' }
})
<div ng-controller="dashboardController"> <input type="text" ng-model="dataFromParent" style="border:1px solid green"/>
 <parent-scope></parent-scope> <inherited-scope></inherited-scope></div>

输出:

说明:

就像前面的指令我定义了一个新指令。 在这个指令中我设置了作用域,这意味着在这个例子中指令不再访问父范围对象,而是它将创建一个新的范围对象,它本身是。 因此在第一个文本框中进行任何更改时,其他文本框也会更新,但如果在第三个文本框中更改,则不会在前两个文本框中反映改变。 首先,两个文本框直接从控制器访问数据,而第三个文本框由于Prototype继承而在它的新范围内访问数据。

范围:{ }

当范围设置为 Object {}, 时,将为该指令创建一个新的作用域对象。 但这次,它不会从父作用域对象中进行 inherit,它将完成与父范围的分离。 这里作用域也称为独立作用域。 创建这种指令的优点是它们是通用的,可以放置在 inside的任何地方,而不会污染父范围。

ngCustomDirectivesApp.controller('dashboardController', function ($scope) {
 $scope.dataFromParent = "Data from parent";
})
ngCustomDirectivesApp.directive('isolatedScope', function () {
 return {
 restrict: 'E',
 scope: {},
 template: '<input type="text" ng-model="dataFromParent" style="border:1px solid red;"/>' }
})
<div ng-controller="dashboardController"> <input type="text" ng-model="dataFromParent" style="border:1px solid green"/>
 <parent-scope></parent-scope> <inherited-scope></inherited-scope> <isolated-scope></isolated-scope></div>

输出:

说明:

这种情况下,创建了一个没有访问父作用域对象的新作用域,因此数据将不会被绑定。

示例 4: 了解独立作用域

如前所述,如果将指令的范围设置为对象文本 {},,则为指令创建一个独立范围。 如果创建可以用组件,这将非常有用,但在大多数情况下,我们需要指令和父作用域。 因此独立范围提供一些过滤器来在父范围对象和指令范围对象之间通信或者交换数据。 要从父范围传递一些数据到指令范围,我们需要对对象文本进行一些属性,我们将这些属性设置为范围属性。 先看看语法然后再解释。

scope: {
 varibaleName1:'@attrName1',
 varibaleName2:'=attrName2',
 varibaleName3:'&attrName3'}

或者

scope: {
 attrName1:'@',
 attrName2:'=',
 attrName3:'&'}

有三个选项可以在独立范围内将数据从父级传递到指令。

@: 文本绑定或者单向绑定或者只读访问。 它是指令和父作用域之间的一种绑定,它期望将属性映射为表达式( {{ }} ) 或者字符串。 因为它提供了一种方法,因这里在父范围内所做的更改将反映在指令范围内,但在父范围中不会反映。

=: 模型绑定或者双向绑定。 它是父范围和指令之间的双向绑定,它期望属性值为模型 NAME。 同步父作用域和指令作用域之间的更改。

&: 方法绑定或者行为绑定。 它用于将从父范围到指令范围的任何方法绑定在一起,从而使我们能够在父范围内执行任何回调。

例如:

让我们创建一个简单的指令来理解所有作用域选项的用法。 首先,创建一个控制器,它将作为指令的父级。 在控制器中定义名为d ataFromParent的范围变量和一个名为 changeValue的函数来修改变量。

ngCustomDirectivesApp.controller('dashboardController', function ($scope) {
 $scope.dataFromParent = "Data from parent";
 $scope.changeValue = function () {
 $scope.dataFromParent = "Changed data from parent";
 }
})

现在我们来创建我们的指令。

ngCustomDirectivesApp.directive('isolatedScopeWithFilters', function () {
 return {
 restrict: 'E',
 scope: {
 oneWayBindData: '@oneWayAttr',
 twoWayBindData: '=twoWayAttr',
 methodBind:'&parentMethodName' },
 template: '<input type="text" ng-model="oneWayBindData" style="border:1px solid red;"/><br/><input type="text" ng-model="twoWayBindData" style="border:1px solid red;"/><br/><button type="button" ng-click="methodBind()">Change Value</button>' }
})

就像你在范围中所看到的,我添加了三个属性。 这些属性将在我们的指令中用于绑定数据。 指令很简单,我们创建两个文本框和一个按钮。 文本的作用是:用范围的oneWayData 属性绑定文本框,第二个文本框使用范围的twoWayData 绑定,而按钮事件则用范围的methodBind 属性绑定。 请仔细查看范围属性中使用的前缀。

在div中使用这个指令,将它的控制器设置为我们在第一步中定义的控制器。 在这里添加指令,指令元素有三个名为 one-way-attr。two-way-attr和parent-method-name的属性,除了使用 Angular 语法而不是camel大小写as我们在指令定义中定义了 these。 另外,添加一个段落标记并使用带有 dataFromParent的表达式映射它的值,以便我们可以看到 dataFromParent 模型的实时值。

<divng-controller="dashboardController"><isolated-scope-with-filtersone-way-attr="{{ dataFromParent}}"two-way-attr="dataFromParent"parent-method-name="changeValue()"></isolated-scope-with-filters><p>{{dataFromParent}}</p></div>

通过 one-way-attr 模型映射,从父范围中评估 dataFromParent 模型值,并用控制器的two-way-attr映射,并用控制器的功能映射模型的值;。

运行代码并亲自查看指令是否正常工作。

示例 5: 使用控制器

让我们创建一个示例来了解控制器cab如何用于在不同指令之间进行通信。 在本例中,我们将创建 accordion 指令。

步骤1: 创建将保存子 accordion 元素的父 accordion 指令。

ngCustomDirectivesApp.directive('accordion', function () {
 return {
 restrict: 'EA',
 template: '<div ng-transclude></div>',
 replace: true,
 transclude: true,
 controllerAs: 'accordionController',
 controller: function () {
 var children = [];
 this.OpenMeHideAll = function (selectedchild) {
 angular.forEach(children, function (child) {
 if (selectedchild!= child) {
 child.show = false;
 }
 });
 };
 this.addChild = function (child) {
 children.push(child);
 }
 }
 }
})

我们已经定义了一个简单的div,但是要注意的一点是我们使用了 transclude,transclude将使div能够容纳其中的子元素。 对于相同的原因,Transclude选项被设置为 true,以允许div保存子元素。 然后定义一个控制器,这是这个指令的焦点区域。 在控制器中,定义一个函数来在 array 中推动子元素,然后定义一个函数来打开选中的子元素并隐藏所有其他子元素。

步骤 2: 创建 accordion 子元素指令。

ngCustomDirectivesApp.directive('accordionChild', function () {
 return {
 restrict: 'EA',
 template: '<div><div class="heading" ng-click="toggle()">{{title}}</div><div class="content" ng-show="show" ng-transclude></div></div>',
 replace: true,
 transclude: true,
 require: '^accordion',
 scope: {
 title:'@' },
 link: function (scope,element,attrs,accordionController) {
 scope.show = false;
 accordionController.addChild(scope);
 scope.toggle = function () {
 scope.show =!scope.show;
 accordionController.OpenMeHideAll(scope);
 }
 }
 }
})

我们要创建一个标题和其他元素,以便在模板中保存数据或者其他元素。我们将创建一个 head div,以便在模板中保存 click,然后我们就必须在这个div中使用 ng-transclude。 Require属性用于指定这里指令需要 accordion 指令。 of用于隐藏和显示头部div的单击事件。 创建独立范围以使它的成为可以用组件,而title属性则用于用于数据绑定的方式。 在链接函数中,作用域用于访问显示模型,用于显示和隐藏 accordion 指令的内容和控制器。

当用户单击 accordion 元素的标题时,该元素的引用传递给 accordion 控制器的函数,以显示特定元素并隐藏所有其他元素。

步骤 3: 在视图中使用指令。

<accordion><accordion-childtitle="Element 1">Data 1</accordion-child><accordion-childtitle="Element 2">Data 2</accordion-child><accordion-childtitle="Element 3">Data 3</accordion-child></accordion>

输出:

示例 6: 使用控制器

让我们在指令中创建另一个使用控制器的示例。 在本例中,我们将创建一个。 列表元素可以按我们的需要拖动列表中的项目。

步骤 1: 根据传递给它的项的array 定义创建列表的指令。

ngCustomDirectivesApp.directive('smartList', function () {
 return {
 restrict: 'EA',
 templateUrl: '../partials/listdirective.html',
 replace: true,
 scope: {
 items: '=' },
 controller: function ($scope) {
 $scope.source = null;
 },
 controllerAs:'listCTRL' }
})

模板:

<ulclass="ul-sty"><ling-repeat="item in items"class="li-sty"draggable> {{item }}
 </li></ul>

指令非常简单,它包含一个无序列表,列表项是由ng重复生成的。 我们还添加了一个可以拖动的属性,这将使列表项可以拖动。 我们将在后续步骤中定义可以拖动指令。 在指令的作用域属性中,我们使用了两种方式的数据绑定,意味着我们能够访问指令范围。 我们还定义了一个控制器,它包含一个名为source的变量。

步骤 2: 创建可以拖动指令。

ngCustomDirectivesApp.directive('draggable', function () {
 return {
 require:'^smartList',
 link: function (scope, element, attr, listCTRL) {
 element.css({
 cursor: 'move',
 });
 attr.$set('draggable', true);
 function isBefore(x, y) {
 if (x.parentNode == y.parentNode) {
 for (var i = x; i; i = i.previousSibling)
 {
 if (i == y)
 returntrue;
 }
 }
 returnfalse;
 }
 element.on('dragenter', function (e) {
 if (e.target.parentNode!= null) {
 if (isBefore(listCTRL.source, e.target)) {
 e.target.parentNode.insertBefore(listCTRL.source, e.target)
 }
 else {
 e.target.parentNode.insertBefore(listCTRL.source, e.target.nextSibling)
 }
 }
 })
 element.on('dragstart', function (e) {
 listCTRL.source = element[0];
 e.dataTransfer.effectAllowed = 'move';
 })
 }
 }
})

这个可以拖动指令在我们在步骤 1中定义的模板中使用。 在这个指令的链接函数中,我们已经传递了步骤 1中定义的指令的控制器。 它将元素的属性设置为可以拖动,一个函数被定义用来比较所传递元素的父节点。 拖到拖动上的事件拖动已经附加到元素以处理拖放。 我们存储被拖动到控制器变量上的元素,以与当前元素的元素进行比较。 除了其余的代码很简单,我们只是在适当的地方插入节点。

步骤 3: 定义父控制器。

ngCustomDirectivesApp.controller('dashboardController', function ($scope) {
 $scope.itemsdata = ['Apple', 'Mango', 'Banana', 'PineApple', 'Grapes', 'Oranges'];
})

步骤 4: 使用指令:

<divng-controller="dashboardController"><smart-listitems="itemsdata"/></div>

指令中,控制器的itemsData被传递到指令的范围。

输出:


输出

你可以根据需要拖动项目以排序项目。

结束语

我试图使用示例来覆盖指令的所有方面。 可以在项目的各种方案中使用指令。 你可以在AngularJS的官方文档中了解有关指令的更多信息。 如果你想要主指令,那么通过在我们的项目中创建指令来实现这一。 我已经把完整的代码与文章一起。 你还可以从 GitHub ( https://github.com/vikas0sharma/Custom-Directive-Examples ) 下载或者克隆它。 我希望它能帮助你理解AngularJS中的指令。


angular  DIR  learn  Directive  
相关文章