Comunicazione del fratello germano angular 2

Ho un componente elenco. Quando un elemento viene cliccato in ListComponent, i dettagli di quell’elemento dovrebbero essere mostrati in DetailComponent. Entrambi sono sullo schermo allo stesso tempo, quindi non è necessario il routing.

Come posso comunicare a DetailComponent quale elemento di ListComponent è stato selezionato?

Ho considerato di emettere un evento fino al genitore (AppComponent), e ho il genitore impostato il SelectedItem.id su DetailComponent con un @Input. Oppure potrei usare un servizio condiviso con abbonamenti osservabili.


EDIT: l’ impostazione dell’elemento selezionato tramite l’evento + @Input non triggers il componente DetailComponent, nel caso in cui dovessi dover eseguire codice aggiuntivo. Quindi non sono sicuro che questa sia una soluzione accettabile.


Ma entrambi questi metodi sembrano molto più complessi del modo Angular 1 di fare cose che erano o tramite $ rootScope. $ Broadcast o $ scope. $ Parent. $ Broadcast.

Poiché tutto in Angular 2 è un componente, sono sorpreso che non ci siano più informazioni sulla comunicazione dei componenti.

C’è un altro / più semplice modo per realizzare questo?

Aggiornato a rc.4: quando si cerca di ottenere dati passati tra componenti di pari livello in angular 2, il modo più semplice in questo momento (angular.rc.4) è quello di sfruttare l’iniezione di dipendenza gerarchica di angular2 e creare un servizio condiviso.

Qui sarebbe il servizio:

import {Injectable} from '@angular/core'; @Injectable() export class SharedService { dataArray: string[] = []; insertData(data: string){ this.dataArray.unshift(data); } } 

Ora, ecco il componente PARENT

 import {Component} from '@angular/core'; import {SharedService} from './shared.service'; import {ChildComponent} from './child.component'; import {ChildSiblingComponent} from './child-sibling.component'; @Component({ selector: 'parent-component', template: ` 

Parent

`, providers: [SharedService], directives: [ChildComponent, ChildSiblingComponent] }) export class parentComponent{ }

e i suoi due figli

bambino 1

 import {Component, OnInit} from '@angular/core'; import {SharedService} from './shared.service' @Component({ selector: 'child-component', template: ` 

I am a child

  • {{data}}
` }) export class ChildComponent implements OnInit{ data: string[] = []; constructor( private _sharedService: SharedService) { } ngOnInit():any { this.data = this._sharedService.dataArray; } }

bambino 2 (È fratello)

 import {Component} from 'angular2/core'; import {SharedService} from './shared.service' @Component({ selector: 'child-sibling-component', template: ` 

I am a child

` }) export class ChildSiblingComponent{ data: string = 'Testing data'; constructor( private _sharedService: SharedService){} addData(){ this._sharedService.insertData(this.data); this.data = ''; } }

ORA: Cose da prendere in considerazione quando si utilizza questo metodo.

  1. Includere solo il fornitore di servizi per il servizio condiviso nel componente PARENT e NON i bambini.
  2. Devi ancora includere costruttori e importare il servizio nei bambini
  3. Questa risposta è stata originariamente risposta per una versione beta 2 angular precoce. Tutto ciò che è cambiato però sono le istruzioni di importazione, quindi è tutto ciò che devi aggiornare se hai usato la versione originale per caso.

In caso di 2 componenti diversi (componenti non nidificati, genitore \ figlio \ grandchild) ti suggerisco questo:

MissionService:

 import { Injectable } from '@angular/core'; import { Subject } from 'rxjs/Subject'; @Injectable() export class MissionService { // Observable string sources private missionAnnouncedSource = new Subject(); private missionConfirmedSource = new Subject(); // Observable string streams missionAnnounced$ = this.missionAnnouncedSource.asObservable(); missionConfirmed$ = this.missionConfirmedSource.asObservable(); // Service message commands announceMission(mission: string) { this.missionAnnouncedSource.next(mission); } confirmMission(astronaut: string) { this.missionConfirmedSource.next(astronaut); } } 

AstronautComponent:

 import { Component, Input, OnDestroy } from '@angular/core'; import { MissionService } from './mission.service'; import { Subscription } from 'rxjs/Subscription'; @Component({ selector: 'my-astronaut', template: ` 

{{astronaut}}: {{mission}}

` }) export class AstronautComponent implements OnDestroy { @Input() astronaut: string; mission = ''; confirmed = false; announced = false; subscription: Subscription; constructor(private missionService: MissionService) { this.subscription = missionService.missionAnnounced$.subscribe( mission => { this.mission = mission; this.announced = true; this.confirmed = false; }); } confirm() { this.confirmed = true; this.missionService.confirmMission(this.astronaut); } ngOnDestroy() { // prevent memory leak when component destroyed this.subscription.unsubscribe(); } }

Fonte: genitori e figli comunicano tramite un servizio

C’è una discussione a riguardo qui.

https://github.com/angular/angular.io/issues/2663

La risposta di Alex J è buona ma non funziona più con l’attuale Angular 4 a partire da luglio 2017.

E questo link plunker dimostrerebbe come comunicare tra fratelli usando il servizio condiviso e osservabile.

https://embed.plnkr.co/P8xCEwSKgcOg07pwDrlO/

Un modo per farlo è usare un servizio condiviso .

Tuttavia trovo molto più semplice la seguente soluzione, consente di condividere i dati tra 2 fratelli (l’ho provato solo su Angular 5 )

Nel modello del componente principale:

     

app-sibling2.component.ts

 import { AppSibling1Component } from '../app-sibling1/app-sibling1.component'; ... export class AppSibling2Component { ... @Input() data: AppSibling1Component; ... } 

È necessario impostare la relazione genitore-figlio tra i componenti. Il problema è che potresti semplicemente iniettare i componenti figlio nel costruttore del componente principale e memorizzarlo in una variabile locale. Invece, dovresti dichiarare i componenti @ViewChild nel tuo componente genitore usando il dichiaratore di proprietà @ViewChild . Ecco come dovrebbe apparire il tuo componente genitore:

 import { Component, ViewChild, AfterViewInit } from '@angular/core'; import { ListComponent } from './list.component'; import { DetailComponent } from './detail.component'; @Component({ selector: 'app-component', template: '', directives: [ListComponent, DetailComponent] }) class AppComponent implements AfterViewInit { @ViewChild(ListComponent) listComponent:ListComponent; @ViewChild(DetailComponent) detailComponent: DetailComponent; ngAfterViewInit() { // afther this point the children are set, so you can use them this.detailComponent.doSomething(); } } 

https://angular.io/docs/ts/latest/api/core/index/ViewChild-var.html

https://angular.io/docs/ts/latest/cookbook/component-communication.html#parent-to-view-child

Attenzione, il componente figlio non sarà disponibile nel costruttore del componente padre, subito dopo il ngAfterViewInit ciclo di vita ngAfterViewInit . Per catturare questo hook, implementa l’interfaccia AfterViewInit nella tua class genitore nello stesso modo in cui faresti con OnInit .

Ma ci sono altri dichiaratori di proprietà come spiegato in questa nota del blog: http://blog.mgechev.com/2016/01/23/angular2-viewchildren-contentchildren-difference-viewproviders/

Soggetti di comportamento. Ho scritto un blog a riguardo.

 import { BehaviorSubject } from 'rxjs/BehaviorSubject'; private noId = new BehaviorSubject(0); defaultId = this.noId.asObservable(); newId(urlId) { this.noId.next(urlId); } 

In questo esempio sto dichiarando un object di comportamento noid di tipo numero. Inoltre è un osservabile. E se “qualcosa è successo” questo cambierà con la funzione new () {}.

Quindi, nei componenti del fratello, si chiamerà la funzione, per effettuare il cambiamento, e l’altro sarà influenzato da quel cambiamento, o viceversa.

Ad esempio, ottengo l’id dall’URL e aggiorno il noid dall’object del comportamento.

 public getId () { const id = +this.route.snapshot.paramMap.get('id'); return id; } ngOnInit(): void { const id = +this.getId (); this.taskService.newId(id) } 

E dall’altra parte, posso chiedere se quell’ID è “cosa voglio sempre” e poi fare una scelta, nel mio caso se voglio deludere un’attività, e quell’attività è l’url corrente, deve reindirmi a casa:

 delete(task: Task): void { //we save the id , cuz after the delete function, we gonna lose it const oldId = task.id; this.taskService.deleteTask(task) .subscribe(task => { //we call the defaultId function from task.service. this.taskService.defaultId //here we are subscribed to the urlId, which give us the id from the view task .subscribe(urlId => { this.urlId = urlId ; if (oldId == urlId ) { // Location.call('/home'); this.router.navigate(['/home']); } }) }) } 

Una direttiva può avere senso in determinate situazioni per connettere i componenti. In effetti, le cose che sono connesse non hanno nemmeno bisogno di essere componenti completi, e talvolta sono più leggeri e in realtà più semplici se non lo sono.

Ad esempio, ho un componente di Youtube Player (che include l’API di YouTube) e volevo alcuni pulsanti per il controller. L’unico motivo per cui i pulsanti non fanno parte del mio componente principale è che si trovano altrove nel DOM.

In questo caso è davvero solo un componente di “estensione” che sarà sempre utile solo con il componente “genitore”. Dico “genitore”, ma nel DOM è un fratello – quindi chiamalo come vuoi.

Come ho detto, non ha nemmeno bisogno di essere un componente completo, nel mio caso è solo un (ma potrebbe essere un componente).

 @Directive({ selector: '[ytPlayerPlayButton]' }) export class YoutubePlayerPlayButtonDirective { player: YoutubePlayerComponent; @Input('ytPlayerVideo') private set player(value: YoutubePlayerComponent) { this.player = value; } @HostListener('click') click() { this.player.play(); } constructor(private elementRef: ElementRef) { // the button itself } } 

Nell’HTML per ProductPage.component , dove youtube-player è ovviamente il mio componente che avvolge l’API di YouTube.

  ... lots more DOM ...  

La direttiva aggancia tutto per me, e non devo dichiarare l’evento (clic) nel codice HTML.

Quindi la direttiva può benissimo connettersi al video player senza dover coinvolgere ProductPage come mediatore.

Questa è la prima volta che lo faccio, quindi non sono ancora sicuro di quanto possa essere scalabile per situazioni molto più complesse. Per questo però sono felice e lascia il mio HTML semplice e le responsabilità di tutto ciò che è distinto.

Questo non è esattamente ciò che vuoi ma di sicuro ti aiuterà

Sono sorpreso che non ci siano più informazioni là fuori sulla comunicazione dei componenti <=> prendi in considerazione questo tutorial di angualr2

Per la comunicazione tra componenti fratelli, suggerirei di andare con sharedService . Ci sono anche altre opzioni disponibili.

 import {Component,bind} from 'angular2/core'; import {bootstrap} from 'angular2/platform/browser'; import {HTTP_PROVIDERS} from 'angular2/http'; import {NameService} from 'src/nameService'; import {TheContent} from 'src/content'; import {Navbar} from 'src/nav'; @Component({ selector: 'app', directives: [TheContent,Navbar], providers: [NameService], template: '' }) export class App { constructor() { console.log('App started'); } } bootstrap(App,[]); 

Si prega di fare riferimento al link in alto per più codice.

Modifica: questa è una demo molto piccola. Hai già detto che hai già provato con sharedService . Quindi per favore guarda questo tutorial di angualr2 per maggiori informazioni.

Ho passato i metodi setter dal genitore a uno dei suoi figli attraverso un’associazione, chiamando quel metodo con i dati dal componente figlio, il che significa che il componente padre viene aggiornato e può quindi aggiornare il suo secondo componente figlio con i nuovi dati. Tuttavia, richiede il binding di “this” o l’utilizzo di una funzione di freccia.

Questo ha il vantaggio che i bambini non sono così accoppiati l’uno all’altro in quanto non hanno bisogno di un servizio condiviso specifico.

Non sono del tutto sicuro che questa sia la pratica migliore, sarebbe interessante ascoltare le opinioni degli altri su questo.