愚墨的博客
  • 首页
  • 前端技术
  • 面试
只争朝夕不负韶华
  1. 首页
  2. 前端框架
  3. AngularJS
  4. 正文

自定义指令

2016年07月26日 2684点热度 0人点赞 0条评论

我发现懒惰是促使科技进步的最大动力,因为懒得走,人类发明了汽车飞机等各种东西,还有就是angular,因为人们懒得写那么多的代码,所以angular才能这么流行。

angular的指令着实强大,内置指令可以为我们解决很多问题,但是有一些需求还是需要我们通过创建自定义指令来实现的,毕竟每个人,每个项目的需求都不同,自定义指令允许你通过自己实现元素的行为来拓展html功能,如果你有需要操作DOM的代码,就应该利用自定义指令做到这一点。

1、指令的实现

可以通过在一个Module对象上调用directive()方法实现自定义指令,directive()方法接收一个指令名称最为第一个参数,并接收一个提供函数来返回一个对象,这个对象包含一些必要的指令来构建指令对象,语法:

var app = angular.module('appModule',[]);
app.directive('myDire',function () {
    return {

    }
});

返回的对象中的指令包含以下几种。

1.2 template 模板

允许你定义插入指令的元素的angularJs模板文本,可以在自定义模板中指定一个根元素,但只能有一个元素,作为所有子元素的根元素,另外如果使用了transclude标识,这个元素中应该包含ng-transclude。

var app = angular.module('appModule',[]);
app.directive('myDire',function () {
    return {
       template:'<div>Hello</div>'//最外层的div是根元素,有且只有一个   
    }
});

也可以通过使用templateUrl属性来指定谓语服务器上的angular模板的url

var app = angular.module('appModule',[]);
app.directive('myDire',function () {
    return {
       templateUrl:'/mytemplate.html'   
    }
});

1.3 transclude

保留指令中的内容,并且如果元素指令中没有内容,transclude设置为true的时候,则该指令的内部组件可以访问指令以外的作用域,还必须在指令模板内的元素中保留ng-transclude指令。

<my-dire>world</my-dire>

return {
    transclude:true,
    template:'<div>Hello <span ng-transclude></span></div>'
}

1.4 restrict

指定该指令是应用于什么范围,是一个html元素还是一个属性还是一个类名等,

restrict可以设置为以下几种  :

指令的分类,1.装饰型指令(类名、属性) 2.组件式指令(元素名、注释)
  • A:应用为一个属性名
  • E:应用为一个元素名
  • C:应用为一个类名
  • M:应用为注释

这几种也可以混合使用,比如AE、EC等。M 比较少用,改变一个注释没啥意思。

var app = angular.module('appModule',[]);
app.directive('myDire',function () {
    return {
       restrict:'EA'
       templateUrl:'/mytemplate.html'   
    }
});

1.5 link函数

指定可以访问该作用域、DOM元素和其他操作元素的链接函数。

link函数接受作用域、元素和与指令相关的属性等参数。可以让你直接在指令中操作DOM元素,语法:

link : function(scope,element,attributes,[controller])

controller 是由require选项指定的控制器。

return {
       transclude:true,
       templateUrl:'panel.html',
       link: function (scope,element,attrs) {
                       
       }
}
app.directive('myDire',function(){
    return{
        template:'<div>{{title}}<span ng-transclude></span></div>',
        transclude:true,
        link:function(scope,ele,attr){
            scope.title = 'Manster'
        }
    }
})

1.6 独立作用域

可以为当前指令设置独立作用域

return {
  scope:true,//或者scope:{}
}

1.7 动态引用数据

有时候我们希望吧外部作用域的一些项目任然映射到指令的作用域内。可以使用这些前缀属性名,是局部作用域内的变量在该指令的作用域内可用。

1.先将控制器上的属性进行传递
1. 先将title值进行取出 {{title}} 取出来的值 title = "scope上的值"
2.在指令中引用scope上的值,
1. 先声明一个title属性,用@符号去引用当前指令上的属性

