Angular学习随记
###指令篇
####什么是指令?
在特定DOM元素上运行的函数,指令可以扩展元素功能,编写属于自己的html标签
####如何定义指令?
angular.module("name",[]).directive("dname",function(){
	return {}
});
两个参数
- name(指令名)
- 
    factory-function(函数) 这个函数返回一个对象,其中包含指令的全部行为,$compile服务利用这个对象,在dom调用指令时构造行为 
####restrict 告诉angular该指令在DOM中的声明方式,默认是A
- E(元素)
<my-direct> </my-direct>
- A(属性)
<div my-direct="express"> </div>
- C(类名)
<div class="my-direct:express"> </div>
- M(注释)
<--directive:my-directive express-->
建议:使用属性声明,便于浏览器兼容
####priority(优先级)
默认:0
ngRepeat:1000(内置指令里优先级最高的)
同一个元素上有两个优先级相同的指令,声明在前面的先调用
####terminal
- true
- false
这个参数用来告诉Angular停止运行当前元素上比本指令优先级低的指令
当摸个元素上的指令被设置了该参数,就不要用优先级比他低的指令了,因为不会执行
ng-if优先级>ngView
当ng-if为true,ngView会执行,当ng-if为false,ngView就不会执行
####template 两种形式:
- 一段HTML文本
- 一个可以接受两个参数的函数(tElement,tAttrs),并返回一个代表模板的字符串,两个参数的t代表template 第二种在后面会讲到
有时你会见到这种样子
<body ng-app="my">
<div haha></div>
	<script type="text/javascript">
		angular.module('my', []).directive("haha",function(){
		return {
			template: '<div>\
				<a>hhhh</a>\
				<h3>sdsdsd</h3>\
				</div>\ '
			}
		});
	</script>
每行末尾的反斜杠,是为了让angular正确解析多行字符串,如果不用反斜杠会报错
实际生产中
- 更好的选择是使用templateUrl引用外部模板
####templateUrl 两种形式:
- 外部html文件路径的字符串
- 一个可以接受(tElement,tAttrs)两个参数的函数,并返回一个外部HTML文件路径的字符串
无论哪种方式,模板的URL都将通过AngularJs内置的安全层,$getTrustedResourceUrl,可以保护模板不会被不信任的源加载
默认情况下,调用指令时会在后台通过Ajax来请求Html模板文件
模板加载是异步的,意味着编译和链接要暂停,等待模板加载完成
模板加载后,angular会将它默认缓存到$templateCache服务中。在实际生产中,可以将模板缓存到一个定义模板的js文件中
就不需要通过XHR来加载模板了,未完待续*(后面讲)
####replace 如果设置这个参数,就将值设为true(替换),
设置为false与不使用该参数一样(魔板被当做子元素插入调用指令元素内部)
####scope参数
默认值:false
- false(父作用域 = 子作用域)
    修改子作用域会影响父作用域 
- true(父作用域 ! = 子作用域)
    子作用域继承副作用域,修改子作用域不会影响父作用域 
- {} (父作用域 ! = 子作用域)
    一个全新的独立作用域,不存在父子关系,理解为同级吧 
对比指令中link的 $scope和controller的 $scope
####绑定策略 目的:让新的作用域可以访问当前本地作用域中的变量
- @
将本地作用域同dom属性值进行绑定,指令内部作用域可以使用外部作用域的变量
- =
双向绑定
- &
从隔离scope中调用父scope中定义的函数,为了能够访问外部scope中定义的函数
	<body ng-app="myApp">
	<div ng-controller="haha">
 	<div dir say="hello()"></div>
</div>
  <script>
    angular.module('myApp', [])
    .controller('haha', function($scope) {
      // we can leave it empty, it just needs to be defined
      $scope.hello = function(){
      	console.log("haha");
      }
    })
   
    .directive('dir', function() {
      return {
        restrict: 'A',
	    scope: {
	    	yoyo:'&say'
	    },
	    template:"<button ng-click='yoyo()'>hh</button>"
      }
    })
  </script>
