2016-03-09 190 views
72

我有一個ListComponent。在ListComponent中單擊某個項目時,該項目的詳細信息應顯示在DetailComponent中。兩者都同時在屏幕上,所以沒有涉及路由。Angular 2兄弟組件通信

如何告訴DetailComponent ListComponent中的哪個項目被點擊?

我已經考慮發佈一個事件直到父(AppComponent),並讓父母用@Input在DetailComponent上設置了selectedItem.id。或者我可以使用具有可觀察訂閱的共享服務。


編輯:通過事件設置所選項目+ @input不會觸發DetailComponent,不過,如果我是需要執行額外的代碼。所以我不確定這是一個可接受的解決方案。


但是這兩種方法似乎比做事這是無論是通過$ rootScope。$廣播或$範圍。$父。$廣播的角1路要複雜得多。

隨着Angular 2中的一切成爲一個組件,我很驚訝沒有關於組件通信的更多信息。

有沒有另外一種/更直接的方法來完成這個?

+0

你有沒有找到任何方式爲兄弟姐妹數據共享?我需要它作爲可觀察的.. –

+1

你找到它的解決方案,我也需要這個 –

回答

1

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

我很驚訝有沒有更多的信息,在那裏有關組件通信 < => consider this tutorial by angualr2

對於同級組件通信,我建議去與sharedService。還有其他選項可用。

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,[]); 

請參考頂部的鏈接以瞭解更多驗證碼。

編輯:這是一個非常小的演示。你已經提到你已經嘗試過sharedService。所以請致電consider this tutorial by angualr2瞭解更多信息。

53

更新到rc.4: 當試圖獲得角2兄弟組件之間傳遞數據,現在(angular.rc.4)最簡單的方法是採取angular2的層次依賴注入的優勢,創造一個共享服務。

這裏將是服務:

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

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

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

現在,這裏將是父組件

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{ 

} 

及其兩個孩子

孩子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; 
    } 
} 

孩子2(這是西布林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 = ''; 
    } 
} 

現在:使用此方法時要注意的事項。

  1. 只包含PARENT組件中的共享服務的服務提供者而不包含子對象的服務提供者。
  2. 您還必須包含構造函數並將服務導入到子項中
  3. 此答案最初是爲早期角度2測試版本回答的。所有改變的都是導入語句,所以如果你偶然使用原始版本,就需要更新。
+0

謝謝,但任何想法爲什麼它不工作時,我們添加服務作爲提供商?我奮鬥了一段時間,並沒有工作,直到我刪除提供商 –

+2

這是仍然有效的角度rc1? – Sergio

+3

雖然我不相信這會通知兄弟姐妹在共享服務中有更新。如果child-component1執行了child-component2需要響應的內容,則此方法不處理該內容。 我相信周圍的方式是與observables? –

2

您需要設置組件之間的父子關係。問題是你可能只需在父組件的構造函數中注入子組件並將其存儲在本地變量中。 相反,您應該使用@ViewChild屬性聲明器在父組件中聲明子組件。 這是你的父母組件應該如何看起來像:

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

當心,子組件將不可用在父組件的構造,只是ngAfterViewInit生命週期後掛鉤被調用。爲了抓住這個鉤子簡單地實現AfterViewInit接口在你父母班,就像你會用OnInit一樣。

但是,也有其他財產的聲明符如本博客註解釋說: http://blog.mgechev.com/2016/01/23/angular2-viewchildren-contentchildren-difference-viewproviders/

17

在2個不同的組件(不是嵌套的組件,父\子\孫子)的情況下,我建議你:

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

來源:Parent and children communicate via a service

+1

希望您在此答案中添加一些術語。我相信它符合RxJS,Observable模式等。不完全確定,但在其中添加描述對於某些人(比如我自己)會有所幫助。謝謝你, – karns

0

我已經從父母傳下來的setter方法,它的一個子通過綁定,要求從該數據的方法子組件,這意味着父組件將被更新,然後可以使用新數據更新其第二個子組件。它確實需要綁定'this'或使用箭頭函數。

這樣做的好處是,孩子們不需要彼此耦合,因爲他們不需要特定的共享服務。

我不完全確定這是最佳實踐,聽到其他人對此的看法很有意思。

0

一種方法是使用一個shared service

但是我發現下面的 解決方案要簡單得多,它允許2個兄弟姐妹之間共享數據(我測試了這個只在角5

在你父組件模板:

<!-- 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

行爲主題。我寫了一個關於這個的blog

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

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

在這個例子中,我聲明瞭一個noid行爲主題的類型編號。它也是可觀察的。如果「發生了什麼」,這將隨着new(){}函數而改變。

因此,在兄弟姐妹的組件中,人們會調用該函數來進行更改,而另一個會受到該更改的影響,反之亦然。

例如,我從URL獲取id並更新行爲主題的noid。

public getId() { 
    const id = +this.route.snapshot.paramMap.get('id'); 
    return id; 
} 

ngOnInit(): void { 
const id = +this.getId(); 
this.taskService.newId(id) 
} 

而從另一面,我可以問,如果該ID是「我什麼都想要」,並做出選擇之後,於我而言,如果我想delte一個任務,該任務是當前網址,它必須重定向我到家裏:

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