1.7.1 "@"引用字符串

指令和作用域间的交互 声明控制器

app.controller('appCtrl',function (scope) {scope.title = [  //控制器上挂载数据
        {name:'第一个面板'},
        {name:'第二个面板'}
    ]
});

挂载scope上的属性

<my-dire title="{{title[0].name}}">这是一个面板,helloAngular</my-dire>
<my-dire title="{{title[1].name}}">这是二个面板,helloAngular</my-dire>

在当前作用域下声明title属性,引用对应的值

return {
    transclude:true,
    template:'<div>{{title}}</div>',
    scope:{
        title:'@'//如果前缀后面没有加属性名默认名字相同
    }
}

1.7.2 "="引用scope上的属性

= 在局部作用域和指令作用域属性之间创建一个双向的绑定。

注意一点的是在controller作用域中,

app.controller('ctrl', function (scope) {scope.title = 'this is title';
    $scope.name = 'Manster';
});
<!-- {{title}}取得的是title对应的值,是字符串   直接写name 是引用的scope的属性-->
<div ng-controller="ctrl">
    <input type="text" ng-model="name">
    <man title="{{title}}" name="name"></man>
</div>

例子:

<!DOCTYPE html>
<html ng-app="appModule">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div ng-controller="ctrl">
    <input type="text" ng-model="name">
     <!--分别得到的是对应的字符串和属性(地址)--->
    <man title="{{title}}" name="name"></man>
</div>
<script src="angular.js"></script>
<script>
    var app = angular.module('appModule',[]);
    app.controller('ctrl', function (scope) {scope.title = 'this is title';
        $scope.name = 'Manster';
    });
    app.directive('man',function(){
        return{
            restrict:'E',
            template:'<div>{{title}}<input type="text" ng-model="name"> </div>',
            scope:{
                title:'@',//@引用字符串
                name:'=' //=引用的是属性,操作的是地址,所以可以实现双向绑定
            }
        }
    })
</script>
</body>
</html>

1.7.4 &绑定函数

<!DOCTYPE html>
<html lang="en" ng-app="appModule">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body ng-controller="ctrl">
<my add="add(person,personage)" name="{{name}}" age="age"></my>
<script src="angular.js"></script>
<script>
    var app = angular.module('appModule',[]);
    app.controller('ctrl', function (scope) {scope.add = function (who,age) {
            alert(who+age)
        };
        scope.name = 'manster';scope.age =22
    });
    app.directive('my', function () {
        return {
            scope:{
                add:'&',
                name:'@',
                myage:'=age'
            },
            //当前ng-click引用的是控制器上的方法
            //传递参数时,要通过对象传递 键代表的是名字 ---> person:name,personage:myage
            template:'{{name}}{{myage}}<div ng-click="add({person:name,personage:myage})">clickMe</div>'
        }
    })
</script>
</body>
</html>

1.7 replace替换指令

增加replace:true替换原有指令,

<div ng-controller="ctrl" ng-cloak>
    <man></man>
</div>
</body>
<script type="text/javascript" src="angular.js"></script>
<script>
    var app = angular.module('appModule',[]);
    app.controller('ctrl',['scope',function(scope){
        scope.name = 'Manster',scope.age=22;
        $scope.arr = ['name','数组']
    }]);
    app.directive('man',function(){
        return{
            replace:true,
            template:'<div>替换</div>',
        }
    })
</script>

74408443

1.9 compile函数

在link函数前执行,编译模板,返回的函数为link函数,如果同时设置了compile 函数和 link函数的话,link函数会失效

