angularJS directive用法詳解

前言html

最近學習了下angularjs指令的相關知識,也參考了前人的一些文章,在此總結下。jquery

歡迎批評指出錯誤的地方。angularjs

Angularjs指令定義的APIexpress

圖片描述

AngularJs的指令定義大體以下數組

angular.module("app",[]).directive("directiveName",function(){緩存

return{
 //經過設置項來定義
};

})
其中return返回的對象包含不少參數,下面一一說明服務器

你知道用AngularJs怎麼定義指令嗎?0app

1.restrictless

(字符串)可選參數,指明指令在DOM裏面以什麼形式被聲明;異步

取值有:E(元素),A(屬性),C(類),M(註釋),其中默認值爲A;

E(元素):<directiveName></directiveName>
A(屬性):<div directiveName='expression'></div>
C(類): <div class='directiveName'></div>
M(註釋):<--directive:directiveName expression-->

你知道用AngularJs怎麼定義指令嗎?1

例如restrict:‘EA’ 則表示指令在DOM裏面可用元素形式和屬性形式被聲明;

通常來講,當你建立一個有本身模板的組件的時候,須要使用元素名,若是僅僅是爲爲已有元素添加功能的話,就使用屬性名

注意:若是想支持IE8,則最好使用屬性和類形式來定義。 另外Angular從1.3.x開始, 已經放棄支持IE8了.

2.priority

(數字),可選參數,指明指令的優先級,若在單個DOM上有多個指令,則優先級高的先執行;

設置指令的優先級算是不經常使用的

比較特殊的的例子是,angularjs內置指令的ng-repeat的優先級爲1000,ng-init的優先級爲450;

3.terminal

(布爾型),可選參數,能夠被設置爲true或false,若設置爲true,則優先級低於此指令的其餘指令則無效,不會被調用(優先級相同的仍是會執行)

4.template(字符串或者函數)可選參數,能夠是:

(1)一段HTML文本

angular.module("app",[]).directive("hello",function(){

return{
             restrict:'EA',
             template:"<div><h3>hello world</h3></div>"
            };
        })

HTML代碼爲:<hello></hello>
結果渲染後的HTML爲:<hello>
<div><h3>hello world</h3></div>
</hello>
(2)一個函數,可接受兩個參數tElement和tAttrs

其中tElement是指使用此指令的元素,而tAttrs則實例的屬性,它是一個由元素上全部的屬性組成的集合(對象)形如:

{
title:‘aaaa’,
name:'leifeng'
}
下面讓咱們看看template是一個函數時候的狀況

angular.module("app",[]).directive("directitle",function(){

return{
             restrict:'EAC',
             template: function(tElement,tAttrs){
                var _html = '';
                _html += '<div>'+tAttrs.title+'</div>';
                return _html;
             }
            };
        })

HTML代碼:<directitle title='biaoti'></directitle>
渲染以後的HTML:<div>biaoti</div>
由於一段HTML文本,閱讀跟維護起來都是很麻煩的,所用一般會使用templateUrl這個。

5.templateUrl(字符串或者函數),可選參數,能夠是

(1)一個表明HTML文件路徑的字符串

(2)一個函數,可接受兩個參數tElement和tAttrs(大體同上)

注意:在本地開發時候,須要運行一個服務器,否則使用templateUrl會報錯 Cross Origin Request Script(CORS)錯誤

因爲加載html模板是經過異步加載的,若加載大量的模板會拖慢網站的速度,這裏有個技巧,就是先緩存模板

你能夠再你的index頁面加載好的,將下列代碼做爲你頁面的一部分包含在裏面。

<script type='text/ng-template' id='woshimuban.html'>

<div>我是模板內容</div>

</script>
這裏的id屬性就是被設置在templateUrl上用的。

另外一種辦法緩存是:

angular.module("template.html", []).run(["$templateCache", function($templateCache) {
$templateCache.put("template.html",

"<div>wo shi mu ban</div>");

}]);

6.replace

(布爾值),默認值爲false,設置爲true時候,咱們再來看看下面的例子(對比下在template時候舉的例子)

angular.module("app",[]).directive("hello",function(){

return{
             restrict:'EA',
             replace:true,
             template:"<div><h3>hello world</h3></div>"
            };
        })

HTML代碼爲:
<hello></hello>
渲染以後的代碼:<div><h3>hello world</h3></div>
對比下沒有開啓replace時候的渲染出來的HTML。發現<hello></hello>不見了。

另外當模板爲純文本(即template:"wo shi wen ben")的時候,渲染以後的html代碼默認的爲文本用span包含。

7.scope

可選參數,(布爾值或者對象)默認值爲false,可能取值:

(1)默認值false。

表示繼承父做用域;

(2)true

表示繼承父做用域,並建立本身的做用域(子做用域);

(3){}

表示建立一個全新的隔離做用域;

7.1首先咱們先來了解下scope的繼承機制。咱們用ng-controller這個指令舉例,

咱們都知道ng-controller(內置指令)能夠從父做用域中繼承而且建立一個新的子做用域。以下:

<!doctype html>
<html ng-app="myApp">
<head>
<script src="http://cdn.staticfile.org/ang...
</head>
<body>
<div ng-init="aaa='父親'">

parentNode:{{aaa}}
<div ng-controller="myController">
    chrildNode: {{aaa}}
</div>

</div>

<script>

angular.module('myApp', [])
.controller('myController',function($scope){
   $scope.aaa = '兒子'
})

</script>
</body>
</html>
這時頁面顯示是

parentNode:父親

chrildNode: 兒子

若去掉

$scope.aaa = '兒子'
則顯示

parentNode:父親

chrildNode: 父親

注意:

1)若一個元素上有多個指令,使用了隔離做用域,則只有其中一個能夠生效;

2)只有指令模板中的根元素才能得到一個新的做用域,這時候,scope就被設置爲true了;

<!doctype html>
<html ng-app="myApp">
<head>
<script src="http://cdn.staticfile.org/ang...
</head>
<body>
<div ng-init="aaa='父親'">

parentNode:{{aaa}}
<div class='one' ng-controller="myController">
    chrildNode: {{aaa}}
    <div class='two' ng-controller='myController2'>
      {{aaa}}
    </div>
</div>

</div>

<script>

angular.module('myApp', [])
.controller('myController',function($scope){
  $scope.aaa = '兒子';
})
.controller('myController2',function($scope){
  $scope.aaa = '孫女';
})

</script>
</body>
</html>
頁面顯示爲:

parentNode:父親

chrildNode: cunjieliu

孫女

上面中class爲one那個div得到了指令ng-controller=’myController‘所建立的新的做用域;

而class爲two那個div得到了指令ng-controller=’myController2‘所建立的新的做用域;

這就是「只有指令模板中的根元素才能得到一個新的做用域」;

接下來咱們經過一個簡單明瞭的例子來講明scope取值不一樣的差異

<!doctype html>
<html ng-app="myApp">
<head>
<script src="http://cdn.staticfile.org/ang...
</head>
<body>

<div ng-controller='MainController'>

父親: {{name}}
    <input ng-model="name" />
    <div my-directive></div>

</div>

<script>

angular.module('myApp', [])
    .controller('MainController', function ($scope) {
       $scope.name = 'leifeng';
    })
    .directive('myDirective', function () {
        return {
            restrict: 'EA',
            scope:false,//改變此處的取值,看看有什麼不一樣
            template: '<div>兒子:{{ name }}<input ng-model="name"/></div>'
        };
    });

</script>

</body>
</html>
依次設置scope的值false,true,{},結果發現(你們別偷懶,動手試試哈)

當爲false時候,兒子繼承父親的值,改變父親的值,兒子的值也隨之變化,反之亦如此。(繼承不隔離)

當爲true時候,兒子繼承父親的值,改變父親的值,兒子的值隨之變化,可是改變兒子的值,父親的值不變。(繼承隔離)

當爲{}時候,沒有繼承父親的值,因此兒子的值爲空,改變任何一方的值均不能影響另外一方的值。(不繼承隔離)

tip:當你想要建立一個可重用的組件時隔離做用域是一個很好的選擇,經過隔離做用域咱們確保指令是‘獨立’的,並能夠輕鬆地插入到任何HTML app中,而且這種作法防止了父做用域被污染;

7.2隔離做用域能夠經過綁定策略來訪問父做用域的屬性。

下面看一個例子

<!doctype html>
<html ng-app="myApp">
<head>
<script src="http://cdn.staticfile.org/ang...
</head>
<body>

<div ng-controller='MainController'>

