イベントバインディング - シンプルで双方向通信に最適
Angular 2 では、コンポーネント間でデータを共有する様々な方法があります。兄弟コンポーネント間通信(Sibling Component Communication)は、依存関係のない2つのコンポーネント間でデータをやり取りする方法を指します。
主要な方法
-
イベントバインディング (@Output と @Input):
- @Outputデコレータでイベントを定義し、EventEmitterオブジェクトを返します。
- @Inputデコレータでプロパティを定義し、兄弟コンポーネントから値を受け取ります。
- イベントをエミットすることで、兄弟コンポーネントがそれを受け取り、対応する処理を実行します。
例:
// first-component.ts @Component({ selector: 'first-component', template: ` <button (click)="onClick()">送信</button> <p>{{ message }}</p> ` }) export class FirstComponent { @Output() messageEvent = new EventEmitter<string>(); message = ''; onClick() { this.message = 'メッセージを送信しました'; this.messageEvent.emit(this.message); } } // second-component.ts @Component({ selector: 'second-component', template: ` <h2>受信メッセージ: {{ receivedMessage }}</h2> <first-component (messageEvent)="onMessageReceived($event)"></first-component> ` }) export class SecondComponent { receivedMessage = ''; onMessageReceived(message: string) { this.receivedMessage = message; } }
-
サービス:
- 共通サービスを作成し、コンポーネント間で共有するデータを保持します。
- 兄弟コンポーネントは、サービスをインジェクションし、データにアクセスおよび更新します。
// data.service.ts @Injectable() export class DataService { private message = ''; getMessage() { return this.message; } setMessage(message: string) { this.message = message; } } // first-component.ts constructor(private dataService: DataService) {} onClick() { this.dataService.setMessage('メッセージを送信しました'); } // second-component.ts constructor(private dataService: DataService) {} ngOnInit() { this.receivedMessage = this.dataService.getMessage(); }
-
NgRx:
- NgRx Store と Action を使用して、アプリケーション全体のデータを管理します。
- 兄弟コンポーネントは、Store からデータを購読し、Action を dispatch して Store を更新します。
// app.store.ts import { Injectable } from '@angular/core'; import { createStore } from 'redux'; export interface AppState { message: string; } const initialState: AppState = { message: '', }; const reducer = (state = initialState, action: any) => { switch (action.type) { case 'SEND_MESSAGE': return { ...state, message: action.payload }; default: return state; } }; const store = createStore(reducer); export const MessageActions = { SEND_MESSAGE: '[Message] Send Message', }; @Injectable() export class MessageService { sendMessage(message: string) { store.dispatch({ type: MessageActions.SEND_MESSAGE, payload: message, }); } } // first-component.ts constructor(private messageService: MessageService) {} onClick() { this.messageService.sendMessage('メッセージを送信しました'); } // second-component.ts ngOnInit() { store.subscribe(state => { this.receivedMessage = state.message; }); }
- コンポーネント間共有モジュール: 兄弟コンポーネント間で共有するデータやロジックを格納するモジュールを作成できます。
選択
- シンプルなデータ共有には イベントバインディング が適しています。
Angular 2 兄弟コンポーネント間通信 - サンプルコード
イベントバインディング
first-component.ts
@Component({
selector: 'first-component',
template: `
<button (click)="onClick()">送信</button>
<p>{{ message }}</p>
`
})
export class FirstComponent {
@Output() messageEvent = new EventEmitter<string>();
message = '';
onClick() {
this.message = 'メッセージを送信しました';
this.messageEvent.emit(this.message);
}
}
@Component({
selector: 'second-component',
template: `
<h2>受信メッセージ: {{ receivedMessage }}</h2>
<first-component (messageEvent)="onMessageReceived($event)"></first-component>
`
})
export class SecondComponent {
receivedMessage = '';
onMessageReceived(message: string) {
this.receivedMessage = message;
}
}
data.service.ts
@Injectable()
export class DataService {
private message = '';
getMessage() {
return this.message;
}
setMessage(message: string) {
this.message = message;
}
}
constructor(private dataService: DataService) {}
onClick() {
this.dataService.setMessage('メッセージを送信しました');
}
constructor(private dataService: DataService) {}
ngOnInit() {
this.receivedMessage = this.dataService.getMessage();
}
app.store.ts
import { Injectable } from '@angular/core';
import { createStore } from 'redux';
export interface AppState {
message: string;
}
const initialState: AppState = {
message: '',
};
const reducer = (state = initialState, action: any) => {
switch (action.type) {
case 'SEND_MESSAGE':
return { ...state, message: action.payload };
default:
return state;
}
};
const store = createStore(reducer);
export const MessageActions = {
SEND_MESSAGE: '[Message] Send Message',
};
@Injectable()
export class MessageService {
sendMessage(message: string) {
store.dispatch({
type: MessageActions.SEND_MESSAGE,
payload: message,
});
}
}
constructor(private messageService: MessageService) {}
onClick() {
this.messageService.sendMessage('メッセージを送信しました');
}
ngOnInit() {
store.subscribe(state => {
this.receivedMessage = state.message;
});
}
説明
first-component.ts
で、@Output
デコレータでmessageEvent
イベントを定義し、EventEmitter
オブジェクトを返します。onClick()
メソッドで、message
プロパティに値を設定し、messageEvent
イベントをエミットします。second-component.ts
で、@Input
デコレータでmessageEvent
イベントを受け取り、onMessageReceived()
メソッドで処理します。
data.service.ts
で、DataService
クラスを作成し、message
プロパティとgetMessage()
/setMessage()
メソッドを定義します。first-component.ts
で、DataService
をコンポーネントに注入し、onClick()
メソッドでsetMessage()
メソッドを使用してmessage
プロパティに値を設定します。
app.store.ts
で、AppState
インターフェースとinitialState
オブジェクトを定義します。reducer
関数を作成して、Store の状態を更新します。MessageActions
オブジェクトを作成して、アクションの型を定義します。- `MessageService
Angular 2 兄弟コンポーネント間通信 - その他の方法
前述の主要な方法に加え、Angular 2 で兄弟コンポーネント間通信を実現する方法は他にもいくつかあります。
コンポーネント間共有モジュール
- モジュール内のプロバイダを使用して、コンポーネントで共有されるサービスを注入します。
例
// shared-module.ts
@NgModule({
providers: [
SharedService
]
})
export class SharedModule {}
// shared.service.ts
@Injectable()
export class SharedService {
// 共有するデータやロジックを定義
}
// first-component.ts
import { SharedModule } from '../shared/shared.module';
import { SharedService } from '../shared/shared.service';
@Component({
selector: 'first-component',
template: `
`,
imports: [SharedModule]
})
export class FirstComponent {
constructor(private sharedService: SharedService) {}
// 共有サービスを使用してデータにアクセス/更新
}
// second-component.ts
import { SharedModule } from '../shared/shared.module';
import { SharedService } from '../shared/shared.service';
@Component({
selector: 'second-component',
template: `
`,
imports: [SharedModule]
})
export class SecondComponent {
constructor(private sharedService: SharedService) {}
// 共有サービスを使用してデータにアクセス/更新
}
親コンポーネント経由
- 親コンポーネントを介して兄弟コンポーネント間でデータをやり取りします。
- 親コンポーネントに入力と出力を定義し、兄弟コンポーネントを子コンポーネントとして使用します。
// parent.component.ts
@Component({
selector: 'app-parent',
template: `
<first-component [message]="message" (messageEvent)="onMessageReceived($event)"></first-component>
<second-component [message]="message"></second-component>
`
})
export class ParentComponent {
message = '';
onMessageReceived(message: string) {
this.message = message;
}
}
// first-component.ts
@Component({
selector: 'first-component',
template: `
<button (click)="onClick()">送信</button>
<p>{{ message }}</p>
`,
inputs: ['message']
})
export class FirstComponent {
@Input() message: string;
onClick() {
this.message = 'メッセージを送信しました';
}
}
// second-component.ts
@Component({
selector: 'second-component',
template: `
<h2>受信メッセージ: {{ message }}</h2>
`,
inputs: ['message']
})
export class SecondComponent {
@Input() message: string;
}
RxJS Subjects
- RxJS Subjects を使用して、コンポーネント間でイベントを非同期に発行および購読します。
import { Subject } from 'rxjs';
// first-component.ts
@Component({
selector: 'first-component',
template: `
<button (click)="onClick()">送信</button>
<p>{{ message }}</p>
`
})
export class FirstComponent {
private messageSubject = new Subject<string>();
message = '';
onClick() {
this.message = 'メッセージを送信しました';
this.messageSubject.next(this.message);
}
getMessageSubject() {
return this.messageSubject.asObservable();
}
}
// second-component.ts
@Component({
selector: 'second-component',
template: `
<h2>受信メッセージ: {{ message }}</h2>
`
})
export class SecondComponent {
message = '';
ngOnInit() {
// first-component からのメッセージを購読
const firstComponentMessageSubject = this.firstComponent.getMessageSubject();
firstComponentMessageSubject.subscribe(message => {
this.message = message;
});
}
constructor(private firstComponent: FirstComponent)
javascript angular typescript