当前位置: 移动技术网 > IT编程>开发语言>JavaScript > AngularJS 大雅之堂的指令

AngularJS 大雅之堂的指令

2017年12月21日  | 移动技术网IT编程  | 我要评论

学习要点:

嵌入包含 在指令中使用控制器 自定义表单元素

一、嵌入包含

嵌入包含是指将一个文档的一部分通过引用插入到另一个文档中

使用嵌入包含需要两个特定的步骤:

第一步:在创建指令时将transclude定义属性设置为true

第二步:将ng-transclude指令使用到模板中,就放入想插入被包装元素的地方

1.先看一个案例

<!DOCTYPE>
<!-- use module -->
<html ng-app="exampleApp">
<head>
    <title>Angluar test</title>
    <meta charset="utf-8"/>
    <link rel="stylesheet" type="text/css" href="css/bootstrap.min.css">
    <link rel="stylesheet" type="text/css" href="css/bootstrap-theme.min.css">
</head>
<body ng-controller="defaultCtrl">
    <!-- 将panel标签中的内容放入到下面模板的panel-body中 -->
    <panel>
        The data value comes from the: {{dataSource}}
    </panel>
 
 
<script type="text/javascript" src="js/angular.min.js"></script>
<script type="text/ng-template" id="template">
    <p class="panel panel-default">
        <p class=" panel-heading">
            <h4>This is the panel</h4>
        </p>
        <p class=" panel-body" ng-transclude>
        <!-- 在此处插入想插入的元素,即上面定义的<panel>标签-->
        </p>
    </p>
</script>
<script type="text/javascript">
 
angular.module("exampleApp", [])
    .directive("panel", function () {
        return {
            // link : 为指令指定连接函数
            link: function (scope, element, attrs) {
                scope.dataSource = "directive";
            },
            // restrict : 指定指令如何使用 ECMA 元素、类、注释和属性, 类和注释一般不用
            restrict : "E",
            // 为每个实例创建一个作用域----但这里是被嵌入包含中的表达式
            // 当为true时,scope.dataSource==controller
            // 当为false时,scope.dataSource==directive
            // 因为被嵌入包含的内容中的表达式是在控制器作用域被计算的,并非指令的作用域
            scope : true,
            // 指定指令模板
            template : function () {
                return angular.element(document.querySelector("#template")).html();
            },
            // transclude : 指定指令是否被用于包含任意内容
            transclude : true
        }
    })
    .controller("defaultCtrl", function ($scope) {
        $scope.dataSource = "controller";
    })
</script>
</body>
</html>

 

这里写图片描述

 

注意:在嵌入的内容中我们使用了内联的数据绑定

The data value comes from the: {{dataSource}}

这样做主要说明的是:被嵌入包含内容中的表达式是在控制器中被计算的,并非在指令的作用域。

这句话的意思就是:上述的dataSource的值直接受控制器的影响,而不是受指令作用域的影响。在本案例中,控制器的dataSource=controller,在指令中dataSource=directive,显然dataSource最终显示为controller

当然,如果你想让指令作用域也在考虑之内,那么可以将定义指令中的scope设置为false

 

这里写图片描述

 

2.使用编译函数

什么时候使用编译函数?

当指令特别复杂或者需要处理大量数据的时候,我们可以使用编译函数操作DOM,而让链接函数执行其他任务。这样不仅提高性能,而且可以使用嵌入包含来重复生成内容的能力
 

<!DOCTYPE>
<!-- use module -->
<html ng-app="exampleApp">
<head>
    <title>Angluar test</title>
    <meta charset="utf-8"/>
    <link rel="stylesheet" type="text/css" href="css/bootstrap.min.css">
    <link rel="stylesheet" type="text/css" href="css/bootstrap-theme.min.css">
</head>
<body ng-controller="defaultCtrl" class="panel panel-body">
    <table class="table table-striped">
        <thead><tr><th>Name</th><th>Price</th></tr></thead>
        <tbody>
            <tr simple-repeater source="products" item-name="item">
                <td>{{item.name}}</td>
                <td>{{item.price | currency}}</td>
            </tr>
        </tbody>
    </table>
    <!-- 单击按钮,添加商品,提高价格 -->
    <buton class="btn btn-default text" ng-click="changeData()">Change</buton>
<script type="text/javascript" src="js/angular.min.js"></script>
<script type="text/javascript">
 
