読者です 読者をやめる 読者になる 読者になる

AngularJS Directive チュートリアル STEP1〜STEP5

JavaScript

f:id:Naotsugu:20140418011743p:plain

Step0 事前準備

index.html

<!doctype html>
<html lang="ja" ng-app="app">
<head>
  <meta charset="utf-8">
  <title>My HTML File</title>
  <link href="http://netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet">
</head>
<body ng-controller="AppCtrl">

  <p>{{value}}</p>

  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.6/angular.min.js"></script>
  <script src="controllers.js"></script>
</body>
</html>

controllers.js

'use strict';

angular.module('app', [])

.controller('AppCtrl', function($scope) {
    $scope.value = 'hello'
});

ブラウザの表示は以下となる

hello

これを出だしとする。

Step1 ディレクティブの定義

myDivというディレクティブを定義する

'use strict';

angular.module('app', [])

.controller('AppCtrl', function($scope) {
    $scope.value = 'hello'
})

.directive('myDiv', function() {
    return {
        restrict: 'E',
        template: "My directive"
    };
});

my-divという要素を追加する

<!doctype html>
<html lang="ja" ng-app="app">
<head>
  <meta charset="utf-8">
  <title>My HTML File</title>
  <link href="http://netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet">
</head>
<body ng-controller="AppCtrl">

  <my-div>will be replaced</my-div>

  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.6/angular.min.js"></script>
  <script src="controllers.js"></script>
</body>
</html>

ブラウザの表示は以下となる。my-divがディレクティブで定義されたテンプレートの内容で置き換えられる。

My directive

Angularは以下のルールでタグや要素の名前を正規化する

  • 要素/属性の前方から、x-data-を取り除く
  • : - _ 区切りされた名称をキャメルケースに変換

そのためmy-divというタグがmyDivとして定義したディレクティブにマッチして適用される

Step2 restrict の指定

属性名としてディレクティブを適用したい場合は

  <div my-div>will be replaced</div>

restrict として A を指定する

.directive('myDiv', function() {
  return {
    restrict: 'A',
    template: "My directive"
  };
});

ブラウザの表示は上と同じように以下となる。

My directive

restrict はディレクティブを適用したい名前を制限できる

  • 'A'属性名のみにマッチ
  • 'E'要素名のみにマッチ
  • 'AE'属性名、要素名のどちらかにマッチ

通常、テンプレートを管理しているコンポーネントを作成する場合は要素Eを使用し、既存の要素に新しい機能を追加する場合には属性'A'を使用する。

Step3 テンプレートの分離

ディレクティブのテンプレートは外だしにすることができる。 少し複雑なテンプレートを利用する場合にはディレクティブと分離しておくのが良い。

template.html として以下を作成

  My directive template

ディレクティブ側で templateUrl としてテンプレート読み込み。

.directive('myDiv', function() {
  return {
    restrict: 'A',
    templateUrl: "template.html"
  };
});

ブラウザの表示は以下となる。

My directive template

ローカルのファイルとしてブラウザから見た場合に、Chromeでは今見ているファイルだけが Same origin として 扱われるため「Cross origin requests are only supported for HTTP」のエラーとなる。 Firefox で見るか、サーバを立てる必要がある。

index.html に ng-template を以下のように記述することで、Chrome の Same origin ポリシーを回避することもできる。

<!doctype html>
<html lang="ja" ng-app="app">
<head>
  <meta charset="utf-8">
  <title>My HTML File</title>
  <link href="http://netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet">
</head>
<body ng-controller="AppCtrl">

  <div my-div>will be replaced</div>

  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.6/angular.min.js"></script>
  <script src="controllers.js"></script>

<script type="text/ng-template" id="template.html">
  My directive template
</script>

</body>
</html>

Step4 スコープ

template.html に{{value}}を追加する。

  My directive template {{value}}

コントローラで定義した $scope.value の値が interpolation 対象となり。

angular.module('app', [])

.controller('AppCtrl', function($scope) {
    $scope.value = 'hello'
})

.directive('myDiv', function() {
    return {
        restrict: 'A',
        templateUrl: "template.html"
    };
});

ブラウザの表示は以下のようになる。

My directive template hello

scopeを以下のように定義すると、

.directive('myDiv', function() {
  return {
    restrict: 'A',
    scope: {},
    templateUrl: "template.html"
  };
});

ブラウザの表示は以下のようになり、コントローラで定義したスコープが見えなくなっていることがわかる。

My directive template

スコープの定義は以下が可能

  • scope: false 新しいscopeを作成せず、ディレクティブが適用される場所のscopeが使われる
  • scope: true 既存のscopeを継承して新しいscopeを作成する
  • scope: {...}独立した隔離スコープ(isolate scope)を作成する

デフォルトでは scope: false として動作するが、外部スコープに依存するため通常はスコープを分離して再利用性を確保する。 ng-controller ディレクティブのように 親 scope のデータや function を利用する場合にはscope: trueを利用する。

Step5 隔離スコープ(isolate scope)

template.html を以下のように変更する。

My directive template [{{value}}] [{{isolateValue}}]

ディレクティブを以下のように scope 定義する。

angular.module('app', [])

.controller('AppCtrl', function($scope) {
    $scope.value = 'hello';
})

.directive('myDiv', function() {
    return {
        restrict: 'A',
        scope: {
            isolateValue: '='
        },
        templateUrl: 'template.html'
    };
});

上記スコープ定義scope: {・・・は、外部から isolate-value (正規化してisolateValue)という名前で指定された値を ディレクティブの内部スコープにisolateValueとして紐づけるという意味になる。

ディレクティブの呼び出しを以下のように isolate-value としてコントローラで定義した $scope.value の値を指定する。

  <div my-div isolate-value="value">will be replaced</div>

ブラウザの表示は以下となる。コントローラで定義した $scope.value の値は見えず、isolateValue として外部から呼び入れた値のみが見えている。

My directive template [] [hello]

前述のスコープ定義は、省略記法で、html側で定義された名前とディレクティブ内部での名前を変える場合には以下のように記載する。

scope: {
  isolateValue: '=info'
},

html側では info という名前で指定できる。

  <div my-div info="value">will be replaced</div>

scope に指定したオブジェクトにて、関数に対する引数定義を行っているイメージ。 通常は親からプロトタイプ継承するスコープだが隔離スコープでは行われない。

隔離スコープには以下の指定が可能

  • name: '@' 値を紐づける
  • name: '=' データを紐づける
  • name: '&' 関数を紐づける(ディレクティブに振る舞いを紐付けるためにAPIを公開したい際に使用)