Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
hjzheng opened this issue May 20, 2015 · 3 comments
Open

Comments

@hjzheng
Copy link
Owner

hjzheng commented May 20, 2015

Reusable Components in Angular

  • Multi-transclude
  • Attribute passing
  • Sub-directives

视频: 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
Copy link
Owner Author

hjzheng commented May 22, 2015

Attribute passing

知识点:
scope设置

  • No scope set: 共享scope
  • set true: 产生一个子的scope
  • set {}: 产生一个隔离的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
Copy link
Owner Author

hjzheng commented May 22, 2015

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
Copy link
Owner Author

hjzheng commented May 22, 2015

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant