199

거기에 상응하는 것이 없다.$scope.emit()또는$scope.broadcast()각도로?

나는 알고있다.EventEmitter기능을 제공하지만, 이해하는 한 상위 HTML 요소에 이벤트를 내 보냅니다.

FX 사이에서 의사 소통이 필요한 경우 어떻게해야합니까? 형제 또는 DOM의 루트에있는 구성 요소와 여러 수준으로 중첩 된 요소 사이에 있습니까?


10 답변


353

이에 상응하는 것은 없습니다.$scope.emit()또는$scope.broadcast()AngularJS에서. 구성 요소 내부의 EventEmitter는 가까이에 있지만 앞에서 언급했듯이 바로 위 부모 구성 요소에만 이벤트를 내 보냅니다.

Angular에는 아래에서 설명하려고하는 다른 대안이 있습니다.

@Input () 바인딩을 사용하면 응용 프로그램 모델을 직접 객체 그래프 (루트에서 리프)로 연결할 수 있습니다. 구성 요소의 변경 감지기 전략의 기본 동작은 연결된 구성 요소의 모든 바인딩에 대한 응용 프로그램 모델에 모든 변경 사항을 전파하는 것입니다.

옆으로 : 모델에는보기 모델과 응용 프로그램 모델이라는 두 가지 유형이 있습니다. 응용 프로그램 모델은 @Input () 바인딩을 통해 연결됩니다. 뷰 모델은 구성 요소의 템플릿에 바인딩 된 구성 요소 속성 (@Input ()으로 장식되지 않음)입니다.

귀하의 질문에 대답하십시오 :

형제 구성 요소 간 통신이 필요한 경우 어떻게해야합니까?

  1. 공유 응용 프로그램 모델: 형제는 공유 응용 프로그램 모델 (각도 1과 동일)을 통해 통신 할 수 있습니다. 예를 들어 한 형제가 모델을 변경하면 동일한 모델에 바인딩 된 다른 형제가 자동으로 업데이트됩니다.

  2. 구성 요소 이벤트: 자식 구성 요소는 @Output () 바인딩을 사용하여 부모 구성 요소로 이벤트를 내보낼 수 있습니다. 부모 구성 요소는 이벤트를 처리하고 응용 프로그램 모델 또는 자체 뷰 모델을 조작 할 수 있습니다. 응용 프로그램 모델의 변경 사항은 동일한 모델에 직접 또는 간접적으로 바인딩되는 모든 구성 요소에 자동으로 전파됩니다.

  3. 서비스 이벤트: 구성 요소는 서비스 이벤트를 구독 할 수 있습니다. 예를 들어, 두 형제 구성 요소는 동일한 서비스 이벤트에 가입하고 각각의 모델을 수정하여 응답 할 수 있습니다. 이에 대한 자세한 내용은 아래를 참조하십시오.

루트 구성 요소와 여러 수준의 중첩 된 구성 요소 사이에서 통신하려면 어떻게해야합니까?

  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 또는 이벤트 관찰자의 경우 몇 개의 게시물에서 후행 $를 보았습니다 (예 :itemAdded$. RxJS 대회인가요? 이것은 어디서 오는 것입니까? - Mark Rajcok
  • 좋은 대답. '앱 모델 변경 사항은 동일한 모델에 직접 또는 간접적으로 바인딩되는 모든 구성 요소에 자동으로 전파됩니다.' 이런 식으로는 제대로 작동하지 않는다는 직감이 있습니다 (그러나 확실하지는 않습니다). 사빈의 기타블로그 게시물구성 요소를 변경하는 예제를 제공합니다.street응용 프로그램 모델의 속성이지만 Angular 2에서는 ID / 참조에 의한 변경 감지를 구현하므로 변경 사항이 전파되지 않습니다 (onChanges호출되지 않음), 앱 모델 참조가 변경되지 않았기 때문에 (계속 ...) - Mark Rajcok
  • 나는 또한 귀하의 의견에 동의합니다. 이 확장 된 댓글은 다른 사람들에게 도움이 될 것입니다. :) - pixelbits
  • 서비스에서 EventEmitter 대신 Observable을 사용하도록 답변을 업데이트하고자 할 수 있습니다. 만나다stackoverflow.com/a/35568924/215945stackoverflow.com/questions/36076700 - Mark Rajcok
  • eventemitter를 수동으로 구독해서는 안됩니다. 최종 릴리즈에서는 관찰 할 수 없을 수도 있습니다! 이것 좀 봐: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 [새로운 Inject (EventsService)]; } - Beanwah
  • 이 예제에서는 Ionic 2 Framework를 사용하고 있습니다. 정적 매개 변수 메서드는 생성자 메서드가 호출 될 때 호출되며 생성자에 종속성을 제공하는 데 사용됩니다. 설명은 여기에stackoverflow.com/questions/35919593/… - 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 개의 구성 요소로 통신 할 수 없음을 알았습니다.다른 모듈들이 방법으로. 목표를 달성하기 위해 나는 공급자를 추가하여 app.module 레벨에 MessageService를 등록해야했다. 어떤 식 으로든 이것은 정말 멋진 방법입니다. - Rukshan Dangalla

11

사용하지 마세요귀하의 서비스 통신을위한 EventEmitter.

Observable 유형 중 하나를 사용해야합니다. 저는 개인적으로 BehaviorSubject를 좋아합니다.

간단한 예 :

당신은 초기 상태를 전달할 수 있습니다, 여기는 null을 전달합니다.

피사체 = 새로운 BehaviorSubject (null);

제목을 업데이트하려고 할 때

subject.next (myObject)

모든 서비스 또는 구성 요소를 관찰하고 새 업데이트가있을 때 작동합니다.

subject.subscribe (this.YOURMETHOD);

여기에 더 많은 정보가 있습니다..



9

당신이 사용할 수있는EventEmitter 또는observables를 사용하여 DI에 등록하는 eventbus 서비스를 생성하십시오. 참여하려는 모든 구성 요소는 서비스를 생성자 매개 변수로 요청하고 이벤트를 내보내거나 구독합니다.

또한보십시오



2

여기에 펍 서브 샘플을 만들었습니다.

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

제가 가장 좋아하는 방법은 내 하위 구성 요소 모두를 제어하기 위해 내 서비스에서 동작 제목 또는 이벤트 이미 터 (거의 동일한)를 사용하는 것입니다.

각 클립을 사용하여 새 서비스를 만든 다음 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 지시문을 구현했습니다. 이벤트 이미 터를 지정.에 Y 인드하면됩니다.

만나다:https://github.com/atomicbits/angular2-modelchangeobservable

html에서 이벤트 이미 터 (이 예제에서는 countryChanged)를 바인딩하십시오.

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

타이피 스크립트 구성 요소에서 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

서비스 이벤트 : 구성 요소는 서비스 이벤트에 가입 할 수 있습니다. 예를 들어, 두 형제 구성 요소는 동일한 서비스 이벤트에 가입하고 각각의 모델을 수정하여 응답 할 수 있습니다. 이에 대한 자세한 내용은 아래를 참조하십시오.

하지만 부모 구성 요소가 파손되었을 때이를 구독 취소해야합니다.

연결된 질문


최근 질문