第1回では最も単純なAngularDartアプリケーションを作りました。今回はHelloWorld改ということでもう少しAngularDartの機能を使ってみましょう。
今回の元記事は2. Building Something More than Hello Worldです。
まずはサンプルアプリを動かしてみよう
こちらにサンプルアプリのソースコードがあります。リポジトリをクローンして、Chapter_02ディレクトリで> pub serve
すればサンプルアプリが起動します。
Recipe Listのアイテムをクリックすると下にレシピの詳細が表示されます。
スコープを理解する
AngularDartにはスコープ(Scope)という概念があります。スコープはHTMLの要素に対応した入れ子構造になっており、ルートスコープ(Root Scope)が前回説明したng-app
が付いた要素です。ルートスコープは必ず<html>
要素に対して定義されますが、その子になるスコープは自由に定義することができます。ただし、ルートスコープ以外のスコープは必ず親スコープを持ちます。
モデルを理解する
前回のコードに次のようなバインディングがありました。
<input type="text" ng-model="name">
このname
はモデルですが、モデルは常にスコープに紐付いています。前回はルートスコープ以外にスコープがありませんでしたので、このname
はルートスコープ(ng-app
)に紐付いたモデルだったということです。Angularでモデルを扱う場合は、そのモデルがどのスコープに紐付いているのかを把握していなければなりません。
コンポーネントを理解する
コンポーネントというのはスコープを持つオブジェクトです。アプリケーションにコンポーネントをひもづけることで、アプリケーション内に独自のスコープを定義することができます。
コンポーネントを定義するには@Component
アノテーションをクラスに付けます。
@Component(
selector: 'recipe-book',
templateUrl: 'recipe_book.html'
)
class RecipeBookComponent {
@Component
にはいくつかの引数があります。selector
はそのコンポーネントのCSSセレクタを設定します。[recipe-book]
は、html内ではrecipe-book属性として宣言します。宣言には以下の様な種類があります。
-
recipe-book
: DOM要素 -
.recipe-book
: クラス -
[recipe-book]
: DOM属性 -
[attribute=value]
: 条件付きDOM属性 - `:contains(/recipe-book/) : 任意の文字列を含む要素
templateUrl
にはそのコンポーネントのテンプレートとなるHTMLを指定します。記述するUrlは基本的にはpackages/
から始まる相対パスですが後述するtransformerの設定によりファイル名のみに省略できます。
<div><strong>Name: </strong>{{selectedRecipe.name}}</div>
<li ng-click="selectRecipe(recipe)">{{recipe.name}}</li>
publishAs
はhtml内でのコンポーネントのインスタンス名です。publishAs
に設定した名前と同じ名前のgetterを作り、thisを返す必要があります。publishAs
を設定すると、モデルへのアクセスはcmp.selectedRecipe
のようになります。設定しなかった場合はselectedRecipe
とするだけでアクセスできますが、先述の通りスコープは入れ子になっているのでどのスコープに属するモデルなのか明確にするために出来るだけ設定しましょう。
[追記]
publishAsは現在Deprecatedされています。後方互換性のために残されていますが実際の処理は何も行われていません。現在は何もしなくても@Component
のオブジェクトが暗黙のルートスコープになっています。
このようにして作ったコンポーネントはアプリケーションに紐付けなければなりません。モジュールを宣言し、モジュールにコンポーネントを紐付け、そのモジュールをアプリケーションに追加します。
import 'package:angular/angular.dart';
import 'package:angular/application_factory.dart';
import 'package:angular_sample/component/recipe_book.dart';
class MyAppModule extends Module {
MyAppModule() {
bind(RecipeBookComponent);
}
}
void main() {
applicationFactory()
.addModule(new MyAppModule())
.run();
}
アプリケーションにコンポーネントを紐付けたら、HTML内で呼び出しましょう。
<recipe-book></recipe_book>
<recipe-book>
要素の内側がRecipeBookComponentのスコープになります。
[追記]
AngularDart 1系ではControllerという概念は取り払っているので、属性によりScopeを付与して操作するという以前の方法はあまり推奨されません(可能ではある)。なので基本的にはすべてComponent化して、テンプレートHTMLを別ファイルに書きます。
AngularDartを使うためにはangular
パッケージへの依存を記述します。web_componentsはWeb Componentsに対応していない古いブラウザに対するポリフィルのパッケージです。外してもいいですが、その場合は後述のHTML内のweb_components依存部分を削除してください。
[追記]transformerによるHTMLファイルの変換
ComponentのtemplateUrl
に指定するHTMLファイルや、cssUrl
に指定するCSSファイルは基本的にはpackages/angular_sample/component/recipe_book.html
のような相対パスを書く必要がありますが、これはとても面倒なのでAngularDartではpubspecのtransformerを使ってrecipe_book.html
だけで記述できるようにしてあります。
pubspec.yamlのtransformersに次のように追記します。
transformers:
- angular:
html_files:
- lib/component/recipe_book.html
Angularの機能
ng-repeat
ng-repeat
は、反復可能なオブジェクトに対して、テンプレートに当てはめて子要素を追加します。今回の内容がなんとなく理解できていれば、ng-repeat="recipe in cmp.recipes"
がpublishAs:recipe
なコンポーネントの定義であることがわかると思います。
<ul>
<li class="pointer" ng-repeat="recipe in recipes" ng-click="selectRecipe(recipe)">
{{recipe.name}}
</li>
</ul>
ng-click
ng-click
はクリックイベントが起きた時に実行する式を設定できます。
まとめ
- モデルはスコープに属する
- コンポーネントはスコープを持つ
- コンポーネントはアプリケーションに紐付ける
第3回は3. Creating a Custom Component
を元にバージョン1.0に置き換えて解説します。