1. ホーム
  2. アンギュラー

[解決済み】Angularのグローバルイベント

2022-04-05 11:56:53

質問

に相当するものはないのでしょうか? $scope.emit() または $scope.broadcast() をAngularで使うか?

私は EventEmitter という機能がありますが、私の理解では、それは単に親HTML要素にイベントを発するだけです。

fx.の兄弟間や、DOMのルートにあるコンポーネントと数レベル深いネストにある要素との間で通信する必要がある場合はどうすればよいですか?

どのように解決するのですか?

に相当するものはありません。 $scope.emit() または $scope.broadcast() をAngularJSから取得しました。 コンポーネント内のEventEmitterは近いですが、あなたが言ったように、それは直接の親コンポーネントにのみイベントを発行します。

Angularでは、他の選択肢もありますが、以下に説明したいと思います。

@Input()バインディングでは、アプリケーションモデルを有向オブジェクトグラフ(ルートからリーフへ)で接続することができます。コンポーネントの変更検出ストラテジーのデフォルトの動作は、接続された任意のコンポーネントからのすべてのバインディングに対して、アプリケーションモデルへのすべての変更を伝搬させることです。

余談ですが モデルには2種類あります。ビューモデルとアプリケーションモデルです。 アプリケーションモデルは、@Input()バインディングによって接続されます。ビューモデルは、コンポーネントのプロパティ(@Input()で装飾されていない)であり、コンポーネントのテンプレートにバインドされているだけです。

質問にお答えします。

兄弟コンポーネント間で通信する必要がある場合はどうすればよいですか?

  1. 共有アプリケーションモデル : 兄弟は共有アプリケーションモデルを介して通信することができます(ちょうどangular 1のような)。例えば、ある兄弟がモデルに変更を加えると、同じモデルへのバインディングを持つ他の兄弟が自動的に更新されます。

  2. コンポーネントイベント : 子コンポーネントは、@Output()バインディングを使用して親コンポーネントにイベントを発行することができます。 親コンポーネントはそのイベントを処理し、アプリケーションモデルまたは自身のビューモデルを操作することができます。 アプリケーションモデルの変更は、同じモデルに直接または間接的にバインドしているすべてのコンポーネントに自動的に伝搬されます。

  3. サービスイベント : コンポーネントは、サービスイベントを購読することができます。例えば、2つの兄弟コンポーネントが同じサービスイベントをサブスクライブし、それぞれのモデルを修正することによって応答することができます。詳細は後述します。

Rootコンポーネントと数レベルネストされたコンポーネントの間で通信するにはどうすればよいですか?

  1. アプリケーションモデルの共有 : アプリケーションモデルは、@Input()バインディングを通して、ルートコンポーネントから深くネストされたサブコンポーネントまで渡すことができます。任意のコンポーネントからのモデルへの変更は、同じモデルを共有するすべてのコンポーネントに自動的に伝搬されます。
  2. サービスイベント : また、EventEmitterを共有サービスに移動させ、任意のコンポーネントがサービスをインジェクトしてイベントを購読できるようにすることもできます。この方法では、ルートコンポーネントがサービスメソッド(通常はモデルの変更)を呼び出し、イベントを発生させることができる。 数層下の孫コンポーネントもサービスをインジェクトし、同じイベントをサブスクライブしているので、それを処理することができます。 共有されたアプリケーションモデルを変更するイベントハンドラは、それに依存するすべてのコンポーネントに自動的に伝搬されます。これはおそらく $scope.broadcast() Angular 1から採用されました。次のセクションでは、このアイデアをより詳細に説明します。

変更を伝えるためにサービスイベントを使用するObservableサービスの例

以下は、サービスイベントを使用して変更を伝達する、観測可能なサービスの例です。TodoItem が追加されると、サービスは、そのコンポーネントの購読者に通知するイベントを発行します。

export class TodoItem {
    constructor(public name: string, public done: boolean) {
    }
}
export class TodoService {
    public itemAdded$: EventEmitter<TodoItem>;
    private todoList: TodoItem[] = [];

    constructor() {
        this.itemAdded$ = new EventEmitter();
    }

    public list(): TodoItem[] {
        return this.todoList;
    }

    public add(item: TodoItem): void {
        this.todoList.push(item);
        this.itemAdded$.emit(item);
    }
}

以下は、ルートコンポーネントがイベントを購読する方法です。

export class RootComponent {
    private addedItem: TodoItem;
    constructor(todoService: TodoService) {
        todoService.itemAdded$.subscribe(item => this.onItemAdded(item));
    }

    private onItemAdded(item: TodoItem): void {
        // do something with added item
        this.addedItem = item;
    }
}

数レベルネストされた子コンポーネントも、同じようにイベントを購読します。

export class GrandChildComponent {
    private addedItem: TodoItem;
    constructor(todoService: TodoService) {
        todoService.itemAdded$.subscribe(item => this.onItemAdded(item));
    }

    private onItemAdded(item: TodoItem): void {
        // do something with added item
        this.addedItem = item;
    }
}

イベントをトリガーするサービスを呼び出すコンポーネントです(コンポーネントツリーのどこにあってもかまいません)。

@Component({
    selector: 'todo-list',
    template: `
         <ul>
            <li *ngFor="#item of model"> {{ item.name }}
            </li>
         </ul>
        <br />
        Add Item <input type="text" #txt /> <button (click)="add(txt.value); txt.value='';">Add</button>
    `
})
export class TriggeringComponent{
    private model: TodoItem[];

    constructor(private todoService: TodoService) {
        this.model = todoService.list();
    }

    add(value: string) {
        this.todoService.add(new TodoItem(value, false));
    }
}

参考 Angularの変更検出