目次
今回はサービスでのコンポーネント間の値の受け渡しについてサンプルを紹介していきます。
Angularの公式チュートリアルに、コンポーネントはビューへの値受け渡しに集中し、データの取得や保存はサービスへ移譲すべきであると書かれています。
httpで通信する部分や、取得したデータを成形したりなど、難しいことはサービスに移して、コンポーネントのコードはスッキリさせよう!…というのがAngularが推奨しているアーキテクチャのようです。
このサービスを用いて、値を保持し、コンポーネント同士で値を共有することができます。
普段サービスを作るときは 以下のコマンドで作ることができます
$ ng g service (サービス名)
@Injectable({
providedIn: 'root'
})
このコマンドで作成されたサービスには上記のコードが含まれており、これはアプリ全体で使えるサービスですよ、ということを宣言しています。
アプリ全体で使えるサービスなので、どこのコンポーネントで呼び出しても、同じ値を共有することができます。
簡単なサンプルで値が渡せることを確認してみます。
緑背景部分と黒背景の部分がそれぞれ別コンポーネントになっていて、
フォームに入力した内容を、取得ボタンを押したときにサービスを通じて値を共有できることを確認するサンプルです。
cssについてはbootstrapを使用しているため、割愛します。
$ ng g c service-child
$ ng g c service-child2
$ ng g s service post
コンポーネント名などは好きにつけてくださって大丈夫です
作成したコンポーネントを、app-componentに配置します。
<app-service-child></app-service-child>
<app-service-child2></app-service-child2>
service-childに、入力フォームを配置。
// service-child.component.html
<form (submit)="submit()" class="m-5 bg-light" class="m-5 p-3 bg-info">
<input name="message" class="mr-3" [(ngModel)]="message">
<button type="submit" class="btn btn-primary">保存</button>
</form>
// service-child.component.ts
export class ServiceChildComponent implements OnInit {
message:string;
constructor(private postService:PostService) { }
ngOnInit(): void {}
submit(){
this.postService.setMessage(this.message);
}
}
service-child2に、service-childで入力した文字列を表示します
// service-child2.component.html
<div class="m-5 p-3 bg-secondary">
<p class="text-white">{{ message }}</p>
<button type="button" (click)="getMessage()" class="btn btn-primary">
取得
</button>
</div>
// service-child2.component.ts
export class ServiceChild2Component implements OnInit {
message:string;
constructor(private postService:PostService) { }
ngOnInit(): void {
}
getMessage(){
this.message = this.postService.getMessage();
}
}
サービスには値を共有するためのフィールドを用意。
export class PostService {
private message:string;
constructor() { }
setMessage(message:string){
this.message = message;
}
getMessage(){
return this.message;
}
}
コードは以上です。ng serve を行って確認してみてください。
最初の説明に、コマンドで作ったサービスはアプリ全体で使うことができるサービスであると説明しました。
使用範囲を絞ることもできます。
$ ng g s hoge
// @Injectable({
// providedIn: 'root' // <- コメントアウト
// })
export class HogeService {
constructor() {}
}
@NgModule({
declarations: [・・・],
imports: [・・・],
providers: [HogeService], // HogeModuleにあるコンポーネントでしか使えない
})
export class HogeModule {}
使いたいモジュールのprovidersにセット。
サービスのコメントアウトまではモジュールと同じです。
@Component({
selector: 'app-hoge',
templateUrl: './hoge.component.html',
styleUrls: ['./hoge.component.scss'],
providers: [HogeService], // <- 追加
})
export class HogeComponent implements OnInit {
constructor() {}
ngOnInit(): void {}
}
特定のモジュールでのみ使用できるように設定したサービスを、他のモジュールに設定しようとするとどうなるでしょうか。特にエラーにはならないので使うことはできるみたいです。
ですが、異なるコンポーネントやモジュールで同じサービスをproviderに指定していても、
同じサービスとして値は共有されません。コンポーネントではデストロイされるたびに、
サービスも破棄されているのか、そのコンポーネントが表示されるたびに値は初期化されていました。
モジュールはそのモジュールに所属しているコンポーネント間のみ、値が保持されているようです。
値が共有されないのに、同じサービス名で異なるコンポーネントやモジュールのproviderに使用するのは、バグの元になりそうでとても不親切だなと思いますので、1つのサービスがproviderとして所属する先は必ず1対1にする方が良さそうですね。
だからコマンドで作るサービスはデフォルトがrootになっているのでしょう……🤔