observables - observable angular example




Delegação: EventEmitter ou Observable in Angular (5)

Eu descobri outra solução para este caso sem usar o Reactivex nem os serviços. Na verdade, eu amo a API rxjx, no entanto, acho que ela funciona melhor ao resolver uma função assíncrona e / ou complexa. Usá-lo dessa maneira, é muito superior a mim.

O que eu acho que você está procurando é para uma transmissão. Só isso. E eu descobri esta solução:

<app>
  <app-nav (selectedTab)="onSelectedTab($event)"></app-nav>
       // This component bellow wants to know when a tab is selected
       // broadcast here is a property of app component
  <app-interested [broadcast]="broadcast"></app-interested>
</app>

 @Component class App {
   broadcast: EventEmitter<tab>;

   constructor() {
     this.broadcast = new EventEmitter<tab>();
   }

   onSelectedTab(tab) {
     this.broadcast.emit(tab)
   }    
 }

 @Component class AppInterestedComponent implements OnInit {
   broadcast: EventEmitter<Tab>();

   doSomethingWhenTab(tab){ 
      ...
    }     

   ngOnInit() {
     this.broadcast.subscribe((tab) => this.doSomethingWhenTab(tab))
   }
 }

Este é um exemplo completo de trabalho: https://plnkr.co/edit/xGVuFBOpk2GP0pRBImsE

Estou tentando implementar algo como um padrão de delegação no Angular. Quando o usuário clica em um nav-item , eu gostaria de chamar uma função que emite um evento que, por sua vez, deve ser tratado por algum outro componente que estiver ouvindo o evento.

Aqui está o cenário: Eu tenho um componente de Navigation :

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

@Component({
    // other properties left out for brevity
    events : ['navchange'], 
    template:`
      <div class="nav-item" (click)="selectedNavItem(1)"></div>
    `
})

export class Navigation {

    @Output() navchange: EventEmitter<number> = new EventEmitter();

    selectedNavItem(item: number) {
        console.log('selected nav item ' + item);
        this.navchange.emit(item)
    }

}

Aqui está o componente de observação:

export class ObservingComponent {

  // How do I observe the event ? 
  // <----------Observe/Register Event ?-------->

  public selectedNavItem(item: number) {
    console.log('item index changed!');
  }

}

A questão principal é: como faço para o componente de observação observar o evento em questão?


Se alguém quiser seguir um estilo de programação mais reativo, então definitivamente o conceito de "Tudo é um fluxo" entra em cena e, portanto, use o Observables para lidar com esses fluxos o mais rápido possível.


você pode usar o BehaviourSubject conforme descrito acima ou há mais uma maneira:

você pode manipular o EventEmitter assim: primeiro adicione um seletor

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

@Component({
// other properties left out for brevity
selector: 'app-nav-component', //declaring selector
template:`
  <div class="nav-item" (click)="selectedNavItem(1)"></div>
`
 })

 export class Navigation {

@Output() navchange: EventEmitter<number> = new EventEmitter();

selectedNavItem(item: number) {
    console.log('selected nav item ' + item);
    this.navchange.emit(item)
}

}

Agora você pode lidar com esse evento como suponhamos que observer.component.html seja a visualização do componente Observer

<app-nav-component (navchange)="recieveIdFromNav($event)"></app-nav-component>

depois no ObservingComponent.ts

export class ObservingComponent {

 //method to recieve the value from nav component

 public recieveIdFromNav(id: number) {
   console.log('here is the id sent from nav component ', id);
 }

 }

Atualização 27/06/2016: em vez de usar Observables, use

  • um BehaviorSubject, conforme recomendado por @Abdulrahman em um comentário, ou
  • um ReplaySubject, recomendado por @Jason Goemaat em um comentário

Um Subject é um Observável (para que possamos subscribe() ) e um Observador (para que possamos chamar next() para emitir um novo valor). Nós exploramos esse recurso. Um Assunto permite que os valores sejam multicast para muitos Observadores. Não exploramos esse recurso (temos apenas um observador).

BehaviorSubject é uma variante do Subject. Tem a noção de "o valor atual". Exploramos isso: sempre que criamos um ObservingComponent, ele obtém o valor atual do item de navegação do BehaviorSubject automaticamente.

O código abaixo e o plunker usam BehaviorSubject.

ReplaySubject é outra variante do Subject. Se você quiser esperar até que um valor seja realmente produzido, use ReplaySubject(1) . Enquanto um BehaviorSubject requer um valor inicial (que será fornecido imediatamente), ReplaySubject não. O ReplaySubject sempre fornecerá o valor mais recente, mas como ele não possui um valor inicial necessário, o serviço pode executar algumas operações assíncronas antes de retornar seu primeiro valor. Ele ainda dispara imediatamente nas chamadas subsequentes com o valor mais recente. Se você deseja apenas um valor, use first() na assinatura. Você não precisa cancelar a inscrição se usar first() .

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

@Injectable()
export class NavService {
  // Observable navItem source
  private _navItemSource = new BehaviorSubject<number>(0);
  // Observable navItem stream
  navItem$ = this._navItemSource.asObservable();
  // service command
  changeNav(number) {
    this._navItemSource.next(number);
  }
}
import {Component}    from '@angular/core';
import {NavService}   from './nav.service';
import {Subscription} from 'rxjs/Subscription';