<input type="text" ng-model="color" placeholder="Enter a color"/>   
    <hello-world></hello-world>

</div>

<script>

var app = angular.module('myApp',[]);
app.controller('MainController',function(){});
app.directive('helloWorld',function(){
 return {
    scope: false,
    restrict: 'AE',
    replace: true,
    template: '<p style="background-color:{{color}}">Hello World</p>'      
 }
});

</script>

</body>
</html>
運行代碼,並在input中輸入顏色值,結果爲

你知道用AngularJs怎麼定義指令嗎?2

可是,但咱們將scope設置爲{}時候,再次運行上面的代碼能夠發現頁面並不能成功完整顯示!

緣由在於,這裏咱們將scope設置爲{},產生了隔離做用域。

因此在template模板中{{color}}變成了依賴於本身的做用域,而不是依賴於父做用域。

所以咱們須要一些辦法來讓隔離做用域能讀取父做用域的屬性,就是綁定策略。

下面咱們就來探索設置這種綁定的幾種方法

方法一:使用@(@attr)來進行單向文本(字符串)綁定

<!doctype html>
<html ng-app="myApp">
<head>
<script src="http://cdn.staticfile.org/ang...
</head>
<body>

<div ng-controller='MainController'>

<input type="text" ng-model="color" placeholder="Enter a color"/>   
    <hello-world color-attr='{{color}}'></hello-world>   //注意這裏設置了color-attr屬性,綁定了{{color}}

</div>

<script>

var app = angular.module('myApp',[]);
app.controller('MainController',function(){});
app.directive('helloWorld',function(){
 return {
    scope: {color:'@colorAttr'},  //指明瞭隔離做用域中的屬性color應該綁定到屬性colorAttr
    restrict: 'AE',
    replace: true,
    template: '<p style="background-color:{{color}}">Hello World</p>'      
 }
});

</script>

</body>
</html>
這種辦法只能單向,經過在運行的指令的那個html標籤上設置color-attr屬性,而且採用{{}}綁定某個模型值。

注意,你也能夠再這裏直接綁定字符串的顏色值,如:color-attr=「red」;

而後你能夠看到表達式{{color}}被賦值給了color-attr。

當表達式的值發生變化時,屬性color-attr也會發生變化,因此也改變了隔離做用域中的屬性color。

tips:若是綁定的隔離做用域屬性名與元素的屬性名相同,則能夠採起缺省寫法。

html:
<hello-world color="{{color}}"/>

js定義指令的片斷:
app.directive('helloWorld',function(){

return {
    scope: {
        color: '@'
    },
    ...
    //配置的餘下部分
}

});
方法二:使用=(=attr)進行雙向綁定

<!doctype html>
<html ng-app="myApp">
<head>
<script src="http://cdn.staticfile.org/ang...
</head>
<body>

<div ng-controller='MainController'>

<input type="text" ng-model="color" placeholder="Enter a color"/>
    {{color}}
    <hello-world color='color'></hello-world>  //注意這裏的寫法

</div>

<script>

var app = angular.module('myApp',[]);
app.controller('MainController',function(){});
app.directive('helloWorld',function(){
 return {
    scope:{color:'='},
    restrict: 'AE',
    replace: true,
    template: '<div style="background-color:{{color}}">Hello World<div><input type="text" ng-model="color"></div></div>'      
 }
});

</script>

</body>
</html>
此處也相似上面採用了缺省的寫法。

這裏須要注意的是,咱們要直接在指令運行的那個元素上設置屬性時候,是直接將 實際的做用域模型 賦值給該屬性(這裏就是color)

這樣一個雙向綁定被創建了,改變任何一個input都會改變另外一個值。

你知道用AngularJs怎麼定義指令嗎?3

方法三:使用&來調用父做用域中的函數

<!doctype html>
<html ng-app="myApp">
<head>
<script src="http://cdn.staticfile.org/ang...
</head>
<body>

<div ng-controller='MainController'>

<input type="text" ng-model="name" placeholder="Enter a color"/>
    {{name}}
    <hello-world saysomething999="say();" name="liucunjie"></hello-world> //注意這裏

</div>

<script>

