LoginSignup
4
5

More than 3 years have passed since last update.

【Angular】ComponentでインジェクションするServiceを切り替えられるようにしてみる

Last updated at Posted at 2020-03-28

Angularを触っていて、同じコンポーネントでサービスを切り替えたい時ってありますよね?
一般ユーザー向けと管理者用で画面は一緒だけど、中身は変えたいとか。

サービスにisAdminとか持たせちゃいます?
色んな関数でif(isAdmin)とかやっちゃいます?

実はサービスを切り替えられるんです。

Angularプロジェクトを作成する

ng new service-test --routing

画面の追加

コンポーネントの追加

  • サービスからデータを取得して、表示するようにします
src/app/page1/page1.component.ts
import { Component } from '@angular/core';
import { Page1Service } from './page1.service';

@Component({
    selector: 'app-page1',
    template: `
        <h1>type: {{type}}</h1>
        <p>{{data}}</p>
    `,
})
export class Page1Component {
    constructor(private service: Page1Service) { }

    get type() { return this.service.type; }
    get data() { return this.service.getData(); }
}

サービスの追加

  • 一般ユーザー向けサービスを作成します
src/app/page1/page1.service.ts
import { Injectable } from '@angular/core';

@Injectable()
export class Page1Service {
    type = 'general';

    getData() {
        return 'これは一般ユーザー向けです';
    }
}
  • 次に管理者用サービスを作成します
    • Page1Serviceを継承して作成します(継承しなくても変数名やメソッド名が一致していれば問題ないと思います)
    • abstractクラス作るのが一番安全だと思います。
src/app/page1/page1-for-admin.service.ts
import { Injectable } from '@angular/core';
import { Page1Service } from './page1.service';

@Injectable()
export class Page1ForAdminService extends Page1Service {
    type = 'admin';

    getData() {
        return 'これは管理者ユーザー向けです';
    }
}

モジュールの定義

ここが今回の肝です!!!

  • providersでuseFactoryを使って、指定のサービスを別のサービスに差し替えます
    • これによりComponentでインジェクションするServiceが差し変わります
src/app/page1/page1.module.ts
import { NgModule } from '@angular/core';
import { Page1Component } from './page1.component';
import { ActivatedRoute, ActivatedRouteSnapshot } from '@angular/router';
import { Page1Service } from './page1.service';
import { Page1ForAdminService } from './page1-for-admin.service';
import { Page1RoutingModule } from './page1-routing.module';

@NgModule({
    declarations: [Page1Component],
    imports: [Page1RoutingModule],
    providers: [
        {
            // 置換対象のサービス
            provide: Page1Service,
            // 置換
            useFactory: (route: ActivatedRoute) => {
                const getData = (snapshot: ActivatedRouteSnapshot) => snapshot.firstChild ? getData(snapshot.firstChild) : snapshot.data;
                const isAdmin = getData(route.snapshot)?.isAdmin;
                if (isAdmin) {
                    return new Page1ForAdminService();
                }
                return new Page1Service();
            },
            // useFactoryでインジェクションするサービス
            deps: [ActivatedRoute],
        }
    ]
})
export class Page1Module { }

isAdminは後述するルーティングファイルで設定するdata内から取得します

ルーティングの設定

  • Page1Module内のルーティングを定義します
src/app/page1/page1-routing.module.ts
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { Page1Component } from './page1.component';

const routes: Routes = [
    // 一般ユーザー向け
    { path: '', component: Page1Component },
    // 管理者向け
    { path: 'admin', component: Page1Component, data: { isAdmin: true } },
];

@NgModule({
    imports: [RouterModule.forChild(routes)],
    exports: [RouterModule]
})
export class Page1RoutingModule { }

⇒管理者向けの方はdataにisAdminを設定します

  • /page1アクセス時にPage1Moduleを遅延ロードするようにします
src/app/app-routing.module.ts
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

const routes: Routes = [
  { path: 'page1', loadChildren: () => import('./page1/page1.module').then(m => m.Page1Module) },
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

動作確認

image.png

image.png

最後に

こんな感じで簡単?にコンポーネントを共通化しつつ、サービスだけ差し替えることができます。
こうすることで、権限ごとにif文が増えたりとかしなくなるので、割とサービスがシンプルになります。

ファイル数は増えるし、Moduleが複雑になるというデメリットもあるので、使いどころの見極めは必要ですね。

参考

ファクトリープロバイダー: useFactory

4
5
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
4
5