Angular学习随记

###指令篇


####什么是指令?

在特定DOM元素上运行的函数,指令可以扩展元素功能,编写属于自己的html标签


####如何定义指令?

angular.module("name",[]).directive("dname",function(){
	return {}
});

两个参数


####restrict 告诉angular该指令在DOM中的声明方式,默认是A

<my-direct> </my-direct>
<div my-direct="express"> </div>
<div class="my-direct:express"> </div>
<--directive:my-directive express-->

建议:使用属性声明,便于浏览器兼容


####priority(优先级)

默认:0

ngRepeat:1000(内置指令里优先级最高的)

同一个元素上有两个优先级相同的指令,声明在前面的先调用


####terminal

这个参数用来告诉Angular停止运行当前元素上比本指令优先级低的指令

当摸个元素上的指令被设置了该参数,就不要用优先级比他低的指令了,因为不会执行

ng-if优先级>ngView

当ng-if为true,ngView会执行,当ng-if为false,ngView就不会执行


####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 两种形式:

无论哪种方式,模板的URL都将通过AngularJs内置的安全层,$getTrustedResourceUrl,可以保护模板不会被不信任的源加载

默认情况下,调用指令时会在后台通过Ajax来请求Html模板文件

模板加载是异步的,意味着编译和链接要暂停,等待模板加载完成

模板加载后,angular会将它默认缓存到$templateCache服务中。在实际生产中,可以将模板缓存到一个定义模板的js文件中

就不需要通过XHR来加载模板了,未完待续*(后面讲)


####replace 如果设置这个参数,就将值设为true(替换),

设置为false与不使用该参数一样(魔板被当做子元素插入调用指令元素内部)


####scope参数

默认值:false

对比指令中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"
}

注意:上面的代码是有问题的

<div my-directive some-attr="123"></div>

Scope:{
	haha:'@someAttr'
},
template:'<h3></h3>',
controller: function($scope){
$scope.haha//就可以获取到了
}

####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>

问题:


####controller

angular.module('myApp',[]).directive('myD',function(){
return{

controller:'SomeControll'
}
})

angular.module('myApp',[]).controller('SomeControll',function($scope,$element,$attrs,$transclude){

})

与指令元素相关的当前作用域

当前指令对应的元素

由当前属性组成的对象

<div id="s" class="e"></div>

{
id:"s",
class:"e"
}

如果你在指令定义中设置 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.指令中标签里的元素都会先删除然后被嵌入包含后的内容所替换。

但是局限出现了

例如:

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>

于是两种解决方法

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操作了。

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


####匿名控制器

两种形式:

.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的生命周期

遍历dom,编译(将指令中的模板插入等工作)

pre-link阶段 ,生成dom实例,重新遍历,不注入scope

post-link阶段,反向注入scope

具体细节


####ngModelController

属性:


未完待续:


未完待续: