欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

AngularJS 大雅之堂的指令

程序员文章站 2022-06-22 13:18:06
学习要点: 嵌入包含 在指令中使用控制器 自定义表单元素 一、嵌入包含 嵌入包含是指将一个文档的一部分通过引用插入到另一个文档中 使用嵌入包含需要两个特定的步骤: 第一步...

学习要点:

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

一、嵌入包含

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

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

第一步:在创建指令时将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>

 

AngularJS 大雅之堂的指令

 

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

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

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

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

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

 

AngularJS 大雅之堂的指令

 

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>

 

AngularJS 大雅之堂的指令

 

二、在指令中使用控制器

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

 

AngularJS 大雅之堂的指令

 

三、创建自定义表单元素
 

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

 

AngularJS 大雅之堂的指令