199

それに相当するものはないか?$scope.emit()または$scope.broadcast()角度で?

私は知っているEventEmitter私が理解する限り、親HTML要素にイベントを送出するだけであることは理解しています。

fxとの間で通信が必要な場合はどうすればいいですか? DOMのルートにあるコンポーネントといくつかのレベルのネストされた要素の間に深いところがありますか?


  • 私はdomの任意のポイントからアクセスできるダイアログコンポーネントを作成することに関連する同様の質問を持っていました:stackoverflow.com/questions/34572539/…基本的には、イベントエミッタをサービスに配置することです - brando
  • ここでは、サブスクリプション時にn番目の最後の値を取得できるようにするRXJSを使用したそのようなサービスの実装です。幸いです。 - Codewarrior

10 답변


353

同等物はありません$scope.emit()または$scope.broadcast()AngularJSから。 コンポーネント内のEventEmitterは近づいてきますが、前述したように、直接の親コンポーネントにイベントを送信します。

Angularでは、以下で説明しようとする他の選択肢があります。

@Input()バインディングを使用すると、アプリケーションモデルを有向オブジェクトグラフ(ルートからリーフ)に接続できます。コンポーネントの変更検出器戦略のデフォルトの動作は、接続されているすべてのコンポーネントからのすべてのバインディングのすべての変更をアプリケーションモデルに伝播することです。

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

あなたの質問に答えるには:

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

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

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

  3. サービスイベント:コンポーネントはサービスイベントをサブスクライブできます。たとえば、2つの兄弟コンポーネントは、同じサービスイベントにサブスクライブし、それぞれのモデルを変更して応答することができます。もっと詳しくはこちら。

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

  1. 共有アプリケーションモデル:アプリケーションモデルは、ルートコンポーネントから深くネストされたサブコンポーネントに@Input()バインディングを介して渡すことができます。どのコンポーネントからのモデルへの変更も、同じモデルを共有するすべてのコンポーネントに自動的に反映されます。
  2. サービスイベント:EventEmitterを共有サービスに移動することもできます。共有サービスを使用すると、コンポーネントにサービスを注入してイベントに登録することができます。このようにして、Rootコンポーネントはサービスメソッドを呼び出すことができ(通常はモデルを変更する)、イベントを送出します。いくつかのレイヤーがダウンしています。同じコンポーネントにサービスを注入し、同じイベントを購読している子コンポーネントです。共有アプリケーションモデルを変更するイベントハンドラは、それに依存するすべてのコンポーネントに自動的に伝播します。これはおそらく、$scope.broadcast()次のセクションでは、このアイデアについて詳しく説明します。

サービスイベントを使用して変更を伝播する観察可能なサービスの例

次に、サービスイベントを使用して変更を伝播する観測可能なサービスの例を示します。 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));
    }
}