var app = angular.module('myApp',[]);
app.controller('MainController',function($scope){
  $scope.say = function(){
    alert('hello');
  }
  $scope.name = 'leifeng';
});
app.directive('helloWorld',function(){
 return {
    scope:{
      saysomething:'&saysomething999',
      name:'@'
    },
    restrict: 'AE',
    replace: true,
    template: '<button type="button" ng-bind="name" ng-init="saysomething();"></button>'
 }
});

</script>

</body>
</html>
運行以後,彈出alert框。

8.transclude

(布爾值或者字符‘element’),默認值爲false;

這個配置選項可讓咱們提取包含在指令那個元素裏面的內容,再將它放置在指令模板的特定位置。

當你開啓transclude後,你就可使用ng-transclude來指明瞭應該在什麼地方放置transcluded內容

<!doctype html>
<html ng-app="myApp">
<head>
<script src="http://cdn.staticfile.org/ang...
</head>
<body>

<div ng-controller='MainController'>

<div class='a'>
      <p>china</p>
      <hello-world>
        {{name}}
      </hello-world>
    </div>

</div>

<script>

var app = angular.module('myApp',[]);
app.controller('MainController',function($scope){
  $scope.name = 'leifeng';
});
app.directive('helloWorld',function(){
 return {
    scope:{},  
    restrict: 'AE',
    transclude: true,
    template: '<div class="b"><div ng-transclude>你看不見我</div></div>'
 }
});

</script>

</body>
</html>
運行上面的代碼,輸出

china

leifeng

另外當開啓transclude,會建立一個新的transclude空間,而且繼承了父做用域(即便Scope設置爲隔離做用域),

上面代碼中的{{name}}是依賴於父做用域的,仍然能被渲染出來,就說明了這點。

咱們再看看生成的html爲下圖所示,能夠發現文本「你看不見我」消失了,這是由於被transclude內容替換掉了。

這裏的transclude內容就是{{name}}

你知道用AngularJs怎麼定義指令嗎?4

接下來再來看看transclude的另外一個取值transclude:「element」

那transclude:「element」與transclude:true有什麼區別呢?

區別在於嵌入的內容,以上面的例子來講,

當transclude:true時候,嵌入的內容爲{{name}},

而當transclude:「element」時候,嵌入的內容爲

<hello-world>
{{name}}
</hello-world>
沒錯,此時嵌入的內容爲整個元素。

將上面代碼transclude:true換成transclude:true後,再運行,你會發現結果並非和你想的同樣

再次查看生成的html代碼

你知道用AngularJs怎麼定義指令嗎?5

你會發現指令綁定的元素被轉換爲了一個HTML註釋

關於這方面的疑問能夠查看 transclude: 'element' is useless without replace:true 獲取更多

解決方案是加上replace: true,就正常了

這時再查看HTML代碼

你知道用AngularJs怎麼定義指令嗎?6

注意:在一個指令的模板template上只能申明一個ng-transclude。

OK,那麼如今問題來了,若是咱們想把嵌入部分屢次放入咱們的模板中要怎麼辦?

則可使用$transclude(後面再controller選項中會講)

或者可使用compile函數,裏面有個transcludeFn參數(後面會講)

或者使用link連接函數。。。

9.controller

能夠是一個字符串或者函數。

如果爲字符串,則將字符串當作是控制器的名字,來查找註冊在應用中的控制器的構造函數

angular.module('myApp', [])
.directive('myDirective', function() {
restrict: 'A', // 始終須要
controller: 'SomeController'
})
// 應用中其餘的地方,能夠是同一個文件或被index.html包含的另外一個文件
angular.module('myApp')
.controller('SomeController', function($scope, $element, $attrs, $transclude) {
// 控制器邏輯放在這裏
});
也能夠直接在指令內部的定義爲匿名函數,一樣咱們能夠再這裏注入任何服務($log,$timeout等等)

angular.module('myApp',[])
.directive('myDirective', function() {
restrict: 'A',
controller:
function($scope, $element, $attrs, $transclude) {
// 控制器邏輯放在這裏
}
});
另外還有一些特殊的服務(參數)能夠注入

(1)$scope,與指令元素相關聯的做用域

(2)$element,當前指令對應的 元素

(3)$attrs,由當前元素的屬性組成的對象

(4)$transclude,嵌入連接函數,實際被執行用來克隆元素和操做DOM的函數

注意: 除非是用來定義一些可複用的行爲,通常不推薦在這使用。