angular.module("exampleApp", [])
    .directive("simpleRepeater", function () {
        return {
            // 隔离作用域,且进行数据绑定
            scope : {
                // 双向数据绑定
                data : "=source",
                // 单向数据绑定
                propName : "@itemName"
            },
            // element : 表示元素本身被包括到嵌入内容中,并非是其内容
            transclude : 'element',
            // 编译函数
            compile : function (element, attrs, transcludeFn) {
                return function ($scope, $element, $attr) {
                    // 添加监听器,当产品的数量发生改变,就会触发后面的工厂函数
                    $scope.$watch("data.length", function () {
                        // 移除子元素
                        var parent = element.parent();
                        parent.children().remove();
                        for (var i = 0; i < $scope.data.length; i++) {
                            // 创建一个新的作用域
                            var childScope = $scope.$new();
                            // 赋予每个实例item属性
                            childScope[$scope.propName] = $scope.data[i];
                            // 进行克隆数据
                            transcludeFn(childScope, function (clone) {
                                parent.append(clone);
                            })
                        }
                    })
                }
            }
        }
    })
    .controller("defaultCtrl", function ($scope) {
        // 数据模型
        $scope.products = [
            { name: "Apples", price: 1.20 },
            { name: "Bananas", price: 2.42 }, 
            { name: "Pears", price: 2.02 }
        ];
        // 添加商品,递增价格
        $scope.changeData = function () {
            $scope.products.push({ name : "Peas", price : 4.02});
            for ( var i = 0; i < $scope.products.length; i++) {
                $scope.products[i].price++;
            }
        }
    })
</script>
</body>
</html>

 

这里写图片描述

 

二、在指令中使用控制器

指令中可以创建被其他指令所用的控制器

 

这里写图片描述

 

三、创建自定义表单元素
 

<!DOCTYPE>
<!-- use module -->
<html ng-app="exampleApp">
<head>
    <title>Angluar test</title>
    <meta charset="utf-8"/>
    <link rel="stylesheet" type="text/css" href="css/bootstrap.min.css">
    <link rel="stylesheet" type="text/css" href="css/bootstrap-theme.min.css">
</head>
<body ng-controller="defaultCtrl">
    <p class="panel panel-default">
        <!-- 嵌入内容ng-transclude -->
        <!-- 双向数据绑定 product-table="totalValue"(绑定产品) product-data="products"(绑定产品质量和)  -->
        <table class="table table-striped" product-table="totalValue" product-data="products" ng-transclude>
            <tr><th>Name</th><th>Quantity</th></tr>
            <tr ng-repeat="item in products" product-item></tr>
            <tr><th>Total:</th><td>{{totalValue}}</td></tr>
            <!-- 嵌入内容ng-transclude -->
            <!-- 双向数据绑定 product-data="products"(绑定产品) property-name="quantity"  -->
            <tr reset-totals product-data="products" property-name="quantity"></tr>
        </table>
    </p>
 
<script type="text/javascript" src="js/angular.min.js"></script>
<!-- 定义模板 -->
<script type="text/ng-template" id="productTpl">
    <td>{{item.name}}</td>
    <td><input ng-model='item.quantity' /></td>
</script>
<script type="text/ng-template" id="resetTpl">
    <td colspan="2"><button ng-click="reset()">Reset</button></td>
</script>
<script type="text/javascript">
 
angular.module("exampleApp", [])
    .directive("productItem", function () {
        return {
            template : document.querySelector("#productTpl").outerText,
            // 声明对某个控制器的依赖,这里是productTable指令中的控制器
            // require属性值的前缀
            // None 表示假定两个指令都应用于同一元素
            // ^    表示在指令所应用到的元素的父元素上查找另一个指令
            // ?    表示如果找不到指令就不报错
            require : "^productTable",
            link : function (scope, element, attrs, ctrl) {
                scope.$watch("item.quantity", function () {
                    // 使用productTable 指令中的控制器
                    ctrl.updateTotal();
                });
            }
        }
    })
    .directive("productTable", function () {
        return {
            transclude : true,
            scope: {
                // 这里的value表示计算的产品数量和
                value : "=productTable",
                // 这里的data相当于控制器中的products
                data : "=productData"
            },
            // 创建控制器,可以被另外的两个指令使用
            // 功能:累计求产品数量和
            controller : function ($scope, $element, $attrs) {
                this.updateTotal = function () {
                    var total = 0;
                    for (var i = 0; i < $scope.data.length; i++ ) {
                        total += Number($scope.data[i].quantity);
                    }
                    $scope.value = total;
                }
            }
        }
    })
    .directive("resetTotals", function () {
        return {
            scope : {
                //  这里的data相当于控制器中的products
                data : "=productData",
                //  这里的propname,相当于products.quantity
                propname : "@propertyName"
            },
            template : document.querySelector("#resetTpl").outerText,
            require : "^productTable",
            link : function (scope, element, attrs, ctrl) {
                scope.reset = function () {
                    for (var i = 0; i < scope.data.length; i++) {
                        scope.data[i][scope.propname] = 0;
                    }
                    // 使用productTable 指令中的控制器
                    ctrl.updateTotal();
                }
            }
        }
    })
    .controller("defaultCtrl", function ($scope) {
        $scope.products = [
            { name: "Apples", price: 1.20, quantity: 2 },
            { name: "Bananas", price: 2.42, quantity: 3 },
            { name: "Pears", price: 2.02, quantity: 1 }
        ];
    })
</script>
</body>
</html>

 

这里写图片描述

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网