参照:角の変化の検出


  • 観察可能なEventEmitterや、EventEmitterのためのいくつかの投稿の末尾に$があることを確認しました。itemAdded$。それはRxJSのコンベンションなのでしょうか?これはどこから来たのですか? - Mark Rajcok
  • いい答えだ。 [App Modelの変更は、同じモデルに直接的または間接的にバインドするすべてのコンポーネントに自動的に反映されます。私はそれがかなりうまく動作していないと思っています(しかしわかりません)。サヴィンの他のブログ投稿コンポーネントを変更する例を示しますstreetアプリケーションモデルのプロパティですが、角度2ではID /参照による変更検出が実装されているため、変更は反映されません(onChangesアプリケーションモデル参照が変更されていないため(cont ...) - Mark Rajcok
  • 私はあなたのコメントにも同意します。これらの拡張されたコメントは他の人に役立つと確信しています:) - pixelbits
  • サービス内のEventEmitterの代わりにObservableを使用するように答えを更新することができます。見るstackoverflow.com/a/35568924/215945そしてstackoverflow.com/questions/36076700 - Mark Rajcok
  • イベントエミッタを手動で購読する必要はありません。最終リリースでは観測可能ではないかもしれません!これを見てください:bennadel.com/blog/… - NetProvoke

44

次のコードは、$ scope.emit()または$ scope.broadcast()角度2で共有サービスイベントを処理する。

import {Injectable} from 'angular2/core';
import * as Rx from 'rxjs/Rx';

@Injectable()
export class EventsService {
    constructor() {
        this.listeners = {};
        this.eventsSubject = new Rx.Subject();

        this.events = Rx.Observable.from(this.eventsSubject);

        this.events.subscribe(
            ({name, args}) => {
                if (this.listeners[name]) {
                    for (let listener of this.listeners[name]) {
                        listener(...args);
                    }
                }
            });
    }

    on(name, listener) {
        if (!this.listeners[name]) {
            this.listeners[name] = [];
        }

        this.listeners[name].push(listener);
    }

    broadcast(name, ...args) {
        this.eventsSubject.next({
            name,
            args
        });
    }
}

使用例:

放送:

function handleHttpError(error) {
    this.eventsService.broadcast('http-error', error);
    return ( Rx.Observable.throw(error) );
}

リスナー:

import {Inject, Injectable} from "angular2/core";
import {EventsService}      from './events.service';

@Injectable()
export class HttpErrorHandler {
    constructor(eventsService) {
        this.eventsService = eventsService;
    }

    static get parameters() {
        return [new Inject(EventsService)];
    }

    init() {
        this.eventsService.on('http-error', function(error) {
            console.group("HttpErrorHandler");
            console.log(error.status, "status code detected.");
            console.dir(error);
            console.groupEnd();
        });
    }
}

それは複数の引数をサポートすることができます:

this.eventsService.broadcast('something', "Am I a?", "Should be b", "C?");

this.eventsService.on('something', function (a, b, c) {
   console.log(a, b, c);
});


  • これは何をするのですか? static get parameters(){return [新しいインジェクション(EventsService)]; } - Beanwah
  • この例では、Ionic 2 Frameworkを使用しています。静的パラメータメソッドは、コンストラクタメソッドが呼び出され、コンストラクタに依存関係を提供するために使用されるときに呼び出されます。ここでの説明 - jim.taylor.1974
  • うまくやった。シンプルで、ワンオフだけでなく、アプリ全体に簡単に適応可能な通知システムを提供します。 - Mike M
  • 私は、ワイルドカードをサポートして同様のサービスを作成しました。それが役に立てば幸い。github.com/govorov/ng-radio - Stanislav E. Govorov
  • 恐ろしい、それを使用して、もはや関心がある場合オフ機能を追加:off(name, listener) { this.listeners[name] = this.listeners[name].filter(x => x != listener); } - LVDM

15

私はrxjsをラップするメッセージサービスを使用していますSubject(タイプスクリプト)

プランカの例:メッセージサービス

import { Injectable } from '@angular/core';
import { Subject } from 'rxjs/Subject';
import { Subscription } from 'rxjs/Subscription';
import 'rxjs/add/operator/filter'
import 'rxjs/add/operator/map'

interface Message {
  type: string;
  payload: any;
}

type MessageCallback = (payload: any) => void;

@Injectable()
export class MessageService {
  private handler = new Subject<Message>();

  broadcast(type: string, payload: any) {
    this.handler.next({ type, payload });
  }

  subscribe(type: string, callback: MessageCallback): Subscription {
    return this.handler
      .filter(message => message.type === type)
      .map(message => message.payload)
      .subscribe(callback);
  }
}

コンポーネントはイベントを購読しブロードキャストできます(送信者)。

import { Component, OnDestroy } from '@angular/core'
import { MessageService } from './message.service'
import { Subscription } from 'rxjs/Subscription'

@Component({
  selector: 'sender',
  template: ...
})
export class SenderComponent implements OnDestroy {
  private subscription: Subscription;
  private messages = [];
  private messageNum = 0;
  private name = 'sender'

  constructor(private messageService: MessageService) {
    this.subscription = messageService.subscribe(this.name, (payload) => {
      this.messages.push(payload);
    });
  }

  send() {
    let payload = {
      text: `Message ${++this.messageNum}`,
      respondEvent: this.name
    }
    this.messageService.broadcast('receiver', payload);
  }

  clear() {
    this.messages = [];
  }

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

(受信機)

import { Component, OnDestroy } from '@angular/core'
import { MessageService } from './message.service'
import { Subscription } from 'rxjs/Subscription'

@Component({
  selector: 'receiver',
  template: ...
})
export class ReceiverComponent implements OnDestroy {
  private subscription: Subscription;
  private messages = [];

  constructor(private messageService: MessageService) {
    this.subscription = messageService.subscribe('receiver', (payload) => {
      this.messages.push(payload);
    });
  }

  send(message: {text: string, respondEvent: string}) {
    this.messageService.broadcast(message.respondEvent, message.text);
  }

  clear() {
    this.messages = [];
  }

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

subscribe方法MessageServicerxjsを返すSubscriptionオブジェクトは、以下のように登録解除することができます:

import { Subscription } from 'rxjs/Subscription';
...
export class SomeListener {
  subscription: Subscription;

  constructor(private messageService: MessageService) {
    this.subscription = messageService.subscribe('someMessage', (payload) => {
      console.log(payload);
      this.subscription.unsubscribe();
    });
  }
}

また、この答えを参照してください:https://stackoverflow.com/a/36782616/1861779

プランカの例:メッセージサービス


  • 非常に価値のある。答えをありがとう。私はあなたが2つのコンポーネントで2つのコンポーネントと通信できないことを知りました異なるモジュールこの方法で使用します。目標を達成するために、そこにプロバイダを追加してapp.moduleレベルでMessageServiceを登録しなければなりませんでした。これは本当にクールな方法です。 - Rukshan Dangalla

11

使ってはいけませんあなたのサービスコミュニケーションのためのEventEmitter。

Observable型のいずれかを使用する必要があります。私は個人的にBehaviorSubjectが好きです。

簡単な例:

あなたは初期状態を渡すことができます、ここで私はnullを渡します

letをsubject = new BehaviorSubject(null)とします。

件名を更新したいとき

subject.next(myObject)

任意のサービスまたはコンポーネントから観察し、新しいアップデートを取得したときに行動します。

subject.subscribe(this.YOURMETHOD);

ここに詳しい情報があります。



9

あなたは使うことができますEventEmitterまたはDIを使用して登録するイベントバスサービスを作成します。参加したいすべてのコンポーネントは、コンストラクタパラメータとしてサービスを要求し、イベントを発行および/またはサブスクライブします。

も参照してください



2

私はここにpub-subサンプルを作成しました:

http://www.syntaxsuccess.com/viewarticle/pub-sub-in-angular-2.0

RxJs Subjectを使用してObserverとObservablesをワイヤードアップして、カスタムイベントを発行したり購読したりする一般的なソリューションとして考えています。私のサンプルでは、私はデモ用に顧客オブジェクトを使用しています

this.pubSubService.Stream.emit(customer);

this.pubSubService.Stream.subscribe(customer => this.processCustomer(customer));

ライブデモもあります:http://www.syntaxsuccess.com/angular-2-samples/#/demo/pub-sub


2

私の好きなやり方は、私のサブコンポーネントをすべてコントロールするために、動作のサブジェクトまたはイベントエミッタ(ほぼ同じ)を使用することです。

angle cliを使用して新しいサービスを作成し、BehaviorSubjectまたはEventEmitterを使用します

export Class myService {
#all the stuff that must exist

myString: string[] = [];
contactChange : BehaviorSubject<string[]> = new BehaviorSubject(this.myString);

   getContacts(newContacts) {
     // get your data from a webservices & when you done simply next the value 
    this.contactChange.next(newContacts);
   }
}

そのようにすると、プロバイダとしてサービスを使用するすべてのコンポーネントが変更を認識します。あなたはeventEmitterと同じように結果を購読するだけです;)

export Class myComp {
#all the stuff that exists like @Component + constructor using (private myService: myService)

this.myService.contactChange.subscribe((contacts) => {
     this.contactList += contacts; //run everytime next is called
  }
}


1

独自のコンポーネントでインスタンス化するイベントエミッタを介して、すべてのモデル変更を送信するngModelChange observableディレクティブを実装しました。イベントエミッタをディレクティブにバインドするだけで済みます。

見る:https://github.com/atomicbits/angular2-modelchangeobservable

htmlでは、イベントエミッタをバインドします(この例ではcountryChanged)。

<input [(ngModel)]="country.name"
       [modelChangeObservable]="countryChanged" 
       placeholder="Country"
       name="country" id="country"></input>

typescriptコンポーネントで、EventEmitterでいくつかの非同期操作を実行します。

import ...
import {ModelChangeObservable} from './model-change-observable.directive'


@Component({
    selector: 'my-component',
    directives: [ModelChangeObservable],
    providers: [],
    templateUrl: 'my-component.html'
})

export class MyComponent {

    @Input()
    country: Country

    selectedCountries:Country[]
    countries:Country[] = <Country[]>[]
    countryChanged:EventEmitter<string> = new EventEmitter<string>()


    constructor() {

        this.countryChanged
            .filter((text:string) => text.length > 2)
            .debounceTime(300)
            .subscribe((countryName:string) => {
                let query = new RegExp(countryName, 'ig')
                this.selectedCountries = this.countries.filter((country:Country) => {
                    return query.test(country.name)
                })
            })
    }
}


1

これは私のバージョンです:

export interface IEventListenr extends OnDestroy{
    ngOnDestroy(): void
}

@Injectable()
export class EventManagerService {


    private listeners = {};
    private subject = new EventEmitter();
    private eventObserver = this.subject.asObservable();


    constructor() {

        this.eventObserver.subscribe(({name,args})=>{



             if(this.listeners[name])
             {
                 for(let listener of this.listeners[name])
                 {
                     listener.callback(args);
                 }
             }
        })

    }

    public registerEvent(eventName:string,eventListener:IEventListenr,callback:any)
    {

        if(!this.listeners[eventName])
             this.listeners[eventName] = [];

         let eventExist = false;
         for(let listener of this.listeners[eventName])
         {

             if(listener.eventListener.constructor.name==eventListener.constructor.name)
             {
                 eventExist = true;
                 break;
             }
         }

        if(!eventExist)
        {
             this.listeners[eventName].push({eventListener,callback});
        }
    }

    public unregisterEvent(eventName:string,eventListener:IEventListenr)
    {

        if(this.listeners[eventName])
        {
            for(let i = 0; i<this.listeners[eventName].length;i++)
            {

                if(this.listeners[eventName][i].eventListener.constructor.name==eventListener.constructor.name)
                {
                    this.listeners[eventName].splice(i, 1);
                    break;
                }
            }
        }


    }


    emit(name:string,...args:any[])
    {
        this.subject.next({name,args});
    }
}

つかいます:

export class <YOURCOMPONENT> implements IEventListener{

  constructor(private eventManager: EventManagerService) {


    this.eventManager.registerEvent('EVENT_NAME',this,(args:any)=>{
       ....
    })


  }

  ngOnDestroy(): void {
    this.eventManager.unregisterEvent('closeModal',this)
  }

}

放出する:

 this.eventManager.emit("EVENT_NAME");


0

サービスイベント:コンポーネントはサービスイベントを購読できます。たとえば、2つの兄弟コンポーネントは、同じサービスイベントにサブスクライブし、それぞれのモデルを変更して応答することができます。もっと詳しくはこちら。

しかし、親コンポーネントの破棄については、それを解除するようにしてください。

リンクされた質問


最近の質問