Angular 2同级组件通信

den*_*ard 110 javascript typescript angular

我有一个ListComponent.在ListComponent中单击某个项目时,该项目的详细信息应显示在DetailComponent中.两者都在屏幕上同时出现,因此不涉及路由.

如何告诉DetailComponent单击ListComponent中的哪个项目?

我考虑过将一个事件发送到父(AppComponent),并让父设置在DetailComponent上使用@Input设置selectedItem.id.或者我可以使用具有可观察订阅的共享服务.


编辑:通过事件+ @Input设置所选项目不会触发DetailComponent,但是,如果我需要执行其他代码.所以我不确定这是一个可以接受的解决方案.


但是这两种方法看起来都比Angular 1的做法复杂得多,这种做法要么通过$ rootScope.$ broadcast或$ scope.$ parent.$ broadcast.

由于Angular 2中的所有内容都是组件,我很惊讶没有关于组件通信的更多信息.

有没有其他/更直接的方法来实现这一目标?

Ale*_*x J 63

更新到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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

} 
Run Code Online (Sandbox Code Playgroud)

和它的两个孩子

孩子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;
    }
}
Run Code Online (Sandbox Code Playgroud)

孩子2(这是兄弟姐妹)

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 = '';
    }
}
Run Code Online (Sandbox Code Playgroud)

现在:使用此方法时需要注意的事项.

  1. 仅包括PARENT组件中共享服务的服务提供者而不包括子项.
  2. 您仍然必须包含构造函数并在子项中导入服务
  3. 这个答案最初是针对早期角度2 beta版本而回答的.所有改变的都是import语句,因此如果您偶然使用原始版本,则需要更新所有内容.

  • 我不相信这会通知兄弟姐妹在共享服务中有更新的东西.如果child-component1执行了child-component2需要响应的内容,则此方法不会处理该问题.我相信周围的方式是可观察的? (4认同)
  • 这已经过时了.`指令`不再在组件中声明. (3认同)
  • 这对于angular-rc1仍然有效吗? (2认同)

Dud*_*udi 26

如果有2个不同的组件(不是嵌套组件,父\ child\grandchild),我建议你:

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

}
Run Code Online (Sandbox Code Playgroud)

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();
  }
}
Run Code Online (Sandbox Code Playgroud)

资料来源:家长和孩子通过服务进行沟通

  • 希望您在此答案中添加一些术语。我相信它符合 RxJS、Observable 模式等。不完全确定,但对其中一些添加描述对人们(比如我自己)有益。 (2认同)

Can*_*ner 11

一种方法是使用共享服务.

但是我发现以下解决方案更简单,它允许在两个兄弟之间共享数据.(我仅在Angular 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> 
Run Code Online (Sandbox Code Playgroud)

APP-sibling2.component.ts

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

export class AppSibling2Component {
   ...
   @Input() data: AppSibling1Component;
   ...
}
Run Code Online (Sandbox Code Playgroud)


小智 9

这里有一个讨论.

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

Alex J的答案很好,但截至2017年7月,它已不再适用于当前的Angular 4.

这个plunker链接将演示如何使用共享服务和可观察的兄弟姐妹之间进行通信.

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


Sim*_*ver 5

指令在某些情况下可以“连接”组件。实际上,连接的东西甚至不需要是完整的组件,有时候,如果不是的话,它会更轻巧,实际上更简单。

例如,我有一个Youtube Player组件(包装Youtube API),我想要一些控制器按钮。这些按钮不属于我的主要组件的唯一原因是它们位于DOM中的其他位置。

在这种情况下,它实际上只是一个“扩展”组件,只能与“父”组件一起使用。我说“父母”,但在DOM中是兄弟姐妹-随便叫它。

就像我说的那样,它甚至不必是一个完整的组件,就我而言,它只是一个<button>(但它可以是一个组件)。

@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
   }
}
Run Code Online (Sandbox Code Playgroud)

在的HTML中ProductPage.componentyoutube-player显然是包装Youtube API的组件在哪里。

<youtube-player #technologyVideo videoId='NuU74nesR5A'></youtube-player>

... lots more DOM ...

<button class="play-button"        
        ytPlayerPlayButton
        [ytPlayerVideo]="technologyVideo">Play</button>
Run Code Online (Sandbox Code Playgroud)

该指令为我提供了一切,而我不必在HTML中声明(click)事件。

因此,该指令可以很好地连接到视频播放器,而不必参与ProductPage调解器。

这是我第一次真正执行此操作,因此尚不确定在更复杂的情况下它的可伸缩性。为此,尽管我很高兴,但它却使我的HTML变得简单,而所有事情的职责却截然不同。


Nic*_*ves 5

对于这个问题,共享服务是一个很好的解决方案。如果您也想存储一些活动信息,您可以将共享服务添加到您的主模块 (app.module) 提供程序列表中。

@NgModule({
    imports: [
        ...
    ],
    bootstrap: [
        AppComponent
    ],
    declarations: [
        AppComponent,
    ],
    providers: [
        SharedService,
        ...
    ]
});
Run Code Online (Sandbox Code Playgroud)

然后你可以直接将它提供给你的组件,

constructor(private sharedService: SharedService)
 
Run Code Online (Sandbox Code Playgroud)

使用共享服务,您可以使用函数,也可以创建一个主题来一次更新多个位置。

@Injectable()
export class SharedService {
    public clickedItemInformation: Subject<string> = new Subject(); 
}
Run Code Online (Sandbox Code Playgroud)

在您的列表组件中,您可以发布点击的项目信息,

this.sharedService.clikedItemInformation.next("something");
Run Code Online (Sandbox Code Playgroud)

然后您可以在详细信息组件中获取此信息:

this.sharedService.clikedItemInformation.subscribe((information) => {
    // do something
});
Run Code Online (Sandbox Code Playgroud)

显然,列出组件共享的数据可以是任何东西。希望这可以帮助。


mic*_*yks 2

这不是您真正想要的,但肯定会帮助您

我很惊讶没有更多关于组件通信的信息<=> 考虑 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,[]);
Run Code Online (Sandbox Code Playgroud)

请参阅顶部的链接以获取更多代码。

编辑:这是一个非常小的演示。您已经提到您已经尝试过sharedService. 因此,请考虑 angualr2 的本教程以获取更多信息。