angularjs directive and component 指令與組件 ( 1.5.0 以后 )


之前寫過一篇了 http://www.cnblogs.com/keatkeat/p/3903673.html

但某些部分寫的不太清楚,甚至有點錯誤,所以今天特地在這里再來談談。

這篇主要是說指令的隔離還有和controller的溝通.

 

指令的運行過程基本上我們可以簡單的理解為 : template -> compile -> controller -> pre-link -> post-link 

我們通常只是用到post link,但如果你的指令有嵌套,子層需要父層的一些值的話,可以考慮把這些初始值寫在 controller 或者是 pre-link 

pre-link 是上層到下層調用, post-link 則相反下至上調用. 

 

在開發中當一個內容經常重復時,我們就會把它封裝成指令。

這種組件的思想可以節省很多開發時間。

而封裝的指令不太可能每一次使用都是一模一樣的。

所以我們必須可以調整它。

一般上我們使用 attr.  (就好象函數和參數的感念)

<div ng-controller="ctrl">  
    <dir data-obj="obj" data-string="{{string}}" data-method="method(outsideParam,insideParam)" ></dir>
</div>
        
<script>
    var app = angular.module("app", []);
    app.directive("dir", ["$parse",function ($parse) {
        return {
            restrict: "E",
            template: '<div>' +
                            '{{ obj.value }}' +
                            '{{ string }}' +
                            '<div ng-click="click()"></div>' +
                        '</div>',
            link: function (scope, elem, attr) {
                //scope.$parent 是controller scope, 隔離只是表示沒有繼承,但是parent還是可以訪問得到。
                scope.click = function () {
                    scope.method({ insideParam: "88" }); //這里你可以選擇要不要覆蓋掉外面的 outsideParam. 非常靈活
                }
            },
            scope: {
                obj: "=", //雙向綁定,內部變化會在digest的時候同步去外面
                //obj: "=object", //data-object="obj" 如果你的屬性名字很不一樣的話,angular 只會把 a-b-c 換成 駝峰式
                //obj : "=?object", //加 ? 表示這個屬性不一定會有。如果沒寫 ? 你使用 "=" 但是寫屬性的話是會報錯的哦
                string: "@", //單向綁定 (只限於string,int, object array can't), 內部改變不會影響外面
                method : "&" //綁定方法, 注意param, 它是可以像 bind 那樣,放入不同階層的 params
            }
        }
    }])
    app.controller("ctrl", ["$scope", "$timeout", function ($scope, $timeout) {
        $scope.obj = {
            value: "lu xiao feng"
        }
        $scope.string = "luxiaofeng";
        $scope.outsideParam = "77";
        $scope.method = function (outsideParam, insideParam) {
            log(outsideParam); //77 這個是預先設定的
            log(insideParam); //88
        }
    }]);
</script>

 

 

更新 : 2015-07-01 

transclude 

參考 : https://docs.angularjs.org/api/ng/service/$compile  (里面的 transclusion)  

在做指令的時候,我們需要很多動態的東西,像之前的屬性,我們可以通過 scope : { "=","@","&" } 來實現動態。

那么如果我們想實現模板或者說某部分的element動態的話,我們可以使用 translude 

<div ng-controller="ctrl">
    <dir>
        <span>{{ name }}</span>          
    </dir>
</div>
<script>
    var app = angular.module("app", []);
    app.controller("ctrl", ["$scope", "$timeout", function ($scope, $timeout) {
        $scope.name = "keatkeat";         
    }]);
    app.directive("dir", [function () {
        return {
            restrict: "E",
            transclude: true,
            template: '<div>' +
                            '<div ng-transclude></div>' +
                        '</div>',
            link: function (scope, elem, attr, ctrl, $transclude) { },
            scope: {}
        }
    }]);
</script>

只要在指令注冊時寫上 translude : true 就可以了。

ng 會把指令中間的 element keep 起來, 然后放到 ng-transclude 里頭 .

ng-transclude 是 angular 寫好的一個指令很方便使用。 

如果你想發揮到淋漓精致的話,有幾點你需要搞清楚!

compile : function (elem,attr,$transclude) | link : function (scope,elem,attr,ctrl,$transclude)

controller : ["$transclude",function($transclude){}] 

compile,link,controller 都可以獲取到 $transclude 方法。

這個方法其實就是 linkFn , ng 預先把指令內的elem 拿去$compile 了

$transclude(scope,cloneAttachFn)

一般上這個 $tranclude 是這樣用的 

