angular自定义指令获取ng-transclude内容并且动态添加编译

洪飞驰
2023-12-01

  阅读本博客的同学先看一下本博客的主要内容点,如果咩有涉及到同学们需要的知识点,请绕道,有错误请及时指出,以免祸害其他同学:


  主要内容点:

1、如果获取ng-transclude的内容;

2、获取了ng-transclude内容之后如果动态添加内容,并且编译然后加载到dom中;

3、如果在子作用域(指令)中操作父作用域(调用指令的地方所在的controller上下文)的变量(新增,修改等);

4、如果在父作用域中修改子作用域的变量;


  讲一下该指令的意义所在,我们平时写一些列表指令的时候,往往都会有显示加载状态,或者点击加载更多的功能,或者根据某个条件筛选、排序、或者分页查询、根据给出的地址请求数据等等……这些代码有的同学可能已经写了千千万万遍,而现在就是摆脱这种问题的时机。写一个自定义指令,根据给出的url以及参数自动请求数据、监听参数变化重新请求数据(筛选,排序),自动处理显示加载状态……所有的重复劳动都是自动完成,但是有一点不一样的是,列表的内容ui布局是不一样的,然后请求的数据结果也是不一样的,如何才能使得实现的自定义列表适应这两个问题呢?

  先看一下调用代码:

<my-list list="dataList" item="it" var="devLnkList">
    <div ng-click="removeItem(it,$index)">
        <span>123456</span>
        ------<span ng-bind="it.value"></span>-------
        <span>3333333</span>
    </div>
</my-list>

对,这个自定义的列表调用的方式跟ng-repeat非常像,接下来看一下实现原理;

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../../lib/angular/angular.min.js"></script>
</head>
<body ng-app="MyApp">

<div ng-controller="MyCtrl">
    <my-list list="dataList" item="it" var="devLnkList">
        <div ng-click="removeItem(it,$index)">
            <span>123456</span>
            ------<span ng-bind="it.value"></span>-------
            <span>3333333</span>
        </div>
    </my-list>
    {{dataList}}
    <button ng-click="addItem()">add</button>
    <button ng-click="loadMore()">loadMore</button>
</div>
<script>
    var myApp = angular.module('MyApp', []);
    myApp.controller('MyCtrl', function ($scope) {
        $scope.addItem = function () {
            console.log($scope);
            $scope.dataList.push({
                name:'add',
                value:(Math.random()*100).toFixed(0)
            });
        };

        $scope.dataList = [
            {name:1,value:2},
            {name:3,value:"a"},
            {name:4,value:"v"},
            {name:5,value:"f"},
        ];
        $scope.removeItem = function (param,index) {
            console.log(param,index);
            $scope.dataList.splice(index,1);
        };
        $scope.loadMore = function () {
            $scope.devLnkList.loadNextPage();
        }
    });
    myApp.directive('myList', function ($compile, $timeout) {
            return {
                restrict: 'E',
                replace: true,
                template: '<ng-transclude></ng-transclude>',
                scope: false,
                transclude: true,
                controller: [
                    '$scope', '$element', '$attrs', '$transclude',
                    function ($scope, $element, $attrs, $transclude) {
                        if (!$attrs.var) {
                            console.error("my-list need necessary attribute : var");
                            return;
                        }
                        if (!$attrs.list) {
                            console.error("my-list need necessary attribute : list");
                            return;
                        }

                        /*模拟数据请求过程,通过给予的url,自动请求数据,放到父作用域中*/
                        if (!$scope[$attrs.list]) $scope[$attrs.list] = [
                            {name: 111, value: 'aaa'},
                            {name: 2223, value: "bbb"},
                            {name: 33334, value: "ccc"},
                            {name: 4445, value: "ddd"},
                        ];

                        /*暴露一个代理对象给父作用域,父作用域可以通过调用该对象中的方法对lnkList进行操作,比如手动刷新数据等等*/
                        $scope[$attrs.var] = {
                            isLoading: false,
                            loadNextPage: function () {
                                $scope[$attrs.var].isLoading = true;
                                $timeout(function () {
                                    console.log($scope);
                                    if ($scope[$attrs.list]) {
                                        $scope[$attrs.list].push({
                                            name: 'add in directive',
                                            value: (Math.random() * 100).toFixed(0),
                                        });
                                    }
                                }, 1000 * 1).then(function () {
                                    $scope[$attrs.var].isLoading = false;
                                });
                            }
                        };
                    }
                ],
                link: function (scope, element, attrs, ctrl, transclude) {
                    transclude(scope, function (clone) {
                        /*获取调用指令的地方的html内容,由于拿到的是一个dom对象数组,所以需要遍历数组,把dom对象的outerHtml拼接,由于换行等字符会被解析为名为textdom对象(与divspandom对象平级),而textdom对象没有outerHtml属性,只有wholeText属性,所以拼接的时候使用的是wholeText的值*/
                        var html = "<div ng-repeat='" + attrs.item + " in " + attrs.list + " track by $index'>";
                        for (var i = 0; i < clone.length; i++) {
                            if (clone[i].outerHTML) html += clone[i].outerHTML;
                            else if (clone[i].wholeText) html += clone[i].wholeText;
                        }
                        html += "</div>";
                        html += '<div ng-show="!' + attrs.var + '.isLoading" ripple ng-click="' + attrs.var + '.loadNextPage()"><div style="color: #469ce7;">点击加载更多</div></div>';
                        html += '<div ng-show="' + attrs.var + '.isLoading" ripple><div style="color: #469ce7">正在加载中...<ons-icon icon="ion-load-a" style="animation: rotating 1.2s linear infinite;color: #469ce7"></ons-icon></div></div>';
                        /*拼接完成,开始编辑html内容,并且加载到当前指令中*/
                        element.html(html);
                        $compile(element.contents())(scope);
                    });
                },
            }
        }
    );

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

  在指令中,link函数中的代码是本次的主要代码,它获取了未编译的ng-transclude的html内容,就是在调用的时候,<my-list>节点下的自定义的内容,然后使用ng-repeat去显示,后面还自动加上了显示加载中的文字,本来是使用了onsenui,为了能够使得代码能够直接使用,就去掉了这个框架,导致界面看起来有点难看,同学们就将就一下。


  指令controller的主要工作是请求数据,不过这个我没有写好,因为这个请求数据的过程因使用场景而异,我这里只是模拟了一个请求数据的过程,加载数据也是,最后还暴露了一个对象保存在父作用域的scope中,父作用域可以调用这个对象的方法调用指令中的方法。比如代码中的loadNextPage函数,同学们需要其他的指令动作可以自己扩展这个对象的函数;

  最后一点比较重要的,父作用域的获取,我这个指令的scope的值是false,表示直接使用父作用域,所以是$scope[$attrs.var],如果同学们发现$scope中的变量不是父作用域中的变量,可以试试$scope.$parent,再不行可以试试$scope.$parent. $parent。



 类似资料: