建立module:javascript
var module1 = angular.module('module1',[]); angular.bootstrap(document.body,['module1']);
建立controller:php
var module1 = angular.module('module1',[]); module1.controller('ctl1', function($scope) { $scope.content = 'i\'m, module 1'; $scope.name = 'module1'; $scope.save = function() { console.log('is function save'); }; }); angular.bootstrap(document.body,['module1']);
建立指令(directive):html
// 衔接上面的代码 module1.directive('testDirective', function() { // 将对象return出去 return{ restrict: 'E',// 指令类型 E:element A:attribute M:comment C: class template: '<div>我是指令生成的内容</div>'; replace: true, //使用模板替换原始标记 指令内本来的数据将被清空 } }); angular.bootstrap(document.body,['module1']);
html引入指令:java
<body> <div ng-controller="ctl1">{{content}} <test-directive>这是本来的内容</test-directive> </div> </body>
以上代码须要注意一下几点:bootstrap
1.咱们定义的指令名称是testDirective,可是在html中要写成test-directive。api
2.咱们把指令里面的代码都放在了function中的return里面,其实return出去的内容就是整个指令对象。数组
3.angular.bootstrap(document.body,['module1']);至关于咱们在html中使用ng-app指令。推荐使用bootstarp而不是ng-app;app
指令的属性以下:框架
name:就是指令名,对应上面代码中的testDirectiveionic
priority:多个指令设置在同一个元素上的执行优先级,执行顺序从低至高:1>2>3.priority的值为正整数,好比priority: 1
terminal: true/false 若是为true,同一个元素上的其余指令的优先级高于本指令,其余指令将中止执行
restrict:ngular内置的一些指令,好比ng-app,ng-click之类的,都是以html元素的属性(atrrbile)的形式来实现的,我在使用ionic框架的时候,里面有不少自定义的标签、好比:<ion-content>
</ion-content>。这也是一个指令,他是经过html元素(element)来实现的。除了这两个以外,指令还支持class(html标签的class属性)、commment(html中的注释)来实现。
在JS代码中,restrict能够有如下赋值:
restrict: 'AE',// 指令类型 E:element A:attribute M:comment C: class
能够是多个restrict: 'AEMC',也能够是一个restrict: 'E'。在html中对应的写法为:
字母 | 声明风格 | 事例 |
E | 元素 | <my-memu title="products"></my-memu> |
A | 属性 | <div my-memu="products"></div> |
C | 样式 | <div class="my-memu:products"></div> |
M | 注释 | <!-- directive my-memu products --> |
template:
同一个指令中只能template和templateUrl只能选其一。
template为模板内容。即你要在指令所在的容器中插入的html代码。
template属性接收一个字符串,相似这样: template: '<div>我是指令生成的内容</div>';
templateUrl:
为从指定地址获取模板内容。即你要在指令所在的容器中插入的一个.html文件。
有了templateUrl咱们就能够将想实现的内容写成一个单独的html模版,在须要的地方插入
angular.module('module1').run(['$templateCache', function ($templateCache) { $templateCache.put('test.html', '<div>This is templateUrl</div>'); }]); angular.module('module1').directive("testDirective", ["$parse", "$http", function ($parse, $http) { return { restrict: "E", templateUrl: "test.html" }; }]);
replace:
是否用模板替换当前元素。true : 将指令标签替换成temple中定义的内容,页面上不会再有<my-directive>标签;false :则append(追加)在当前元素上,即模板的内容包在<my-directive>标签内部。默认false。
var app = angular.module("app", []) .directive("hello", function () { var option = { restrict: "AECM", template: "<h3>Hello, Directive</h3>", replace: true //这里replace为true,因此原来的内容会被template代替 }; return option; })
<html> <head></head> <body> <hello>我是原来的内容</hello><!--消失--> ===> 变成<h3>Hello, Directive</h3> 若是replace为false ===><hello><h3>Hello, Directive</h3></hello> </body> </html>
transculde:
是否使用ng-transculde来包含html中指令包含的原有的内容,接收两个参数true/false
var app = angular.module("app", []) .directive("hello", function () { var option = { restrict: "AECM", template: "<h3>Hello, Directive</h3><span ng-transclude></span>", transculde: true //这里transculde为true,因此原来的内容会被放在有ng-transclude属性的标签内 }; return option; })
<html> <head></head> <body> <hello>我是原来的内容</hello> ===> 变成<hello><h3>Hello, Directive</h3><span ng-transclude>我是原来的内容</span></hello> </body> </html>
scope:
directive 默认能共享父 scope 中定义的属性,例如在模版中直接使用父 scope 中的对象和属性。一般使用这种直接共享的方式能够实现一些简单的 directive 功能。
可是,当你要建立一个能够重复使用的directive的时候,就不能依赖于父scope了,由于在不一样的地方使用directive对应的父scope不同。
因此你须要一个隔离的scope,咱们能够向下面这样来定义咱们的scope。
module1.directive("testDirective", function () { return { scope: { "":"@", "":"&", "":"=" }, template: 'Say:{{value}}' } });
这样就很方便的将咱们directive的上下文scope给定义出来了,可是,若是我想将父scope中的属性传递给directive的scope怎么办呢?
directive 在使用隔离 scope 的时候,提供了三种方法同隔离以外的地方交互:
<script type="text/ng-template" id="scopeTemplate"> <div class="panel-body"> <p>Data Value: {{local}}</p> <p>Data Value: {{secondLocal}}</p> </div> </script> <script type="text/javascript"> angular.module("exampleApp", []) .directive("scopeDemo", function () { return { template: function() { return angular.element( document.querySelector("#scopeTemplate")).html(); }, scope: { local: "@nameprop", secondLocal:"@secondNameprop" } } }) .controller("scopeCtrl", function ($scope) { $scope.data = { name: "Adam" }; }); </script>
<body ng-controller="scopeCtrl"> <div class="panel panel-default"> <div class="panel-body"> Direct Binding: <input ng-model="data.name" /> // 这里改变下面会跟着改变 </div> <div class="panel-body" scope-demo nameprop="{{data.name}}"></div> // 跟着上面数值动态变化 <div class="panel-body" scope-demo second-nameprop="{{data.name}}"></div> // 跟着上面数值动态变化 </div> </body>
指令中的local经过nameprop得到外部属性data.name,因此在控制器中的data.name变化时,指令中的local也会跟着改变;(但若是是只改变local的值,外界的data.name是不会变的);
<script type="text/ng-template" id="scopeTemplate"> <div class="panel-body"> <p>Name: {{local}}, City: {{cityFn()}}</p> </div> </script> <script type="text/javascript"> angular.module("exampleApp", []) .directive("scopeDemo", function () { return { template: function () { return angular.element( document.querySelector("#scopeTemplate")).html(); }, scope: { local: "=nameprop", cityFn: "&city" } } }) .controller("scopeCtrl", function ($scope) { $scope.data = { name: "Adam", defaultCity: "London" }; $scope.getCity = function (name) { return name == "Adam" ? $scope.data.defaultCity : "Unknown"; } }); </script>
<body ng-controller="scopeCtrl"> <div class="panel panel-default"> <div class="panel-body"> Direct Binding: <input ng-model="data.name" /> </div> <div class="panel-body" scope-demo city="getCity(data.name)" nameprop="data.name"></div> </div> </body>
这里全部指令做用域跟外界交互都是经过属性值传入的:<div class="panel-body" scope-demo city="getCity(data.name)"nameprop="data.name"></div>
controller link:
controller: function ($scope, $element, $attrs) {}三个参数
link: function (scope, element, attrs, controller) {}四个参数
第一次尝试:
<custom-directive></custom-directive>
angular .module('app',[]) .directive('customDirective', customDirective); function customDirective() { var directive = { restrict: 'EA', template: '<div>{{vm.test}}</div>', link: function(){}, controller: directiveController }; return directive; } function directiveController() { var vm = this; vm.test = "I'm from Controller"; }
第二次尝试:
angular .module('app',[]) .directive('customDirective', customDirective); function customDirective() { var directive = { restrict: 'EA', template: '<div>{{test}}</div>', link: directiveLink }; return directive; } function directiveLink(scope,elem,attr) { scope.test = "I'm from Link"; }
到这里,咱们不只要开始思索:指令中的controller和link均可以实现一样的效果,那在指令中放这两个属性干吗?咱们的代码究竟是放在controller仍是link中?
咱们首先来看看当两者一块儿使用时,呈现结果的顺序即在编译先后生成的顺序。
angular .module('app',[]) .directive('customDirective', customDirective); function customDirective() { var directive = { restrict: 'EA', template: '<div>xpy0928{{test}}</div>', link: directiveLink, controller:directiveController }; return directive; } function directiveController($scope){ $scope.test = " from contrller cnblogs"; } function directiveLink(scope,elem,attr) { scope.test = scope.test + ",and from link cnblogs"; }
咱们由此得出结论:编译以前执行控制器(controller),编译以后执行连接(link)。
可是咱们还未从根本上解决问题,在controller和link应该放哪些代码?咱们接下来再看一个例子:
var app = angular.module('app',[]); app.directive('customDirective', customDirective); function customDirective() { var directive = { restrict: 'EA', template: '<child-directive><child-directive>', controller: function($scope, $element) { $element.find('span').text('hello cnblogs!'); } }; return directive; } app.directive("childDirective",childDirective); function childDirective() { var directive = { restrict: 'EA', template: '<h1>hello xpy0928</h1>', replace: true, link: function($scope, $element, attr) { $element.replaceWith(angular.element('<span>' + $element.text() + '</span>')); } } return directive; }
此时结果应该仍是hello xpy0928仍是hello cnblogs呢?咱们看下结果
咱们再来将如上进行修改看看:
var app = angular.module('app',[]); app.directive('customDirective', customDirective); function customDirective() { var directive = { restrict: 'EA', template: '<child-directive><child-directive>', link: function(scope, el) { el.find('span').text('hello cnblogs!'); } }; return directive; } app.directive("childDirective",childDirective); function childDirective() { var directive = { restrict: 'EA', template: '<h1>hello xpy0928</h1>', replace: true, link: function($scope, $element, attr) { $element.replaceWith(angular.element('<span>' + $element.text() + '</span>')); } } return directive; }
为何会出现如此状况?由于在controller函数中此时全部child-directive指令中的link函数还未运行因此此时替换无效
由此咱们能够基本得出在controller和link中应该写什么代码的结论:
(1)在controller写业务逻辑(咱们明白业务逻辑大部分是放在服务中),这里所说的业务逻辑乃是为呈现视图以前而准备的数据或者是与其余指令进行数据交互而暴露这个api。
(2)在link中主要操做DOM。
controllerAs:
<div ng-app="myApp"> <div ng-controller="firstController"> <div book-list></div> </div> </div>
angular.module('myApp',[]) .directive('bookList',function(){ return { restrict:'ECAM', //此处定义了该指令的controller属性 controller:function($scope){ $scope.books=[ {name:'php'}, {name:'javascript'}, {name:'java'} ]; this.addBook=function(){ //或者 scope.addBook=... alert('test'); } }, controllerAs:'bookListController', //给当前controller起个名称 template:'<ul><li ng-repeat="book in books">{{ book.name }}</li></ul>', replace:true, //link中注入 bookListController ,就可使用它的方法了 link:function(scope,iElement,iAttrs,bookListController){ iElement.on('click',bookListController.addBook); } } }) .controller('firstController',['$scope',function($scope){ }])
require:
谈require选项以前,应该先说说controller选项,controller选项容许指令对其余指令提供一个相似接口的功能,只要别的指令(甚至是本身)有须要,就能够获取该controller,将其做为一个对象,并取得其中的全部内容。而require就是链接两个指令的锁链,它能够选择性地获取指令中已经定义好的controller,并做为link函数的第四个参数传递进去,link函数的四个参数分别为scope,element,attr和someCtrl,最后一个就是经过require获取的controller的名字,对于controller的名字,能够在指令中用controllerAs选项进行定义,这是发布控制器的关键.
具体如何获取controller呢?require选项的值能够分别用前缀?、^ 和?^进行修饰,也能够不修饰。
若是不进行修饰,好比require:'thisDirective',那么require只会在当前指令中查找控制器
若是想要指向上游的指令,那么就是用^进行修饰,好比require:'^parentDirective',若是没有找到,那就会抛出一个错误。
若是使用?前缀,就意味着若是在当前指令没有找到控制器,就将null做为link的第四个参数;
那么,若是将?和^结合起来,咱们就能够既指定上游指令,又能够在找不到时,不抛出严重的错误。
如今问题来了,若是我想指定多于一个指令,那怎么办呢?这时,咱们能够将须要的指令放进一个数组中,例如:require:['^?firstDirective','^?secondDirective','thisDirective'],这不正是依赖注入的形式吗?但要注意一点,若是使用这种写法的话,原先指令中发布的控制器的名字,即controllerAs选项,就失去意义了。此时,咱们在link中调用这些指令的controller的方法变为:先为上边的数组定义一个名字,接着根据其下标号来指定具体的某个指令。相似于:ctrlList[0]。
假如咱们如今须要编写两 个指令,在linking函数中有不少重合的方法,为了不重复本身(著名的DRY原则),咱们能够将这个重复的方法写在第三个指令的 controller中,而后在另外两个须要的指令中require这个拥有controller字段的指令,最后经过linking函数的第四个参数就能够引用这些重合的方法。代码的结构大体以下:
var app = angular.modeule('myapp',[]); app.directive('common',function(){ return { ... controller: function($scope){ this.method1 = function(){ }; this.method2 = function(){ }; }, ... } }); app.directive('d1',function(){ return { ... require: '?^common', link: function(scope,elem,attrs,common){ scope.method1 = common.method1; .. }, ... } });