HTMLコンパイラ

概要

AngularのHTMLコンパイラは、開発者からブラウザに新しいHTML文法を伝えるための手段です。 コンパイラは、あるHTML要素または属性に対し動作を紐付け、 さらにはカスタマイズされた振る舞いをする新しい要素または属性を作ることも出来るようにします。 Angularは、これらの動作拡張ディレクティブを呼び出します。

HTMLは宣言による静的ドキュメントのための構造フォーマットを多く持ちあわせています。 例えば、何かを中央寄せにする必要がある場合、ブラウザに対してウインドウのサイズの半分の位置を中央として、 その中央とテキストの中心を一致させる、といった指示をする必要はありません。 単純に目的の振る舞いをさせたい要素に対して、align="center"属性を追加するだけです。 宣言言語には、このような力があります。

ただし、宣言言語にはブラウザに対して新しい文法を伝える手段が無いため、限界もあります。 例えば、テキストを1/2の位置に配置する代わりに1/3の位置に配置するのは容易ではありません。 必要とされているのは、ブラウザに新しいHTML文法を伝える方法です。

Angularは、アプリケーション構築の際に便利な共通のディレクティブが予めバンドルされています。 我々はまた、あなた独自のディレクティブを作って欲しいとも願っています。 これらの拡張は、あなたのアプリケーション構築のためのドメイン特化言語(DSL)になります。

このコンパイルの全ては、サーバ側とは関係ないWebブラウザ上、またはプリコンパイルが実行された際に展開されます。 (翻訳に自信なし:プリコンパイルは対象なのか、対象外なのか?)

コンパイラ

コンパイラは、DOMを辿って属性を探すAngularによるサービスです。 コンパイルのプロセスには、2つのフェーズがあります。

  1. コンパイル: DOMを辿り、全てのディレクティブを集めます。結果、機能がリンクされることになります。
  2. リンク: 対象範囲にディレクティブを結合し、ライブビュー機能を提供します。 範囲内のモデルの変更がビューへ影響し、ビューに対するユーザーの操作が範囲内のモデルに影響するようになります。 これは実際、単一のソースだけでその範囲モデルが作成されます。

ng-repeatのようなディレクティブは、コレクション内の項目毎にDOM要素の複製を作ります。 テンプレートの複製が必要になるのはコンパイル時の1度だけなので、コンパイルとリンクのフェーズによってパフォーマンスが向上し、 複製された各インスタンスへのリンクも1度だけ行われます。

ディレクティブ

ディレクティブは、指定したHTML構造がコンパイルプロセス中に遭遇した際に、トリガされるべき振る舞いを決めるものです。 ディレクティブは要素名、属性、クラス名、また、コメントに対しても配置することが可能です。 下記は、全て同じように実行されるng-bindディレクティブの例です。

<span ng-bind="exp"></span>
<span class="ng-bind: exp;"></span>
<ng-bind></ng-bind>
<!-- directive: ng-bind exp -->

ディレクティブは、コンパイル中にそのDOMが遭遇した際に実行される関数に過ぎません。 ディレクティブの書き方についての詳細なドキュメントは、ディレクティブAPIを参照してください。

下記は要素をドラッグ可能にするディレクティブです。 draggable属性が<span>要素上にあることに注目してください。

Source

<!doctype html>
<html ng-app="drag">
  <head>
    <script src="http://code.angularjs.org/1.2.0-rc.2/angular.min.js"></script>
    <script src="script.js"></script>
  </head>
  <body>
    <span draggable>Drag ME</span>
  </body>
</html>
angular.module('drag', []).
  directive('draggable', function($document) {
    return function(scope, element, attr) {
      var startX = 0, startY = 0, x = 0, y = 0;
      element.css({
       position: 'relative',
       border: '1px solid red',
       backgroundColor: 'lightgrey',
       cursor: 'pointer'
      });
      element.on('mousedown', function(event) {
        // Prevent default dragging of selected content
        event.preventDefault();
        startX = event.screenX - x;
        startY = event.screenY - y;
        $document.on('mousemove', mousemove);
        $document.on('mouseup', mouseup);
      });

      function mousemove(event) {
        y = event.screenY - startY;
        x = event.screenX - startX;
        element.css({
          top: y + 'px',
          left:  x + 'px'
        });
      }

      function mouseup() {
        $document.unbind('mousemove', mousemove);
        $document.unbind('mouseup', mouseup);
      }
    }
  });

要素上のdraggable属性の存在は、要素に対して新しい振る舞いを与えます。 このアプローチの素晴らしい所は、我々がブラウザに対して新しいトリックを教えていることです。 我々は、この方法でブラウザが理解出来る単語(用語)を拡張し、こうすることでHTMLに慣れ親しんだ人にとっても馴染みやすいものにしています。

ビューについて

世の中には、多くのテンプレートシステムが存在します。 それらの多くは、静的な文字列のテンプレートとデータを結合し、結果的に新しい文字列を出力します。 こうして作られたテキストが要素内にinnerHTMLされます。

これは、あるデータの変更に対して、テンプレートとの再結合とDOMへのinnerHTMLが必要であることを意味します。 このアプローチでは、ユーザー入力を読み込みデータと結合し、その上書きによるユーザー入力の破壊、更新プロセス全体の管理、 そして表現の欠落といった問題があります。

Angularは違います。 Angularのコンパイラは、文字列テンプレートでは無くディレクティブでDOMを扱います。 結果は、ライブビュー内の範囲モデルの結果と結合した際のリンク関数となります。 ビューと範囲モデルの紐付けは非常に分かりやすいものになっています。 開発者がビューの更新を行う必要ありません。 また、innerHTMLを使用しないため、ユーザー入力を上書きしてしまう心配もありません。 そのため、Angularのディレクティブはテキストだけのバインディングを含めることが出来ませんが、 同様の動作を行う仕組みが用意されています。

Angularは安定したDOM供給のアプローチをとります。 これは、モデル項目インスタンスと紐付いたDOM要素インスタンスが、紐付きの存続期間のために変更されないことを意味します。 コードから、要素の保持と登録されているイベントハンドラを取得出来ること、 テンプレートのデータ結合によって参照先が破壊されていないことを知ることが出来ること、を意味します。

 Back to top

© 2017 Google
Licensed under the Creative Commons Attribution License 3.0.

このページは、ページトップのリンク先のAngularJS公式ドキュメント内のページを翻訳した内容を基に構成されています。 下記の項目を確認し、必要に応じて公式のドキュメントをご確認ください。 もし、誤訳などの間違いを見つけましたら、 @tomofまで教えていただければ幸いです。

  • AngularJSの更新頻度が高いため、元のコンテンツと比べてドキュメントの情報が古くなっている可能性があります。
  • "訳注:"などの断わりを入れた上で、日本人向けの情報やより分かり易くするための追記を行っている事があります。