Javascript 角度2同级组件通信
我有一个列表组件。在ListComponent中单击某个项目时,该项目的详细信息应显示在DetailComponent中。两者同时出现在屏幕上,因此不涉及路由 如何告诉DetailComponent单击了ListComponent中的哪个项目 我考虑向父级(AppComponent)发出一个事件,并让父级在DetailComponent上使用@Input设置selectedItem.id。或者我可以使用具有可观察订阅的共享服务Javascript 角度2同级组件通信,javascript,angular,typescript,Javascript,Angular,Typescript,我有一个列表组件。在ListComponent中单击某个项目时,该项目的详细信息应显示在DetailComponent中。两者同时出现在屏幕上,因此不涉及路由 如何告诉DetailComponent单击了ListComponent中的哪个项目 我考虑向父级(AppComponent)发出一个事件,并让父级在DetailComponent上使用@Input设置selectedItem.id。或者我可以使用具有可观察订阅的共享服务 编辑:通过event+@Input设置所选项目不会触发Detail
编辑:通过event+@Input设置所选项目不会触发DetailComponent,以防我需要执行其他代码。所以我不确定这是一个可以接受的解决方案
但这两种方法似乎都比通过$rootScope.$broadcast或$scope.$parent.$broadcast的角度1方式复杂得多 Angular 2中的所有内容都是一个组件,我很惊讶没有更多关于组件通信的信息 是否有其他/更直接的方法来实现这一点 我很惊讶没有更多关于组件通信的信息 对于兄弟组件通信,我建议使用
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,[]);
从'angular2/core'导入{Component,bind};
从'angular2/platform/browser'导入{bootstrap};
从'angular2/HTTP'导入{HTTP_提供者};
从'src/NameService'导入{NameService};
从'src/content'导入{TheContent};
从'src/nav'导入{Navbar};
@组成部分({
选择器:“应用程序”,
指令:[内容,导航栏],
提供者:[名称服务],
模板:“”
})
导出类应用程序{
构造函数(){
log(“应用程序启动”);
}
}
引导(应用程序,[]);
有关更多代码,请参阅顶部的链接
编辑:这是一个非常小的演示。您已经提到您已经尝试了
sharedService
。因此,请了解更多信息 更新为rc.4:
在angular2中尝试在兄弟组件之间传递数据时,目前最简单的方法(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{
}
import { Subscription } from 'rxjs/Subscription';
import { CallService } from "../../../services/call.service";
ngOnInit() {
this.subscription = this.Util.getClickCall().subscribe(message => {
this.message = message;
console.log('---button clicked at another component---');
//call you action which need to execute in this component on button clicked
});
}
import { Subscription } from 'rxjs/Subscription';
import { CallService } from "../../../services/call.service";
ngOnInit() {
this.subscription = this.Util.getClickCall().subscribe(message => {
this.message = message;
console.log('---button clicked at another component---');
//call you action which need to execute in this component on button clicked
});
}
从'@angular/core'导入{Component};
从“/shared.service”导入{SharedService};
从“./child.component”导入{ChildComponent};
从“./child sibling.component”导入{ChildSiblingComponent};
@组成部分({
选择器:“父组件”,
模板:`
父母亲
`,
提供者:[共享服务],
指令:[ChildComponent,ChildSiblingComponent]
})
导出类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;
}
}
从'@angular/core'导入{Component,OnInit};
从“./shared.service”导入{SharedService}
@组成部分({
选择器:'子组件',
模板:`
我是一个孩子
- {{data}}
`
})
导出类ChildComponent实现OnInit{
数据:字符串[]=[];
建造师(
私有_sharedService:sharedService{}
ngOnInit():任何{
this.data=this.\u sharedService.dataArray;
}
}
孩子2(它的兄弟姐妹)
从'angular2/core'导入{Component};
从“./shared.service”导入{SharedService}
@组成部分({
选择器:“子同级组件”,
模板:`
我是一个孩子
`
})
导出类ChildSiblingComponent{
数据:字符串=‘测试数据’;
建造师(
私有_sharedService:sharedService{}
addData(){
this.\u sharedService.insertData(this.data);
这个数据=“”;
}
}
现在:使用此方法时需要注意的事项
您需要设置组件之间的父子关系。问题在于,您可能只需将子组件注入父组件的构造函数中,并将其存储在局部变量中。 相反,您应该使用
@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();
}
}
从'@angular/core'导入{Component,ViewChild,AfterViewInit};
从“./list.component”导入{ListComponent};
从“./detail.component”导入{DetailComponent};
@组成部分({
选择器:“应用程序组件”,
模板:“”,
指令:[ListComponent,DetailComponent]
})
类AppComponent实现AfterViewInit{
@ViewChild(ListComponent)ListComponent:ListComponent;
@ViewChild(DetailComponent)DetailComponent:DetailComponent;
ngAfterViewInit(){
//在这一点之后,孩子们被设置好了,所以你可以使用他们
this.detailComponent.doSomething();
}
}
注意,在调用ngAfterViewInit
lifecyclehook之后,子组件在父组件的构造函数中将不可用。要捕获这个钩子,只需在父类中实现AfterViewInit
接口,方法与OnInit
相同
但是,还有其他属性声明程序,如本博客注释所述:
如果有两个不同的组件(不是嵌套组件,而是父组件\
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);
}
}
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();
}
}
<!-- Assigns "AppSibling1Component" instance to variable "data" -->
<app-sibling1 #data></app-sibling1>
<!-- Passes the variable "data" to AppSibling2Component instance -->
<app-sibling2 [data]="data"></app-sibling2>
import { AppSibling1Component } from '../app-sibling1/app-sibling1.component';
...
export class AppSibling2Component {
...
@Input() data: AppSibling1Component;
...
}
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
private noId = new BehaviorSubject<number>(0);
defaultId = this.noId.asObservable();
newId(urlId) {
this.noId.next(urlId);
}
public getId () {
const id = +this.route.snapshot.paramMap.get('id');
return id;
}
ngOnInit(): void {
const id = +this.getId ();
this.taskService.newId(id)
}
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']);
}
})
})
}
@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
}
}
<youtube-player #technologyVideo videoId='NuU74nesR5A'></youtube-player>
... lots more DOM ...
<button class="play-button"
ytPlayerPlayButton
[ytPlayerVideo]="technologyVideo">Play</button>
import { Observable } from 'rxjs';
import { Subject } from 'rxjs/Subject';
@Injectable()
export class CallService {
private subject = new Subject<any>();
sendClickCall(message: string) {
this.subject.next({ text: message });
}
getClickCall(): Observable<any> {
return this.subject.asObservable();
}
}
import { CallService } from "../../../services/call.service";
export class MarketplaceComponent implements OnInit, OnDestroy {
constructor(public Util: CallService) {
}
buttonClickedToCallObservable() {
this.Util.sendClickCall('Sending message to another comp that button is clicked');
}
}
import { Subscription } from 'rxjs/Subscription';
import { CallService } from "../../../services/call.service";
ngOnInit() {
this.subscription = this.Util.getClickCall().subscribe(message => {
this.message = message;
console.log('---button clicked at another component---');
//call you action which need to execute in this component on button clicked
});
}
import { Subscription } from 'rxjs/Subscription';
import { CallService } from "../../../services/call.service";
ngOnInit() {
this.subscription = this.Util.getClickCall().subscribe(message => {
this.message = message;
console.log('---button clicked at another component---');
//call you action which need to execute in this component on button clicked
});
}
@NgModule({
imports: [
...
],
bootstrap: [
AppComponent
],
declarations: [
AppComponent,
],
providers: [
SharedService,
...
]
});
constructor(private sharedService: SharedService)
@Injectable()
export class SharedService {
public clickedItemInformation: Subject<string> = new Subject();
}
this.sharedService.clikedItemInformation.next("something");
this.sharedService.clikedItemInformation.subscribe((information) => {
// do something
});
@Component({
selector: 'parent',
template: `<div><notes-grid
[Notes]="(NotesList$ | async)"
(selectedNote)="ReceiveSelectedNote($event)"
</notes-grid>
<note-edit
[gridSelectedNote]="(SelectedNote$ | async)"
</note-edit></div>`,
styleUrls: ['./parent.component.scss']
})
export class ParentComponent {
// create empty observable
NotesList$: Observable<Note[]> = of<Note[]>([]);
SelectedNote$: Observable<Note> = of<Note>();
//passed from note-grid for selected note to edit.
ReceiveSelectedNote(selectedNote: Note) {
if (selectedNote !== null) {
// change value direct subscribers or async pipe subscribers will get new value.
this.SelectedNote$ = of<Note>(selectedNote);
}
}
//used in subscribe next() to http call response. Left out all that code for brevity. This just shows how observable is populated.
onNextData(n: Note[]): void {
// Assign to Obeservable direct subscribers or async pipe subscribers will get new value.
this.NotesList$ = of<Note[]>(n.NoteList); //json from server
}
}
//child 1 sibling
@Component({
selector: 'note-edit',
templateUrl: './note-edit.component.html', // just a textarea for noteText and submit and cancel buttons.
styleUrls: ['./note-edit.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class NoteEditComponent implements OnChanges {
@Input() gridSelectedNote: Note;
constructor() {
}
// used to capture @Input changes for new gridSelectedNote input
ngOnChanges(changes: SimpleChanges) {
if (changes.gridSelectedNote && changes.gridSelectedNote.currentValue !== null) {
this.noteText = changes.gridSelectedNote.currentValue.noteText;
this.noteCreateDtm = changes.gridSelectedNote.currentValue.noteCreateDtm;
this.noteAuthorName = changes.gridSelectedNote.currentValue.noteAuthorName;
}
}
}
//child 2 sibling
@Component({
selector: 'notes-grid',
templateUrl: './notes-grid.component.html', //just an html table with notetext, author, date
styleUrls: ['./notes-grid.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class NotesGridComponent {
// the not currently selected fromt eh grid.
CurrentSelectedNoteData: Note;
// list for grid
@Input() Notes: Note[];
// selected note of grid sent out to the parent to send to sibling.
@Output() readonly selectedNote: EventEmitter<Note> = new EventEmitter<Note>();
constructor() {
}
// use when you need to send out the selected note to note-edit via parent using output-> input .
EmitSelectedNote(){
this.selectedNote.emit(this.CurrentSelectedNoteData);
}
}
// here just so you can see what it looks like.
export interface Note {
noteText: string;
noteCreateDtm: string;
noteAuthorName: string;
}