6
3

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.

Angular2 Dart - DI 〜その① 基本編

Last updated at Posted at 2016-08-28

Dependency Injection

今回はAngular2 DartにおけるDependency Injection(DI)についての基本編です。

こちらのエントリーにもあるように今後DI周りは結構修正がありそうです。
Angular Dartのハイレベルロードマップ

現時点では、私たちはDagger2を基礎にした新しいDIとComponent APIを開発中であり、それはDartの強力な静的型解析とレキシカルスコープを活かしたものになる。

今後APIレベルで変更が発生した場合はその都度更新できるように頑張ります。

DI自体の概念等に関しては説明を省きます。
今回のソースはこちら

単純なDI

Angularの世界ではInjectされる側のクラスの事を単にServiceと呼ぶのが通例のようです。

あるComponentに対してServiceをInjectしたい場合。

まずはServiceクラス。Injectableアノテーションの定義が必要です。注意点としては@Injectableではなく@Injectable()()が必ず必要な点です。

app_service.dart
@Injectable()
class SimpleAppService {
  Future<List<String>> getMessages() async => ["message01","message02","message03"];
}

また、上記は文字列をそのままベタ書きしてますがこういうのは基本的に外部ストレージ(RestAPIとかLocalStrage)から取得することが多いので非同期処理にするのが良いとされています。最終的にDartが出力するのはJSですし、JSはシングルスレッドなのでブロッキングが発生するとブラウザが何も操作を受け付けなくなる可能性があるので(Dartもシングルスレッドモデルですが)

次はComponentです。

app_sub.dart
@Component(
    selector: 'app-sub1',
    templateUrl: 'app_sub1.html',
    providers: const[SimpleAppService])
class AppSub1 implements OnInit{

  List<String> messages;

  final SimpleAppService _appService;

  AppSub1(this._appService);

  Future<Null> initMessages() async {
    messages = await _appService.getMessages();
  }

  @override
  void ngOnInit() {
    initMessages();
  }
}

ポイントは

  • Componentアノテーション内のproviders属性
  • コンストラクターインジェクション
  • OnInitとngOnInit

あたりでしょうか。

providers属性

ここにComponent内で使用するServiceクラスを定義することでAngular2がこのComponentを生成するタイミングでInjectしてくれるようになります。また、

const[SimpleAppService]

const [const Provider(SimpleAppService, useClass: SimpleAppService)]

のシンタックスシュガーになります。

コンストラクターインジェクション

DIの世界では様々なInject方法があるのですがAngular2 Dartではコンストラクターインジェクションを提供しています。ですので上記providersにて定義したServiceをコンストラクターにて受け取るための処理が必要になります。

  final SimpleAppService _appService;
  AppSub1(this._appService);

OnInitとngOnInit

OnInitをimplementsしてngOninitを実装し、Serviceから取得したデータでフィールドを初期化します。
コンストラクタ内で行う事も可能ではありますがインスタンスの生成とComponentの初期化というのは別で考えたい場合はインスタンス生成処理以外の事はOnInitをimplementsするのがよいでしょう。

DIのネスト

ある程度のアプリケーションを作成する場合はServiceクラスに別のサービスをInjectさせたい事が出てくると思います(クライアントサイドDDDとか)
その場合には下記のようになります。

app_service.dart
@Injectable()
class NestedAppService {
  RequestService _requestService;
  NestedAppService(this._requestService);
  Future<List<String>> getMessages() async => _requestService.getStrings();
}


@Injectable()
class RequestService {
  Future<List<String>> getStrings() async => ["data01","data02"];
}

NestedAppServiceクラスにRequestServiceをInjectする形です。
RequestSerciceをInjectするNestedAppServiceクラスにはComponentの時と同じようにコンストラクターインジェクションさせるようにします。

Component側はprovidersの定義が2つになります(NestedAppService, RequestService)
この時にRequestServiceの定義を忘れるとエラーになります。

@Component(
    selector: 'app-sub2',
    templateUrl: 'app_sub2.html',
    providers: const[NestedAppService, RequestService])
class AppSub2 implements OnInit{

  List<String> messages;
  final NestedAppService _appService;
  AppSub2(this._appService);

  Future<Null> initMessages() async {
    messages = await _appService.getMessages();
  }

  @override
  void ngOnInit() {
    initMessages();
  }
}

まとめ(雑感)

DIかつフィールドインジェクションに慣れているとこのコンストラクターインジェクションと定義の方法にはちょっと面倒臭さを感じてしまうかもしれません。このあたりはDartというかJavaScript実行環境を考慮してのアーキテクチャーなんでしょうね。今後もっと簡潔かつわかりやすい定義になってれるといいなぁと思います。

次回

次回はServiceの一括定義と実装の切り替えに関して書いていこうと思います。

6
3
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
6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?