<!DOCTYPE html>
<html lang="en" ng-app="appModule">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<my time="5"></my>
<script src="angular.js"></script>
<script>
    var app = angular.module('appModule', []);
    app.directive('my', function () {
        return {
            //compile用来编译模板的,在link函数之前执行,返回link函数
            template: '<div>Hello manster</div>',
            compile: function (elements, attrs) {//参数可变
                console.log(elements)
                console.log(attrs)
                //elements代表当前指令元素
                //attrs Attribute
                //获取编译次数
                var times = attrs['time'];
                //操作指令模板,先获取默认的模板<div>Hello zfpx</div>
                var tmpl = elements.children();
                //根据次数生成要追加的模板
                //因为以前已经有一个模板了,只需要追加time-1个
                for (var i = 0; i < times - 1; i++) {
                    //append具有移动性,所以我们要克隆出来
                    elements.append(tmpl.clone());
                }
                return function () {
                    console.log('link');
                }
            },
            //当我们写了compile函数后返回link函数,就不用自己再写link函数
            link: function () {
                console.log('link');
            }
        }
    })

</script>
</body>
</html>

2.指令和指令间的交互

指令之间的交互主要依赖两个参数:controller 和  require 。

<!DOCTYPE html>
<html lang="en" ng-app="appModule">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<girl lovemoney lovecry></girl>
<script src="angular.js"></script>
<script>
    //指令的分类,1.装饰型指令 2.组件式指令
    var app = angular.module('appModule',[]);
    app.directive('girl', function () {
        return {
            restrict:'E',
            template:'<div ng-click="show()">Angular MM</div>',
            //共享方法  当前指令的控制器
            controller: function (scope) {//这个和我们angular中的控制器有些不同,这个主要是写一些指令的对外方法。
                //用来存放特征的scope.arr = []; //在当前作用域下声明一个数组
                this.add = function (props) {
                    $scope.arr.push(props);
                }
            },
            link: function (scope,element,attrs) {
                //在点击调用show方法后弹出scope.arr
                scope.show = function () {
                    alert(scope.arr)
                }
            }
        }
    });
    app.directive('lovecry', function () {
        return {
            restrict:'A',
            require:'^girl',
            link: function (scope,element,attrs,girlCtrl){//link方法中的第四个参数,叫做父控制器,只要是指令写了require参数,就可以使用这个参数了,它可以访问父级contorller的方法中提供的一些属性和方法。
                girlCtrl.add('lovecry')
            }
        }
    })
    app.directive('lovemoney', function () {
        //当前的lovemoney指令是依赖于girl的
        return {
            restrict:'A',
            //因为当前的lovemoney和lovecry就在当前的指令上
            require:'^girl',
            /*require:'girl',//直接写girl表示在当前指令下查找(平级)
             require:'^girl'//直接写girl表示在当前指令下查找(平级+上一级)
             require:'?^girl',//找不到的话不报错
             require:'?girl',//找不到的话不报错*/
            //当我们依赖了girl指令就会默认把girl的控制的实例注入进来
            link: function (scope,ememnts,attsr,girlCtrl) {
                //girlCtrl是girl的controller的实例,在girl控制器上定义的this上的方法或者属性,都可以通过girlCtrl调用到
                girlCtrl.add('lovemoney');
            }

        }
    })
</script>
</body>
</html>

随后运用指令的交互写一个小DEMO:分组风箱

