angular service input

Angularサービス入力:InputとOutputの違いと使い方

概要

この記事では、AngularにおけるInputとOutputの仕組み、ならびにInjectableサービスの役割について詳しく解説します。これにより、Angularアプリケーションのデータ通信とコンポーネント間のやり取りを理解しやすくします。

InputとOutputの基礎知識

Inputプロパティは親コンポーネントから子コンポーネントへのデータの受け渡しを行う方法です。一方、Outputプロパティは子コンポーネントから親コンポーネントへのイベントの発火手段として機能します。

プロパティ 説明
Input 親から子へのデータ受け渡し
Output 子から親へのイベント通知

Injectableサービスの概要

Injectableサービスは、依存性注入を通じてアプリケーション全体で共有可能なデータや機能を提供します。これにより、コンポーネント間の通信が効率化され、コードの再利用性が向上します。


import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class DataService {
  private messageSource = new BehaviorSubject('初期メッセージ');
  currentMessage = this.messageSource.asObservable();

  changeMessage(message: string) {
    this.messageSource.next(message);
  }
}

InputとOutputの利用シーン

InputとOutputはコンポーネント間の直接的な通信に優れていますが、大規模なアプリケーションではInjectableサービスを使用することで、より柔軟なデータフローを実現することができます。

以下は、InputとOutputを使用したコンポーネントの例です。


@Component({
  selector: '親コンポーネント',
  template: `
    <子コンポーネント [data]="parentData" (notify)="onNotify($event)"></子コンポーネント>
  `
})
export class ParentComponent {
  parentData = '親からのデータ';

  onNotify(event: string) {
    console.log('子からの通知:', event);
  }
}

@Component({
  selector: '子コンポーネント',
  template: `<button>通知を送る</button>`
})
export class ChildComponent {
  @Input() data: string;
  @Output() notify = new EventEmitter();

  sendNotify() {
    this.notify.emit('子が親に送ったメッセージ');
  }
}

このように、AngularのInputとOutputを使用することで、コンポーネント間の情報交換が簡単に行え、動的なアプリケーションの構築が可能になります。

受け渡し方法

Angularでコンポーネント間で値を受け渡すための一般的な方法には、以下のものがあります。

  • Input, Output を使う
  • Service を使う
  • RxJS を使う
  • URL に付与する

Input, Output を使う

Input, Output は、親子コンポーネント間で値を受け渡すために使用されます。親コンポーネントから子コンポーネントに値を渡すには Input を、子コンポーネントから親コンポーネントに値を渡すには Output を使用します。

例:親コンポーネントから子コンポーネントへの値の受け渡し

親コンポーネント (parent.component.ts)

import { Component } from '@angular/core';

@Component({
  selector: 'app-parent',
  template: `
    <app-child 
      [control]="myControl" 
      [placeholder]="myPlaceholder" 
      [labelText]="myLabel">
    </app-child>
  `
})
export class ParentComponent {
  myControl = '初期値';
  myPlaceholder = 'プレースホルダー';
  myLabel = 'ラベル';
}

子コンポーネント (child.component.ts)

import { Component, Input } from '@angular/core';

@Component({
  selector: 'app-child',
  template: `
    <label>{{ labelText }}</label>
    <input type="text" [value]="control" [placeholder]="placeholder" />
  `
})
export class ChildComponent {
  @Input() control: string;
  @Input() placeholder: string;
  @Input() labelText: string;
}

実行結果

親コンポーネントで定義した値を、子コンポーネントの Input プロパティにバインドすることで、子コンポーネントでその値を使用することができます。

例:子コンポーネントから親コンポーネントへの値の受け渡し

親コンポーネント (parent.component.ts)

import { Component } from '@angular/core';

@Component({
  selector: 'app-parent',
  template: `
    <app-child (message)="onMessage($event)"></app-child>
    <p>子コンポーネントからのメッセージ: {{ childMessage }}</p>
  `
})
export class ParentComponent {
  childMessage: string;

  onMessage(message: string) {
    this.childMessage = message;
  }
}

子コンポーネント (child.component.ts)

import { Component, Output, EventEmitter } from '@angular/core';

@Component({
  selector: 'app-child',
  template: `
    <input type="text" #messageInput (keyup)="sendMessage(messageInput.value)" />
  `
})
export class ChildComponent {
  @Output() message = new EventEmitter<string>();

  sendMessage(message: string) {
    this.message.emit(message);
  }
}

実行結果

子コンポーネントでは、EventEmitter を使用してイベントを発行し、親コンポーネントではそのイベントを購読することで、子コンポーネントから値を受け取ることができます。

 Service を使う

Service を使用すると、親子関係にないコンポーネント間でも値を受け渡すことができます。Service は、コンポーネント間で共有されるデータやロジックを保持するために使用されます。

参考資料

サービス (message.service.ts)

import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class MessageService {
  private message: string;

  setMessege(message: string) {
    this.message = message;
  }

  getMessege(): string {
    return this.message;
  }
}

親コンポーネント (parent.component.ts)

import { Component } from '@angular/core';
import { MessageService } from './message.service';

@Component({
  selector: 'app-parent',
  template: `
    <button (click)="sendMessage()">子コンポーネントにメッセージを送信</button>
  `
})
export class ParentComponent {
  constructor(private messageService: MessageService) { }

  sendMessage() {
    this.messageService.setMessege('親コンポーネントからのメッセージです。');
  }
}

子コンポーネント (child.component.ts)

import { Component } from '@angular/core';
import { MessageService } from './message.service';

@Component({
  selector: 'app-child',
  template: `
    <button (click)="showMessage()">メッセージを表示</button>
    <p>{{ message }}</p>
  `
})
export class ChildComponent {
  message: string;

  constructor(private messageService: MessageService) { }

  showMessage() {
    this.message = this.messageService.getMessege();
  }
}

実行結果

親コンポーネントと子コンポーネントは、どちらも同じ MessageService を注入することで、サービスを介して値を共有することができます。

RxJS を使う

RxJS は、リアクティブプログラミングを実現するためのライブラリです。RxJS を使用すると、値の変化を監視し、それに応じて処理を実行することができます。コンポーネント間で値をリアルタイムに受け渡したい場合に便利です。

参考資料

サービス (data.service.ts)

import { Injectable } from '@angular/core';
import { Subject, Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class DataService {
  private dataSubject = new Subject<string>();

  sendData(data: string) {
    this.dataSubject.next(data);
  }

  getData(): Observable<string> {
    return this.dataSubject.asObservable();
  }
}

親コンポーネント (parent.component.ts)

import { Component } from '@angular/core';
import { DataService } from './data.service';

@Component({
  selector: 'app-parent',
  template: `
    <input type="text" #dataInput (keyup)="sendData(dataInput.value)" />
  `
})
export class ParentComponent {
  constructor(private dataService: DataService) { }

  sendData(data: string) {
    this.dataService.sendData(data);
  }
}

子コンポーネント (child.component.ts)

import { Component, OnDestroy } from '@angular/core';
import { DataService } from './data.service';
import { Subscription } from 'rxjs';

@Component({
  selector: 'app-child',
  template: `
    <p>親コンポーネントからのデータ: {{ data }}</p>
  `
})
export class ChildComponent implements OnDestroy {
  data: string;
  subscription: Subscription;

  constructor(private dataService: DataService) {
    this.subscription = this.dataService.getData().subscribe(data => {
      this.data = data;
    });
  }

  ngOnDestroy() {
    this.subscription.unsubscribe();
  }
}

実行結果

親コンポーネントでは、入力値が変更されるたびに DataServicesendData メソッドを呼び出してデータを送信します。子コンポーネントでは、DataServicegetData メソッドから Observable を取得し、購読することで、データの変更をリアルタイムに受け取ることができます。

URL に付与する

URL にパラメータを付与することで、コンポーネントに値を渡すことができます。これは、例えば、ユーザーの詳細情報ページなど、URL に基づいて異なるコンテンツを表示したい場合に便利です。

参考資料

app.module.ts

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { UserComponent } from './user/user.component';

const routes: Routes = [
  { path: 'user/:id', component: UserComponent }
];

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

app.component.html

<a routerLink="/user/1">ユーザー1</a> | 
<a routerLink="/user/2">ユーザー2</a>
<router-outlet></router-outlet>

サービス (user.service.ts)

import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class UserService {
  getUsers() {
    return [
      { id: 1, name: '田中太郎' },
      { id: 2, name: '佐藤花子' }
    ];
  }

  getUser(id: number) {
    return this.getUsers().find(user => user.id === id);
  }
}

ユーザー情報コンポーネント (user.component.ts)

import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { UserService } from '../user.service';

@Component({
  selector: 'app-user',
  template: `
    <h2>ユーザー情報</h2>
    <p>ID: {{ user.id }}</p>
    <p>名前: {{ user.name }}</p>
  `
})
export class UserComponent implements OnInit {
  user: any;

  constructor(
    private route: ActivatedRoute,
    private userService: UserService
  ) { }

  ngOnInit() {
    const id = +this.route.snapshot.paramMap.get('id');
    this.user = this.userService.getUser(id);
  }
}

実行結果

ActivatedRoute を使用することで、URL からパラメータを取得することができます。この例では、ユーザー ID を URL から取得し、UserService を使用してユーザー情報を取得しています。

参考文献

Angularのコンポーネントとイベントの公式ドキュメント

Q&A

Q1: InputとOutputはどのように違いますか?

A1: Inputは親から子へのデータ受け渡しを行い、Outputは子から親へのイベント通知を行います。

Q2: Injectableサービスはどのように利用しますか?

A2: Injectableサービスは依存性注入を通じて、他のコンポーネントやサービスで利用できます。

Q3: InputとOutputの両方を使用する際の最適なパターンは何ですか?

A3: 小規模なコンポーネント間の直接通信にはInputとOutputを使い、大規模なアプリケーションではInjectableサービスを併用するのが最適です。