10
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

AngularDart入門 第5回 FormatterとService

Last updated at Posted at 2014-10-26

第4回ではデコレーターについて解説しました。今回はフォーマッターとサービスについて説明します。今までハードコーディングしていたレシピのデータをサービスから取得し、フォーマッターを使ってデータをフィルタリングします。

今回の元記事は5. Introducing Formatters and Servicesです。

まずはサンプルアプリを動かしてみよう

こちらにサンプルアプリのソースコードがあります。リポジトリをクローンして、Chapter_05ディレクトリで> pub serveすればサンプルアプリが起動します。

Imgur

レシピを名前で絞り込むフィルターと、カテゴリで絞り込むフィルターが追加されています。

フォーマッターを理解する

組み込みのフォーマッター

AngularDartには便利なフォーマッターがすでに組み込まれています。Currencyフォーマッターは数字を通貨のように整形します。Dateは日付を整形します。その他にも便利なフォーマッターがあります。今回の名前での絞り込みにはFilterフォーマッターを使います。Filterフォーマッターはリストの中からプロパティが指定された値と一致するものをフィルタリングしてくれます。

まずはじめにフィルターに使うモデルをinputに設定します

<input id="name-formatter" type="text" ng-model="nameFilterString">

そしてフォーマッターをng-repeatに与えます。

<ul>
    <li class="pointer" ng-repeat="recipe in recipes | filter:{name:nameFilterString}">
    ...
    </li>
</ul>

最後にコンポーネントにモデルを宣言します

String nameFilterString = "";

独自フォーマッターを作る

組み込みフォーマッターは便利ですが、アプリケーションの中ではもっと目的に特化したフォーマッターが必要になることがあります。今回はレシピをカテゴリで絞り込むためにCategoryFilterという新しいフォーマッターを作成します。

AngularDartのフォーマッターはシンプルな実装からなり、次のcallメソッドを実装するだけで動作します。

class MyFormatter {
  call(valueToFormat, optArg1, ..., optArgN) { ... }
}

第1引数はフォーマッターの対象となるもので、今回はレシピのリストです。残りの引数はオプショナルな引数で、HTML側から与えられます。今回はオプショナルパラメータを1つだけ使い、表示するレシピをフィルタリングします。

List call(recipeList, filterMap) {
    if (recipeList is Iterable && filterMap is Map) {
      // If there is nothing checked, treat it as "everything is checked"
      bool nothingChecked = filterMap.values.every((isChecked) => !isChecked);
      return nothingChecked
          ? recipeList.toList()
          : recipeList.where((i) => filterMap[i.category] == true).toList();
    }
    return const [];
  }

フォーマッターとしてクラスを定義するには@Formatterアノテーションを付け、HTML側での名前を設定します。

@Formatter(name: 'categoryfilter')
class CategoryFilter { ... }

クラスを作ったらモジュールにも忘れずにバインドします。

class MyAppModule extends Module {
  MyAppModule() {
    ...
    bind(CategoryFilter)
    ...
  }
}

HTML側にフィルタリング用のマップのモデルを設定します。

<div>
    <label>Filter recipes by category
        <span ng-repeat="category in categories">
          <label>
            <input type="checkbox"ng-model="categoryFilterMap[category]">
            {{category}}
          </label>
        </span>
    </label>
</div>

独自フォーマッターは組み込みフォーマッターと全く同じように呼び出せます。

<li class="pointer" ng-repeat="recipe in recipes | categoryfilter:categoryFilterMap">

フォーマッターは連結することができます。

<li class="pointer"
    ng-repeat="recipe in recipes | orderBy:'name' | filter:{name:nameFilterString} | categoryfilter:categoryFilterMap">

Httpサービスを使う

今回はレシピとカテゴリのデータを外部のJSONファイルからHTTP通信で読み込んでいます。HTTP通信を使うには組み込みのHttpサービスを使います。

はじめにrecipe-bookコンポーネントからHttpサービスにアクセスできるようにします。コンストラクタにHttp型の引数を取るだけです。AngularDartは型情報を元に実行時に動的に依存性の注入を行ってくれます。モジュールへの変更は必要ありません。

class RecipeBookComponent {
  final Http _http;
  ...
  RecipeBookComponent(this._http) {
    _loadData();
  }
  ...
}

レシピのデータを取得する処理は次のように書きます。

void _loadData() {
  recipesLoaded = false;
  ...
  _http.get('recipes.json')
    .then((HttpResponse response) {
      recipes = response.data.map((d) => new Recipe.fromJson(d)).toList();
      recipesLoaded = true;
    })
    .catchError((e) {
      recipesLoaded = false;
      message = ERROR_MESSAGE;
    });
  ...
}

Httpgetは次のように書きます。Dartの組み込みのFutureを使っているので非同期に実行されます。


_http.get(URL)
  .then((value) {
    // use value
  })
  .catchError((e) {
    // handle error
  });

データの取得は非同期に実行されるので、最初に同期的にモデルを初期化する必要があります。


List categories = [];
List<Recipe> recipes = [];

HTML側ではレシピとカテゴリ両方の取得が終わったかどうかでng-switchを使いビューを切り替えています。ng-switchに与えられた値はng-switch-whenで一致するビューを表示します。

<div recipe-book ng-cloak>
  <div ng-switch="recipesLoaded && categoriesLoaded">
    <div ng-switch-when="false">
      {{message}}
    </div>
    <div ng-switch-when="true">
      <h3>Recipe List</h3>
      ...
    </div>
  </div>
</body>

Angularの機能

ng-cloak

HTMLが読み込まれてからバインディングが始まるまでの時間、{{ }}が見えることを防ぐための仕組みです。ng-cloakが付けられた要素の中身はAngularがレンダリングを終えるまで表示されません。

まとめ

  • フォーマッターはモデルを整形する
  • HTTP通信にはHttpサービスを使う
  • コンストラクタの引数の型をもとにサービスへの依存性が注入される

演習

ここまでの内容が理解できているかどうか、次のことを試してみましょう

  1. レシピの中の“sugar”と“powdered sugar”を“maple syrup”に置き換えるフォーマッターを作ってみましょう。ヒント: String#replaceAll()
  2. レシピの指示中のファーレンハイト温度(degree F)をセルシウス温度(degree C)に変換するフォーマッターを作ってみましょう。変換した値は5の倍数に丸めてください。(例: 300 degrees F は150 Cになります) ヒント: Dartの正規表現を使ってみましょう。
  3. 大きなパーティを開くために、レシピの分量を人数倍にできるようにしましょう。ヒント: まず、レシピのモデルを変更し、Ingredientに量と単位、そして材料の名前の3つのフィールドを追加しなければなりません。次に、ユーザーが分量を2倍、3倍にできるよう、multipilierの入力エリアを追加しましょう。最後に入力された数字を元に分量を倍にするフォーマッターを作成しましょう。
  4. 最後に、1と2のフォーマッターをチェックボックスで切り替えられるようにしてみましょう。

第6回6. Creating Views
を元にバージョン1.0に置き換えて解説します。

10
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?