javascript - Angular 2 Kommunikation mellan syskonkomponenter


Jag har en ListComponent. När ett objekt klickas på i ListComponent bör detaljerna för det objektet visas i DetailComponent. Båda är på skärmen samtidigt, så det är ingen routing involverad.

Hur berättar jag för DetailComponent vilken post i ListComponent som klickades på?

Jag har övervägt att skicka en händelse upp till föräldern (AppComponent), och låta föräldern sätta selectedItem.id på DetailComponent med en @Input. Eller så kan jag använda en delad tjänst med observerbara prenumerationer.

REDIGERA: Att ställa in det valda objektet via händelse + @Input triggar dock inte DetailComponent, om jag skulle behöva köra ytterligare kod. Så jag är inte säker på om detta är en acceptabel lösning.

Men båda dessa metoder verkar mycket mer komplexa än sättet att göra saker på i Angular 1, vilket var antingen genom $rootScope.$broadcast eller $scope.$parent.$broadcast.

Med allt i Angular 2 som är en komponent, är jag förvånad över att det inte finns mer information där ute om komponentkommunikation.

Finns det ett annat/mer rakt sätt att åstadkomma detta?

Svar #1

Uppdaterad till rc.4: När man försöker få data skickad mellan syskonkomponenter i Angular 2, är det enklaste sättet just nu (Angular.rc.4) att utnyttja Angular 2:s hierarkiska beroendeinjektion och skapa en delad tjänst.

Här skulle tjänsten vara:

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

@Injectable()
export class SharedService {
    dataArray: string[] = [];

    insertData(data: string){
        this.dataArray.unshift(data);
    }
}

Nu skulle här vara PARENT-komponenten

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{

} 

och dess två barn

barn 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;
    }
}

barn 2 (Det är syskon)

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 = '';
    }
}

NU: Saker att vara uppmärksam på när man använder den här metoden.

  1. Inkludera endast tjänsteleverantören för den delade tjänsten i PARENT-komponenten och INTE i barnen.
  2. Du måste fortfarande inkludera konstruktörer och importera tjänsten i barnen
  3. Detta svar besvarades ursprungligen för en tidig beta-version av Angular 2. Allt som har ändrats är dock importuttalandena, så det är allt du behöver uppdatera om du av en slump använde den ursprungliga versionen.

Svar #2

I fallet med 2 olika komponenter (inte nästlade komponenter, förälder\barn\barnbarn) föreslår jag detta:

Uppdragstjänst:

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);
  }

}

AstronautKomponent:

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();
  }
}

Källa: Förälder och barn kommunicerar via en tjänst

Svar #3

Ett sätt att göra detta är genom att använda en delad tjänst.

Jag tycker dock att följande lösning är mycket enklare, den tillåter att dela data mellan två syskon. (Jag har endast testat detta på Angular 5)

I din föräldrakomponentmall:

<!-- 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;
   ...
}

Relaterade artiklar