</body>
Scope:{
	some:"need"
},
template:'<h3></h3>',
controller: function($scope){
$scope.some === "need"
}
注意:上面的代码是有问题的
- 指令的的Scope变量不是直接在Scope中设置的,而是在<directive>标签中设置的,
- 通过Scope配置的关系传递进隔离作用域中,
- scope起类似xml的映射关系作用
<div my-directive some-attr="123"></div>
Scope:{
	haha:'@someAttr'
},
template:'<h3></h3>',
controller: function($scope){
$scope.haha//就可以获取到了
}
####transclude
- 默认为false
- 如果设置了,必须设置为true
- 用途:指令中嵌入,将指令标签内部的标签或指令嵌入指令内部模板中
- 这样可以将任意内容,作用域传给指令,指令内部可以访问外部指令的作用域,模板也可以访问外部作用域对象
- 用法:ng-transclude
<div dir title="123">
        <ul>
            <li>1</li> 
            <li>2</li>
        </ul>
    </div>
        <script>
                 angular.module("mytry",[])
                 .directive("dir",function(){
                    return {
                        restrict:'EA',
                        scope:{
                            title:'@'
                        },
                        transclude:false,
                        template:'<div><h3></h3><p ng-transclude></p></div>'
                    }
                 });
        </script>
问题:
- 使用了transclude,控制器无法正常监听数据模型的变化
####controller
- 字符串
- 函数
angular.module('myApp',[]).directive('myD',function(){
return{
controller:'SomeControll'
}
})
angular.module('myApp',[]).controller('SomeControll',function($scope,$element,$attrs,$transclude){
})
- $scope
与指令元素相关的当前作用域
- $element
当前指令对应的元素
- atts
由当前属性组成的对象
<div id="s" class="e"></div>
{
id:"s",
class:"e"
}
- $trusclude
如果你在指令定义中设置 transclude:true,一个新的嵌入的scope会被创建,它原型继承自父scope。 如果你想要你的指令使用隔离的scope,但是它所包含的内容能够在父scope中执行,transclusion也可以帮忙
app.directive('outputText', function() {
  return {
    transclude: true,
    scope: {},
    template: '<div ng-transclude></div>'
  };
});
<div output-text>
  <p>Hello </p>//在父作用域里被初始化了
</div>
1.ng-transclude指明的是一个插入的位置
2.指令中标签里的元素都会先删除然后被嵌入包含后的内容所替换。
但是局限出现了
- ng-transclude只能提供一个统一的插入位置,如果想要改变传入的dom结构,一个ng-transclude是不够的
例如:
div ng-controller="Ctrl">
      <pane title="">
            <span class="time">time</p>
            <p class="type"><p>
            <p class="content"><p>
      </pane>
</div>
最终要变成
<div style="border: 1px solid black;">
    <div style="background-color: gray">我是标题<span class="time">我是时间</span></div>
    <p class="type">我是分类</p>
    <p class="content">我是内容</p>