指令的控制器和link函數(後面會講)能夠進行互換。區別在於,控制器主要是用來提供可在指令間複用的行爲但link連接函數只能在當前內部指令中定義行爲,且沒法再指令間複用。

html代碼: <my-site site="http://www.cnblogs.com/cunjie...雷鋒叔叔的博客</my-site>

js代碼:
<script>

angular.module('myApp',[]).directive('mySite', function () {
 return {
     restrict: 'EA',
     transclude: true, //注意此處必須設置爲true
     controller:
     function ($scope, $element,$attrs,$transclude,$log) {  //在這裏你能夠注入你想注入的服務
         $transclude(function (clone) {              
             var a = angular.element('<a>');
             a.attr('href', $attrs.site);
             a.text(clone.text());
             $element.append(a);
         });
         $log.info("hello everyone");
     }
 };

});
</script>
運行上面的代碼就是

你知道用AngularJs怎麼定義指令嗎?7

而且在控制檯下輸出hello everyone

讓咱們看看$transclude();在這裏,它能夠接收兩個參數,第一個是$scope,做用域,第二個是帶有參數clone的回調函數。

而這個clone實際上就是嵌入的內容(通過jquery包裝),能夠在它上作不少DOM操做。

它還有最簡單的用法就是

<script>

angular.module('myApp',[]).directive('mySite', function () {
 return {
     restrict: 'EA',
     transclude: true,
     controller:
     function ($scope, $element,$attrs,$transclude,$log) {
         var a = $transclude(); //$transclude()就是嵌入的內容
         $element.append(a);
     }
 };

});
</script>
注意:使用$transclude會生成一個新的做用域。

默認狀況下,若是咱們簡單實用$transclude(),那麼默認的其做用域就是$transclude生成的做用域

可是若是咱們實用$transclude($scope,function(clone){}),那麼做用域就是directive的做用域了

那麼問題又來了。若是咱們想實用父做用域呢

可使用$scope.$parent

<div ng-controller='parentctrl'>

<div ng-controller='sonctrl'>
      <my-site site="http://www.cnblogs.com/cunjieliu"><div>雷鋒叔叔的博客</div></my-site>
    </div>

</div>
<script>

var app = angular.module('myApp',[]);
app.controller('sonctrl',function($scope){
  $scope.title = 'hello son';
});
app.controller('parentctrl',function($scope){
  $scope.title = 'hello parent';
});
app.directive('mySite', function () {
 return {
     restrict: 'EA',
     transclude: true,
     controller:
     function ($scope, $element,$attrs,$transclude,$log) {
        var a = $transclude();
        $element.append(a);
        $log.info($scope.title);
        $log.info($scope.$parent.title);         
     }
 };

});
</script>
同理想要一個新的做用域也可使用$scope.$parent.new();

10.controllerAs

這個選項的做用是能夠設置你的控制器的別名

通常之前咱們常常用這樣方式來寫代碼:

angular.module("app",[])
.controller("demoController",["$scope",function($scope){

$scope.title = "angualr";

}])

<div ng-app="app" ng-controller="demoController">

{{title}}

</div>
後來angularjs1.2給咱們帶來新語法糖,因此咱們能夠這樣寫

angular.module("app",[])
.controller("demoController",[function(){

this.title = "angualr";

}])

<div ng-app="app" ng-controller="demoController as demo">

{{demo.title}}

</div>
一樣的咱們也能夠再指令裏面也這樣寫

<script>

angular.module('myApp',[]).directive('mySite', function () {
 return {
     restrict: 'EA',
     transclude: true,
     controller:'someController',
     controllerAs:'mainController'
     //..其餘配置
 };

});
</script>
11.require(字符串或者數組)

字符串表明另外一個指令的名字,它將會做爲link函數的第四個參數

具體用法咱們能夠舉個例子說明

假設如今咱們要編寫兩個指令,兩個指令中的link連接函數中(link函數後面會講)存在有不少重合的方法,

這時候咱們就能夠將這些重複的方法寫在第三個指令的controller中(上面也講到controller常常用來提供指令間的複用行爲)

而後在這兩個指令中,require這個擁有controller字段的的指令(第三個指令),

最後經過link連接函數的第四個參數就能夠引用這些重合的方法了。

<!doctype html>
<html ng-app="myApp">
<head>
<script src="http://cdn.staticfile.org/ang...
</head>
<body>

