2017年5月12日 星期五

AngularJS 自製有Service、directive的外掛module練習

今天要來製作一個自製的AngularJS外掛,用來練習AngularJS的模組化特性。

要實作的是一個簡單的Lightbox (或Modal),要練習的點是:

  1. 此外掛為一個Module,可引入至其他的AngularJS Module。
  2. 此Module提供一個Service,可以由引入此Module的程式呼叫使用。
  3. 此Module提供一個Directive,Directive可以與參數雙向(或單向,看需求)綁定,並且Directive可以呼叫上述Service來使用。

目標結果為:

  1. Lightbox外掛提供一個Service,可以開始及關閉Lightbox,並且可以在開啟Lightbox時提供要顯示在Lightbox中的文字。
  2. Lightbox外掛提供一個Directive,Directive經過設計,有提供一個按鈕,其被按下會執行Service的開啟lightbox功能,Directive可以給入參數,設定Lightbox中要顯示的文字。
  3. 開啟的Lightbox提供一個Close按鈕,按下可以關閉Lightbox。
  4. 一次只會有一個Lightbox被開啟。

其成品就像如下視頻那樣:


首先先 看一下檔案結構:


  1. /index.html:主要的網頁頁面。
  2. /js/index.js :index.html使用的JS,主要頁面的AngularJS邏輯。
  3. /js/angular.js : 要引入使用的angularjs。
  4. /js/myAngularJsPlugin.js : 要撰寫的自製Lightbox AngularJS Plugin。
程式碼如下,相關的說明都已經寫在註解中了:
  1. /index.html :
    <!DOCTYPE html>
    <html>
        <head>
            <title>TODO supply a title</title>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
    
            <script src="js/angular.js"></script>
            <script src="js/myAngularJsPlugin/myModalModule.js"></script>
            <script src="js/index.js"></script>
        </head>
        <body>
            <div ng-app="myApp" ng-controller="myController as myCtrl">
                可自行輸入要在Modal內顯示的文字
                <div style="margin-bottom: 20px;">
                    <input type="text" ng-model="myCtrl.modalText"/>
                </div>
                在Controller裡呼叫自製Module提供的Service來開啟Modal
                <div style="margin-bottom: 20px;">                
                    <button ng-click="myCtrl.openModal(myCtrl.modalText)">Open Modal manually</button>
                </div>
                
                使用自製Module提供的Directive來開啟Modal
                <div>                
                    <my-modal-directive modal-text="myCtrl.modalText"></my-modal-directive>
                </div>
            </div>
        </body>
    </html>
  2. /js/index.js :
    angular.module("myApp", ["myModalModule"]);
    
    angular.module("myApp").controller("myController", ["myModalService", function(myModalService){
        var self = this;
        self.modalText = "測試文字";
        self.openModal = openModal;
        
        function openModal(modalText){
            myModalService.open(modalText);
        }
    }]);
  3. /js/myAngularJsPlugin.js:
    //Module
    angular.module("myModalModule", []);
    //Service
    angular.module("myModalModule").factory("myModalService", ["$rootScope", "$compile", function ($rootScope, $compile) {
            //設計一次只能有一個Modal開啟
            var scope;
            var modal;
            
            var $body = angular.element(document).find('body');
            var isModalOpened = false;
            //modal template中有跟angularjs scope有關的程式碼,
            //須要使用$compile編譯並與scope連結才能有作用
            var modalTemplate = '<div style="position: fixed; top: 0px; left: 0px; width: 100%; height: 100%; background-color: rgba(158, 158, 158, 0.68); ">' +
                                    '<button ng-click="close()" >Close</button>' +
                                    '<div style="display: flex; align-items: center; justify-content:center; width: 100%; height: 100%;">' +
                                        '{{textContent}}' +
                                    '</div>' +
                                '</div>';
            //functions
            function open(textContent) {
                if (!isModalOpened) {
                    //創建暫時的scope
                    scope = $rootScope.$new();
                    //在scope中設置close函式
                    scope.close = close;
                    //在scope中設置textContent
                    scope.textContent = textContent;
                    //使用$compile來編譯modalTemplate,得到link函式,執行link函式並代入scope來與scope連結,
                    //最後得到連結好的DOM
                    modal = $compile(modalTemplate)(scope);
                    //將得到的DOM加進<body>中
                    $body.append(modal);
                    //設置modal opened狀態
                    isModalOpened = true;
                }
    
            }
            function close() {
                if (isModalOpened) {
                    //將Modal DOM移除
                    modal.remove();
                    //移除暫時scope
                    scope.$destroy();
                    //設置modal opened狀態
                    isModalOpened = false;
                }
            }
            //自製Service對外提供的API
            return {
                open: open,
                close: close
            };
        }]);
    //Directive
    angular.module("myModalModule").directive("myModalDirective", ["myModalService", function (myModalService) {
            return {
                restrict: "E",
                replace: true,
                scope: {
                    //自製Directive可接受modalText參數(為表達示)
                    modalText: "=modalText"
                },
                template: generateTemplate,
                link: function ($scope, $elm, $attrs, $ngModel) {
                    $scope.openModal = myModalService.open;                
                    /* 以上寫法等同以下,上面雖簡潔但下面寫法可讀性比較好
                    $scope.openModal = function (modalText) {
                        myModalService.open(modalText);
                    }
                    */
                }
            };
    
            function generateTemplate($elm, $attrs) {
                 //用function回傳template,也可以直接用字串不用函式
                return '<button ng-click="openModal(modalText)">Open Modal by Directive</button>';
            }
        }]);
原始碼下載:
angularJS-servicePractice.7z


參考資料:
  1. 深入解析 CSS Flexbox

沒有留言 :

張貼留言