$transclude(function (clone, scope) {
    log("trs : " + scope.$id); <- 自己是一個新的scope
    log(scope.$parent); <- 指令的scope ,但卻繼承了 ctrl 的 scope 哦 !

elem.empty(); elem.append(clone); 最后就append 這個clone 出去就行了
});

很重要 : 如果沒有指定 scope 的話,scope 是一個新的scope, 它繼承了ctrl , 但它是指令的孩子。 這有點難理解,但是好處很多 ^^ 下面是原文的解釋

When you call a transclude function it returns a DOM fragment that is pre-bound to a transclusion scope. This scope is special, in that it is a child of the directive's scope (and so gets destroyed when the directive's scope gets destroyed) but it inherits the properties of the scope from which it was taken.

當父指令有 transclude 時,子指令也可以調用的到, 這也是 ng-transclude 能實現的原因了。

 

更新 : 2016-03-01 

angular 1.5.0 版本 translude 有了多一個 mutilple 的功能 

參考 : http://blog.thoughtram.io/angular/2015/11/16/multiple-transclusion-and-named-slots.html

<product>
    @*<detail-one>
        <div>detail one</div>
    </detail-one>*@
    <detail-two>
        <div>detail two</div>
    </detail-two>
</product>
app.directive("product", [function () {
    return {
        restrict: "E",
        transclude: {
            //note : 
            //這對象是一個對接對象, key 對應內部調用 , value 對應外部 (這個算是angular規范,不只是transclude有這樣的設計)
            detailOne: "?detailOne", // ? 代表可有可無
            detailTwo: "detailTwo"
        },
        template: [
            '<div>',
                '<div>',
                    //如果外部沒有傳進來,會使用default的做顯示,有傳進來的話則會覆蓋掉default
                    '<div ng-transclude="detailOne" >default value for details one</div>', 
                '</div>',
                '<br/>',
                '<div>',
                    '<div ng-transclude="detailTwo" >default value for details one</div>',
                '</div>',
            '</div>',
        ].join(''),
        link: function (scope, elem, attr) {}
    }
}]);

 

 更新 : 2016-03-01 

1.5.0 版本 angular 多了一個 component 組件 

refer : 

https://docs.angularjs.org/guide/component

https://toddmotto.com/exploring-the-angular-1-5-component-method/

文章開頭就說明了 a Component is a special kind of directive, 組件是一種特別的指令 (意思是指令基本上可以完成所有組件的功能)

那組件主要的好處就是它比較簡單理解和使用, 邁向 angular 2.0 你應該盡可能的使用組件 (當然如果組件不滿足需求,指令依然是需要的)

這里我主要是講概念的東西。

組件主要就是簡化了以前指令的復雜性。所以你會發現他移除了很多我們不常用到的東西。

比如 : compile, link, scope, multiElement, priority, restrict 都移除了。

而且scope直接就是 isolate 的

所以當我們在使用組件時,我們也必須延着這種簡單的思路去設計。不要再去使用$scope繼承等等。

組件和外部溝通使用的是 bindings , 這個等價於 bindToController + scope : { @,&,= }

1.5.0 多了一個 "<" 符號,叫單向綁定

我們知道以前我們要傳 string,int 我們用 "@"

我們要傳對象,要雙向,我們用 "="

但是我們要傳對象,但是不要雙向,我們就沒有的用了,而現在可以使用 "<"

這里給大家復習一下 : 

@ 外面可以改變里面,里面不可以改變外面 (only for 值類型 e.g. string,int)
= 里面外面完全同步 (如果是obj,array的話,在某些情況下是會打斷引用的,但即使打斷他還是能同步key,value)
< 和 = 一樣,區別在里面不會同步去外面了。(當然如果obj引用一樣的話,你只改prop還是會影響到外面啦)

組件的require和指令也有點不一樣 (多了一個叫 $onInit 的東東)

app.component("product", {
    template: [
        '<div>',
            '<color></color>',
        '</div>'
    ].join(''),
    controller: ["$scope", "$timeout", function ($scope, $timeout) {
        this.shareMethod = function () {
            console.log("share method");
        }
    }]
});
app.component("color", {
    template: [
        '<div>color</div>'
    ].join(''),
    require : {
        productCtrl : "^product"
    },
    controller: ["$scope", function ($scope) {
        this.$onInit = function () {
            this.productCtrl.shareMethod();
        }
    }]
});

require 向上尋找 component 然后在 $onInit 的時候才會注入到ctrl內,require 對象也是一個mapping對象. 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM