hjzheng / CUF_meeting_knowledge_share

Record CUF team meeting knowledge share
121 stars 49 forks source link

2015-5-20 YouTube精彩Angular视频 Reusable Components in Angular #32

Open hjzheng opened 9 years ago

hjzheng commented 9 years ago

Reusable Components in Angular

视频: https://www.youtube.com/watch?v=dF_ObGgzGE8 Slides: https://docs.google.com/presentation/d/1ozBlL1MXxyYPSLzIaMtHO1YF-tHkEzKFsvt4s3rBBt0/pub#slide=id.g57f904f61_80

Multi-transclude Example

看这个例子前需要注意几个点

Fat Arrow(箭头函数)有几个使用注意点。 函数体内的this对象,绑定定义时所在的对象,而不是使用时所在的对象。 不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。 不可以使用arguments对象,该对象在函数体内不存在。

  • 自定义transclude函数
  • transclude function
  • Two parameters:
  • scope (optional)
  • callback function
  • passes clone
  • transclude函数执行时机 (注意指令的生命周期,你可以参看Slides P99
  • link
  • controller
  • compile
  • 它们之间的对比

HTML

   <ot-site>
        <div transclude-to="site-header">
            I am transcluding the header
        </div>
        <div transclude-to="site-menu">
            I am transcluding the menu
        </div>
        <div transclude-to="site-body">
            I am transcluding the body
        </div>
   </ot-site>

JavaScript

    // Use ES6 new feature
    // ng-transclude can not implement the result that we want. 
    /*$transclude(function(clone){
        $element.empty();
        $element.append(clone);
    });*/
    /**
    * test Module
    *
    * Description
    */
    /*angular.module('test', []).directive('otSite', () => {
        // Runs during compile
        return {
            scope: {},
            restrict: 'E', // E = Element, A = Attribute, C = Class, M = Comment
            template: `
                <div class="row">
                    <header class="header" ng-transclude></header>
                    <nav class="menu" ng-transclude></nav>
                    <main class="body" ng-transclude></main>
                    <footer class="footer"></footer>
                </div>
            `,
            replace: true,
            transclude: true,
            link: function($scope, iElm, iAttrs, controller) {

            }
        };
    });*/

    //Custom transclude
    //1.Get target ID
    //2.Find target element by ID
    //3.Append clone element to target 

   /* 
    transclude function
       Two parameters:
       scope (optional)
          callback function
               passes clone

    */

    /*
    Transclude function access point
    compile
    controller 
    link
    */

    angular.module('test', []).factory('MultiTransclude', () => {
        return {
            transclude: (iElm, transcludeFn) => {
                transcludeFn((clone) => {
                    angular.forEach(clone, (cloneEl) => {
                        if(cloneEl.attributes) {
                            var tId = cloneEl.attributes["transclude-to"].value;
                            var target = iElm.find(`[transclude-id="${tId}"]`);

                            if(target.length) {
                                target.append(cloneEl);
                            } else {
                                cloneEl.remove();
                                throw new Error(`
                                    Target not found, Please specify 
                                           the correct transclude-to Attribute
                                `);
                            }
                        }
                    });
                });
            }
        };
    })
    .directive('otSite', ['MultiTransclude', (MultiTransclude) => {
        // Runs during compile
        return {
            scope: {},
            restrict: 'E', // E = Element, A = Attribute, C = Class, M = Comment
            template: `
                <div class="row">
                    <header class="header" transclude-id="site-header"></header>
                    <nav class="menu" transclude-id="site-menu"></nav>
                    <main class="body" transclude-id="site-body"></main>
                    <footer class="footer"></footer>
                </div>
            `,
            transclude: true,
            link: function($scope, iElm, iAttrs, controller, transclude) {
               MultiTransclude.transclude(iElm, transclude);
            }
        };
    }]);
hjzheng commented 9 years ago

Attribute passing

知识点: scope设置

如何对隔离的scope传参数

HTML

<body ng-app="test" class="container-fluid" ng-controller="AppController">
    <ot-site>
        <ot-list items="area.list" selected="area.current" transclude-to="site-header">
        </ot-list>
   </ot-site>
   <ot-site>
        <ot-list items="area.list" selected="area.current" transclude-to="site-menu">
        </ot-list>
   </ot-site>
    <ot-site>
        <ot-list items="app.list" selected="app.current" transclude-to="site-body">
        </ot-list>
   </ot-site>
</body>

JavaScript

   angular.module('test', []).factory('MultiTransclude', () => {
        return {
            transclude: (iElm, transcludeFn) => {
                transcludeFn((clone) => {
                    angular.forEach(clone, (cloneEl) => {
                        if(cloneEl.attributes) {
                            var tId = cloneEl.attributes["transclude-to"].value;
                            var target = iElm.find(`[transclude-id="${tId}"]`);

                            if(target.length) {
                                target.append(cloneEl);
                            } else {
                                cloneEl.remove();
                                throw new Error(`
                                    Target not found, Please 
                                               specify the correct transclude-to Attribute
                                `);
                            }
                        }
                    });
                });
            }
        };
    })
    .directive('otSite', ['MultiTransclude', (MultiTransclude) => {
        // Runs during compile
        return {
            scope: {},
            restrict: 'E', // E = Element, A = Attribute, C = Class, M = Comment
            template: `
                <div class="container">
                    <header class="row header" transclude-id="site-header"></header>
                    <nav class="row menu" transclude-id="site-menu"></nav>
                    <main class="row body" transclude-id="site-body"></main>
                    <footer class="row footer"></footer>
                </div>
            `,
            transclude: true,
            link: function($scope, iElm, iAttrs, controller, transclude) {
               MultiTransclude.transclude(iElm, transclude);
            }
        };
    }])
    .directive('otList', () => {
        // Runs during compile
        return {
            scope: {
                items: "=",
                selected: "="
            }, 
            restrict: 'E',
            template: `<ul class="nav nav-justified">
                        <li ng-repeat="item in items" 
                            ng-class="{'active': item === selected}"
                            ng-click="selectItem(item)">
                            <a href="#" ng-bind="item"></a>
                        </li>
                       </ul>`,
            replace: true,
            link: ($scope, iElm, iAttrs, controller) => {
                $scope.selectItem = function(item){
                    $scope.selected = item;
                }
            }
        };
    }).controller('AppController', ['$scope', ($scope) => {
        $scope.area = {
            list: [
                "Configs",
                "Services",
                "Controllers",
                "Directives"
            ],
            current: 'Configs'
        };

        $scope.app = {
            list: [
                "Configs",
                "Services",
                "Controllers",
                "Directives"
            ],
            current: 'Configs'
        };
    }]);
hjzheng commented 9 years ago

Sub-directives

知识: transclude设置为true会产生一个新的scope 关于transclude scope 和 隔离scope 的分析,抽时间在学习吧! html

<body ng-app="test" class="container-fluid" ng-controller="AppController">
    <ot-site>
        <ot-dropdown transclude-to="site-header">
            <ot-trigger>
                <button type="button" class="btn btn-link">{{app.current}}</button>
            </ot-trigger>
            <ot-target>
                <ot-list items="app.list" selected="app.current" theme="list">
            </ot-target>
        </ot-dropdown>
        <ot-list items="area.list" selected="area.current" 
                                                    theme="nav" transclude-to="site-menu">
        </ot-list>
   </ot-site>
</body>

JavaScript

   angular.module('test', []).factory('MultiTransclude', () => {
        return {
            transclude: (iElm, transcludeFn) => {
                transcludeFn((clone) => {
                    angular.forEach(clone, (cloneEl) => {
                        if(cloneEl.attributes) {
                            var tId = cloneEl.attributes["transclude-to"].value;
                            var target = iElm.find(`[transclude-id="${tId}"]`);

                            if(target.length) {
                                target.append(cloneEl);
                            } else {
                                cloneEl.remove();
                                throw new Error(`
                                    Target not found, 
                                             Please specify the correct transclude-to Attribute
                                `);
                            }
                        }
                    });
                });
            }
        };
    })
    .directive('otSite', ['MultiTransclude', (MultiTransclude) => {
        // Runs during compile
        return {
            scope: {},
            restrict: 'E', // E = Element, A = Attribute, C = Class, M = Comment
            template: `
                <div class="container">
                    <header class="row header" transclude-id="site-header"></header>
                    <nav class="row menu" transclude-id="site-menu"></nav>
                    <main class="row body" transclude-id="site-body"></main>
                    <footer class="row footer"></footer>
                </div>
            `,
            transclude: true,
            link: function($scope, iElm, iAttrs, controller, transclude) {
               MultiTransclude.transclude(iElm, transclude);
            }
        };
    }])
    .directive('otList', () => {
        // Runs during compile
        return {
            scope: {
                items: "=",
                selected: "=",
                theme: "@"
            }, 
            restrict: 'E',
            template: `<ul ng-class="{'nav nav-justified': theme === 'nav', 'list-group': theme === 'list'}">
                        <li ng-repeat="item in items" 
                            ng-class="{'active': item === selected, 'list-group-item': theme === 'list'}"
                            ng-click="selectItem(item)">
                            <a href="#" ng-bind="item"></a>
                        </li>
                       </ul>`,
            replace: true,
            link: ($scope, iElm, iAttrs, controller) => {
                $scope.selectItem = function(item){
                    $scope.selected = item;
                }
            }
        };
    }).directive('otDropdown', () => {
        // Runs during compile
        return {
            scope: {}, 
            restrict: 'E', 
            template: `<div ng-click="toggleTarget()" ng-transclude>
            </div>`,
            transclude: true,
            controller: function($scope) {
               this.targetOpen = false;
               $scope.toggleTarget = () => {
                    this.targetOpen = !this.targetOpen;
               } 
            }
        };
    }).directive('otTarget', () => {
        // Runs during compile
        return {
            restrict: 'E', // E = Element, A = Attribute, C = Class, M = Comment
            require: '^otDropdown',
            template: `
                <div ng-transclude ng-show="ctrl.targetOpen"></div>
            `,
            transclude: true,
            link: function($scope, iElm, iAttrs, ctrl) {
                $scope.ctrl = ctrl;
            }
        };
    })
    .directive('otTrigger', () => {
        // Runs during compile
        return {
            restrict: 'E', // E = Element, A = Attribute, C = Class, M = Comment
            template: `
                <div ng-transclude></div>
            `,
            transclude: true,
            link: function($scope, iElm, iAttrs, controller) {

            }
        };
    })
    .controller('AppController', ['$scope', ($scope) => {
        $scope.area = {
            list: [
                "Configs",
                "Services",
                "Controllers",
                "Directives"
            ],
            current: 'Configs'
        };
        $scope.app = {
            list: [
                "Configs",
                "Services",
                "Controllers",
                "Directives"
            ],
            current: 'Configs'
        };
    }]);
hjzheng commented 9 years ago

三个例子源码: https://github.com/hjzheng/CUF_meeting_knowledge_share/tree/master/2015-5-20