@Component({
  selector: 'obs-comp',
  template: `obs component, item: {{item}}`
})
export class ObservingComponent {
  item: number;
  subscription:Subscription;
  constructor(private _navService:NavService) {}
  ngOnInit() {
    this.subscription = this._navService.navItem$
       .subscribe(item => this.item = item)
  }
  ngOnDestroy() {
    // prevent memory leak when component is destroyed
    this.subscription.unsubscribe();
  }
}
@Component({
  selector: 'my-nav',
  template:`
    <div class="nav-item" (click)="selectedNavItem(1)">nav 1 (click me)</div>
    <div class="nav-item" (click)="selectedNavItem(2)">nav 2 (click me)</div>`
})
export class Navigation {
  item = 1;
  constructor(private _navService:NavService) {}
  selectedNavItem(item: number) {
    console.log('selected nav item ' + item);
    this._navService.changeNav(item);
  }
}

plunker

Resposta original que usa um Observable: (requer mais código e lógica do que usar um BehaviorSubject, por isso não o recomendo, mas pode ser instrutivo)

Então, aqui está uma implementação que usa um Observable em vez de um EventEmitter . Diferentemente da minha implementação EventEmitter, essa implementação também armazena o navItem atualmente selecionado no serviço, para que, quando um componente de observação for criado, ele possa recuperar o valor atual via chamada de API navItem() e ser notificado sobre alterações através do navChange$ Observable .

import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/share';
import {Observer} from 'rxjs/Observer';

export class NavService {
  private _navItem = 0;
  navChange$: Observable<number>;
  private _observer: Observer;
  constructor() {
    this.navChange$ = new Observable(observer =>
      this._observer = observer).share();
    // share() allows multiple subscribers
  }
  changeNav(number) {
    this._navItem = number;
    this._observer.next(number);
  }
  navItem() {
    return this._navItem;
  }
}

@Component({
  selector: 'obs-comp',
  template: `obs component, item: {{item}}`
})
export class ObservingComponent {
  item: number;
  subscription: any;
  constructor(private _navService:NavService) {}
  ngOnInit() {
    this.item = this._navService.navItem();
    this.subscription = this._navService.navChange$.subscribe(
      item => this.selectedNavItem(item));
  }
  selectedNavItem(item: number) {
    this.item = item;
  }
  ngOnDestroy() {
    this.subscription.unsubscribe();
  }
}

@Component({
  selector: 'my-nav',
  template:`
    <div class="nav-item" (click)="selectedNavItem(1)">nav 1 (click me)</div>
    <div class="nav-item" (click)="selectedNavItem(2)">nav 2 (click me)</div>
  `,
})
export class Navigation {
  item:number;
  constructor(private _navService:NavService) {}
  selectedNavItem(item: number) {
    console.log('selected nav item ' + item);
    this._navService.changeNav(item);
  }
}

Plunker

Consulte também o exemplo do Component Interaction Cookbook , que usa um Subject além de observáveis. Embora o exemplo seja "comunicação entre pais e filhos", a mesma técnica é aplicável a componentes não relacionados.


Você pode usar:

  1. Assunto do comportamento:

BehaviorSubject é um tipo de assunto, um assunto é um tipo especial de observável que pode atuar como observável e observador. Você pode assinar mensagens como qualquer outro observável e, após a assinatura, retorna o último valor do assunto emitido pela fonte observável:

Vantagem: Nenhum relacionamento, como o relacionamento pai-filho, é necessário para transmitir dados entre componentes.

SERVIÇO NAV

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

@Injectable()
export class NavService {
  private navSubject$ = new BehaviorSubject<number>(0);

  constructor() {  }

  // Event New Item Clicked
  navItemClicked(navItem: number) {
    this.navSubject$.next(number);
  }

 // Allowing Observer component to subscribe emitted data only
  getNavItemClicked$() {
   return this.navSubject$.asObservable();
  }
}

COMPONENTE DE NAVEGAÇÃO

@Component({
  selector: 'navbar-list',
  template:`
    <ul>
      <li><a (click)="navItemClicked(1)">Item-1 Clicked</a></li>
      <li><a (click)="navItemClicked(2)">Item-2 Clicked</a></li>
      <li><a (click)="navItemClicked(3)">Item-3 Clicked</a></li>
      <li><a (click)="navItemClicked(4)">Item-4 Clicked</a></li>
    </ul>
})
export class Navigation {
  constructor(private navService:NavService) {}
  navItemClicked(item: number) {
    this.navService.navItemClicked(item);
  }
}

OBSERVANDO COMPONENTE

@Component({
  selector: 'obs-comp',
  template: `obs component, item: {{item}}`
})
export class ObservingComponent {
  item: number;
  itemClickedSubcription:any

  constructor(private navService:NavService) {}
  ngOnInit() {

    this.itemClickedSubcription = this.navService
                                      .getNavItemClicked$
                                      .subscribe(
                                        item => this.selectedNavItem(item)
                                       );
  }
  selectedNavItem(item: number) {
    this.item = item;
  }

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

A segunda abordagem é a Event Delegation in upward direction child -> parent

  1. Usando os pais decoradores @Input e @Output que passam dados para o componente filho e o componente pai de notificação filho

por exemplo, Respondida por @Ashish Sharma.





event-delegation