</div>
于是两种解决方法
- 使用compile函数的transclude参数
app.directive('pane', function() {
    return {
        restrict: 'EA',
        template: '<div style="border: 1px solid black;"><div class="title" style="background-olor: gray"></div></div>',
        replace: true,
        transclude: true,
        compile: function(element, attrs, transcludeFn) {
            return function (scope, element, attrs) {
                transcludeFn(scope, function(clone) {
                    var title= element.find('title');
                    var time = clone.find('.time');
                    var type = clone.find('.type'); 
                    var text= clone.find('.content'); 
                                                                                                                                      
                    title.append(time);
                    element.append(type);
                    element.append(text)
                });
            };
        }
    };
});
transcludeFn是一个function:
transcludeFn(scope, function(clone){})作用域和嵌入包含的内容,clone嵌入的内容的jquery封装,有了它,我们就可以做任何想要做的dom操作了。
- 在controller里注入$transclude
app.directive('pane', function() {
    return {
        restrict: 'EA',
        template: '<div style="border: 1px solid black;"><div class="title" style="background-olor: gray"></div></div>',
        replace: true,
        transclude: true,
        controller: ['$scope', '$element', '$transclude', function ($scope, $element, $transclude) {
            $transclude(function(clone, scope) {
                var title= element.find('title');
                var time = clone.find('.time');
                var type = clone.find('.type');
                var text= clone.find('.content');
                                                                                                                                
                title.append(time);
                element.append(type);
                element.append(text)
            });
        }],
    };
});
demo2
<body ng-app="myApp">
	<div ng-controller="haha">
 	<div dir >
 		<h3>ss</h3>
 	</div>
</div>
  <script>
    angular.module('myApp', [])
    .controller('haha', function($scope) {
      // we can leave it empty, it just needs to be defined    
    })   
    .directive('dir', function() {
      return {
        restrict: 'A',
        transclude:true,
	    controller:function($scope,$element,$transclude){
	    	$transclude(function(clone){
	    		console.log(clone);
	    		var a = angular.element('<a>');
	    		a.attr('href',clone.text());
	    		a.text(clone.text());
	    		$element.append(a);
	    	})    		
	    }
      }
    })
  </script>
####transclude的作用域
在官方文档中提到过deretive的作用域是单独的,transclude也创建了一个单独的作用域,而且与derectvie的作用域是平行的(兄弟)
注意
指令的控制器和link函数可以进行互换,区别是,控制器用来提供可在指令间复用的行为,link只定义当前指令中的行为,无法在指令间复用
技术上讲,$scope会在dom元素被实际渲染之前传入到controller中,在有些情况,controller中的scope与我们预期的不同,这样scope就无法保证可以被正常更新
当要与当前屏幕上作用域交互的话,可以使用被传入到link中的scope
####匿名控制器
- 无需注入scope
- controller 中scope改用this
两种形式:
- 指令中
.directive('dir',function(){
return {
restrict:'A',
template:'<h4></h4>',
controllerAs:'m',
controller: function(){
this.msg = 'hhhh'
}
}
});
- 普通
<body ng-app="myApp">
	<div ng-controller="haha as me">
 	
</div>
  <script>
    angular.module('myApp', [])
    .controller('haha', function() {
      // we can leave it empty, it just needs to be defined
     this.md = 'haha'
    })
   </script>
####require
require的值是一个指令的名字,require会将该名字的指令的controller注入到当前调用该属性的指令中, 注入到link函数的第四个参数ctrl
前缀:
- ?
在当前指令中查找controller,没有,ctrl传值为null
在上游的指令链中寻找
- ?^
选择的加载需要的指令并在父指令链中查找
- 用法一
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;  
        ..  
        },  
    ...  
    }  
});  
- 用法二
问题:指令链?
####angular的生命周期
- 编译(compile)
遍历dom,编译(将指令中的模板插入等工作)
- 链接(link)
pre-link阶段 ,生成dom实例,重新遍历,不注入scope
post-link阶段,反向注入scope
####ngModelController
属性:
- $viewValue(更新视图所需的实际字符串)
- $modelValue(由数据模型持有)
- $parse(函数数组,ngModel中读取的值传入$parse中的函数,其中的函数依次解析)
- $formatters(对值进行格式化)
- $viewChangeListeners(在视图中的值变化时调用)
- $error(没有通过验证的错误信息)
- $dirty(是否进行过交互)
- $valid(是否有错误)
- $invalid (是否存在至少一个错误)
未完待续:
- ngModel
- ngModelController
- 自定义验证
- 自定义渲染
未完待续:
- $templateCache服务
- $getTrustedResourceUrl
- 作用域传递给指令
- $injector.invoke
- $compile服务
