2016-03-09 47 views
72

Possiedo 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.Angolare 2 Comunicazione componente fratello singolo

Come si comunica a DetailComponent quale elemento in ListComponent è stato selezionato?

Ho preso in considerazione l'emissione di un evento fino al parent (AppComponent) e il padre ha impostato l'oggetto selected.tem su DetailComponent con un @Input. Oppure potrei usare un servizio condiviso con abbonamenti osservabili.


EDIT: Impostazione della voce selezionata tramite evento + @Input non attiva il DetailComponent, però, nel caso in cui dovessi avere bisogno di eseguire codice aggiuntivo. Quindi non sono sicuro che questa sia una soluzione accettabile.


Ma entrambi questi metodi sembrano molto più complessa di quella angolare 1 modo di fare le cose che era sia attraverso $ rootScope. $ Trasmissione o $ portata. $ Genitore. $ 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?

+0

Hai trovato un modo per condividere i dati di pari livello? Ne ho bisogno come osservabile .. –

+1

trovi la soluzione per questo, ho bisogno anche di questo –

risposta

1

This is not what you exactly want but for sure will help you out

Mi sorprende che non c'è più informazioni là fuori sulla comunicazione componente < => consider this tutorial by angualr2

per il fratello germano di comunicazione componenti, io 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: '<navbar></navbar><thecontent></thecontent>' 
}) 


export class App { 
    constructor() { 
    console.log('App started'); 
    } 
} 

bootstrap(App,[]); 

Fare riferimento al collegamento in alto per ulteriori codici.

Modifica: Questa è una demo molto piccola. Hai già detto che hai già provato con sharedService. Quindi, per favore, per favore, consider this tutorial by angualr2 per maggiori informazioni.

53

aggiornato per rc.4: Quando si cerca di ottenere i dati passati tra i componenti di pari livello in angolare 2, Il modo più semplice in questo momento (angular.rc.4) è quello di approfittare di iniezione di dipendenza gerarchica del 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, qui sarebbe il componente principale

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: ` 
     <h1>Parent</h1> 
     <div> 
      <child-component></child-component> 
      <child-sibling-component></child-sibling-component> 
     </div> 
    `, 
    providers: [SharedService], 
    directives: [ChildComponent, ChildSiblingComponent] 
}) 
export class parentComponent{ 

} 

ed i suoi due figli

bambino 1

import {Component, OnInit} from '@angular/core'; 
import {SharedService} from './shared.service' 

@Component({ 
    selector: 'child-component', 
    template: ` 
     <h1>I am a child</h1> 
     <div> 
      <ul *ngFor="#data in data"> 
       <li>{{data}}</li> 
      </ul> 
     </div> 
    ` 
}) 
export class ChildComponent implements OnInit{ 
    data: string[] = []; 
    constructor(
     private _sharedService: SharedService) { } 
    ngOnInit():any { 
     this.data = this._sharedService.dataArray; 
    } 
} 

bambino 2 (È siblin g)

import {Component} from 'angular2/core'; 
import {SharedService} from './shared.service' 

@Component({ 
    selector: 'child-sibling-component', 
    template: ` 
     <h1>I am a child</h1> 
     <input type="text" [(ngModel)]="data"/> 
     <button (click)="addData()"></button> 
    ` 
}) 
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 provider di servizi per il servizio condiviso nel componente PARENT e NON i figli.
  2. È ancora necessario includere i costruttori e importare il servizio nei bambini
  3. Questa risposta è stata originariamente risposta per una versione beta 2 angolare 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.
+0

Grazie, ma qualche idea del perché non funziona quando aggiungiamo il servizio come fornitori ?? ho lottato con questo per un po 'e non ha funzionato fino a quando non ho rimosso i provider –

+2

E' ancora valido per angular-rc1? – Sergio

+3

Non credo che questo avverta il fratello che qualcosa è stato aggiornato nel servizio condiviso, però. Se child-component1 fa qualcosa a cui child-component2 deve rispondere, questo metodo non lo gestisce. Credo che il modo per aggirare sia con gli osservabili? –

2

È 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. Al contrario, è necessario dichiarare i componenti secondari nel componente principale utilizzando il dichiaratore di proprietà @ViewChild. Questo è come il componente genitore dovrebbe essere simile:

import { Component, ViewChild, AfterViewInit } from '@angular/core'; 
import { ListComponent } from './list.component'; 
import { DetailComponent } from './detail.component'; 

@Component({ 
    selector: 'app-component', 
    template: '<list-component></list-component><detail-component></detail-component>', 
    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 principale, subito dopo il ngAfterViewInit ciclo di vita il gancio è chiamato. Per catturare questo gancio, è sufficiente implementare l'interfaccia AfterViewInit nella classe genitore nello stesso modo in cui si farebbe con OnInit.

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

17

In caso di 2 componenti diversi (componenti non nidificati, genitore \ bambino \ nipote) Io vi suggerisco questo:

MissionService:

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

@Injectable() 

export class MissionService { 
    // Observable string sources 
    private missionAnnouncedSource = new Subject<string>(); 
    private missionConfirmedSource = new Subject<string>(); 
    // 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: ` 
    <p> 
     {{astronaut}}: <strong>{{mission}}</strong> 
     <button 
     (click)="confirm()" 
     [disabled]="!announced || confirmed"> 
     Confirm 
     </button> 
    </p> 
    ` 
}) 
export class AstronautComponent implements OnDestroy { 
    @Input() astronaut: string; 
    mission = '<no mission announced>'; 
    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: Parent and children communicate via a service

+1

Desidero aggiungere una terminologia a questa risposta. Credo che sia in linea con RxJS, pattern osservabile, ecc. Non del tutto sicuro, ma aggiungere delle descrizioni ad alcuni di questi sarebbe vantaggioso per le persone (come me). – karns

0

ho tramandare metodi setter dal genitore a uno dei suoi figli attraverso un legame, chiamando questo metodo con i dati del 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'un l'altro in quanto non hanno bisogno di un servizio condiviso specifico.

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

0

Un modo per farlo è usare un shared service.

Tuttavia trovo la soluzione seguente molto più semplice, che permette di condividere i dati tra 2 fratelli (Ho provato questo solo su angolare 5)

In te genitore modello di componente:.

<!-- Assigns "AppSibling1Component" instance to variable "data" --> 
<app-sibling1 #data></app-sibling1> 
<!-- Passes the variable "data" to AppSibling2Component instance --> 
<app-sibling2 [data]="data"></app-sibling2> 

app-sibling2.component.ts

import { AppSibling1Component } from '../app-sibling1/app-sibling1.component'; 
... 

export class AppSibling2Component { 
    ... 
    @Input() data: AppSibling1Component; 
    ... 
} 
1

Soggetti di comportamento. Ho scritto uno blog a riguardo.

import { BehaviorSubject } from 'rxjs/BehaviorSubject'; 
private noId = new BehaviorSubject<number>(0); 
    defaultId = this.noId.asObservable(); 

newId(urlId) { 
this.noId.next(urlId); 
} 

In questo esempio, sto dichiarando un soggetto di comportamento di tipo numerico. Inoltre è un osservabile.E se "qualcosa è successo" questo cambierà con la funzione new() {}.

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

Ad esempio, ottengo l'id dall'URL e aggiorno il noid dall'oggetto 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 questo ID è "che cosa mai che voglio" e fare una scelta, dopo che, nel mio caso se voglio delte un compito, e questo compito è la corrente url, mi deve reindirizzare 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']); 
       } 
      }) 
    }) 
} 
Problemi correlati