<!DOCTYPE html>
<html ng-app="appModule">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .title {
            height: 50px;;
            width: 300px;
            background: #1b6d85;
            line-height: 50px;
            text-align: center;
            outline: 1px salmon solid;
            font-size: 20px;
            color: white;
            font-weight: bold;
        }
        .content {
            height: 100px;
            width: 300px;
            border: 1px solid seagreen;
            box-shadow: inset 2px 2px 2px #ccc;
            background: -webkit-gradient(#761c19 #5bc0de);
            text-align: center;
        }
    </style>
</head>
<body>
<div ng-controller="ctrl">
    <group>
        <open title="第一个">这里是开关一的内容</open>
        <open title="第二个">这里是开关二的内容</open>
    </group>
    <br><br>
    <group>
        <open title="第一个">这里是开关一的内容</open>
        <open title="第二个">这里是开关二的内容</open>
        <open title="第三个">这里是开关三的内容</open>
    </group>
</div>
<script src="angular.js"></script>
<script>
    var app = angular.module('appModule', []);
    app.controller('ctrl', function (scope) {
    });
    app.directive('group',function(){
        return{
            restrict:'E',
            controller:function(scope){
                var arr = [];
                //所有的open都归group管理,只需当前的切换其他的全部隐藏
                this.add = function(scope){
                    arr.push(scope)
                };
                this.show = function(cur){
                    arr.forEach(function(item){
                        if(item!=cur){
                            item.flag = false;
                        }
                    })
                }
            }
        }
    });
    app.directive('open', function () {
        return {
            require:'^group',
            restrict: 'E',
            transclude: true,
            template: '<div class="title" ng-click="show()">{{title}}</div><div class="content" ng-transclude ng-show="flag"></div>',
            scope: {
                title: '@'
            },
            link: function (scope, e, a,groupCtrl) {
                scope.flag = false;
                scope.show = function () {
                    scope.flag = !scope.flag;
                    groupCtrl.show(scope)//
                }
                //只要有open指令就会执行
                groupCtrl.add(scope)
            }
        }
    })

</script>
</body>
</html>

再来一个我觉得很方便的指令拖拽

<!DOCTYPE html>
<html ng-app="appModule">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        div {
            height: 100px;
            width: 100px;
            background: #124599;
            position: absolute;
            top: 0;
            left: 0;
        }
    </style>
</head>
<body>
<div mydrag>
</div>
<script src="angular.js"></script>
<script>
    var app = angular.module('appModule', []);
    //自定义指令
    app.directive('mydrag',function(){
        return{
            restrict:'A',
            link:function(scope,element,attrs){
                element.on('mousedown',function(e){
                    //在这里求出的是鼠标距离盒子的距离
                    var disx = e.pageX - element[0].offsetLeft;
                    var disy = e.pageY - element[0].offsetTop;
                    //先包装成jq对象
                    angular.element(document).on('mousemove',function(e){
                        var l = e.pageX - disx;
                        var t = e.pageY - disy;
                        var maxT = document.documentElement.clientHeight-element[0].clientHeight;
                        var maxL = document.documentElement.clientWidth-element[0].clientWidth;
                        l=l<0?0:(l>maxL?maxL:l);
                        t=t<0?0:(t>maxT?maxT:t);
                        element.css('top',t+'px');
                        element.css('left',l+'px');
                    });
                    angular.element(document).on('mouseup',function(e){
                        angular.element(document).off()
                    })
                    e.preventDefault();
                })
            }
        }
    })
</script>
</body>
</html>

 

标签: 暂无
最后更新:2016年07月26日

愚墨

保持饥渴的专注,追求最佳的品质

点赞
< 上一篇
下一篇 >

文章评论

取消回复

搜搜看看
历史遗迹
  • 2023年5月
  • 2022年9月
  • 2022年3月
  • 2022年2月
  • 2021年12月
  • 2021年8月
  • 2021年7月
  • 2021年5月
  • 2021年4月
  • 2021年2月
  • 2021年1月
  • 2020年12月
  • 2020年11月
  • 2020年9月
  • 2020年7月
  • 2020年5月
  • 2020年4月
  • 2020年3月
  • 2020年1月
  • 2019年5月
  • 2019年3月
  • 2019年2月
  • 2019年1月
  • 2018年9月
  • 2018年3月
  • 2018年2月
  • 2018年1月
  • 2017年11月
  • 2017年7月
  • 2017年6月
  • 2017年3月
  • 2017年2月
  • 2017年1月
  • 2016年12月
  • 2016年11月
  • 2016年9月
  • 2016年8月
  • 2016年7月
  • 2016年6月
  • 2016年5月
  • 2016年4月
  • 2016年3月
  • 2016年2月
  • 2016年1月
  • 2015年12月
  • 2015年10月
  • 2015年9月
  • 2015年7月
  • 2015年6月
  • 2015年4月

COPYRIGHT © 2020 愚墨的博客. ALL RIGHTS RESERVED.

THEME KRATOS MADE BY VTROIS