<outer-directive>

<inner-directive></inner-directive>
 <inner-directive2></inner-directive2>

</outer-directive>
<script>

var app = angular.module('myApp', []);
app.directive('outerDirective', function() {
      return {
           scope: {},
           restrict: 'AE',
           controller: function($scope) {      
              this.say = function(someDirective) { 
                 console.log('Got:' + someDirective.message);
              };
           }
       };
});
app.directive('innerDirective', function() {
      return {
           scope: {},
           restrict: 'AE',
           require: '^outerDirective',
           link: function(scope, elem, attrs, controllerInstance) {
                   scope.message = "Hi,leifeng";
                   controllerInstance.say(scope);
           }
      };
});
app.directive('innerDirective2', function() {
      return {
           scope: {},
           restrict: 'AE',
           require: '^outerDirective',
           link: function(scope, elem, attrs, controllerInstance) {
                   scope.message = "Hi,shushu";
                   controllerInstance.say(scope);
           }
      };
});

</script>

</body>
</html>
上面例子中的指令innerDirective和指令innerDirective2複用了定義在指令outerDirective的controller中的方法

也進一步說明了,指令中的controller是用來讓不一樣指令間通訊用的。

另外咱們能夠在require的參數值加上下面的某個前綴,這會改變查找控制器的行爲:

(1)沒有前綴,指令會在自身提供的控制器中進行查找,若是找不到任何控制器,則會拋出一個error

(2)?若是在當前的指令沒有找到所需的控制器,則會將null傳給link鏈接函數的第四個參數

(3)^若是在當前的指令沒有找到所需的控制器,則會查找父元素的控制器

(4)?^組合

12.Anguar的指令編譯過程

首先加載angularjs庫,查找到ng-app指令,從而找到應用的邊界,

根據ng-app劃定的做用域來調用$compile服務進行編譯,

angularjs會遍歷整個HTML文檔,並根據js中指令的定義來處理在頁面上聲明的各個指令

按照指令的優先級(priority)排列,根據指令中的配置參數(template,place,transclude等)轉換DOM

而後就開始按順序執行各指令的compile函數(若是指令上有定義compile函數)對模板自身進行轉換

注意:此處的compile函數是咱們指令中配置的,跟上面說的$compile服務不同。

每一個compile函數執行完後都會返回一個link函數,全部的link函數會合成一個大的link函數

而後這個大的link函數就會被執行,主要作數據綁定,經過在DOM上註冊監聽器來動態修改scope中的數據,

或者是使用$watchs監聽 scope中的變量來修改DOM,從而創建雙向綁定等等。

若咱們的指令中沒有配置compile函數,那咱們配置的link函數就會運行,

她作的事情大體跟上面complie返回以後全部的link函數合成的的大的link函數差很少。

因此:在指令中compile與link選項是互斥的,若是同時設置了這兩個選項,

那麼就會把compile所返回的函數當作是連接函數,而link選項自己就會被忽略掉

13.compile編譯函數和link連接函數

13.1compile編譯函數選項

compile選項能夠返回一個對象或者函數

在這裏咱們能夠在指令和實時數據被放到DOM中以前進行DOM操做,

好比咱們能夠在這裏進行添加或者刪除節點的DOM的操做。

因此編譯函數是負責對模板的DOM進行轉換,而且僅僅只會運行一次。

//compile函數的語法
compile:function compile(tElement,tAttrs,transclude){

return{
    pre:function preLink(scope,iElement,iAttrs,controller){},
    post:function postLink(scope,iElement,iAttrs,controller){}
  }
}

對於咱們編寫的大部分的指令來講,並不須要對模板進行轉換,因此大部分狀況只要編寫link函數就能夠了。

tips:preLink函數會在編譯階段以後,指令連接到子元素以前執行

相似的,postLink會在指令連接到子元素以後執行

    這意味着,爲了避免破壞綁定過程,若是你須要修改DOM結構,你應該在postLink函數中來作這件事。

13.2link連接函數選項

連接函數負責將做用域和DOM進行連接。

//link連接函數link:function postLink(scope,iElement,iAttrs){}若指令中定義有require選項,則link函數會有第四個參數,表明控制器或者所依賴的指令的控制器(上面require選項例子已有例子)

相關文章
相關標籤/搜索