我正在尝试在Angular中实现类似委托模式的东西。
当用户单击nav-item时,我想调用一个函数,该函数然后发出一个事件,该事件应该由侦听该事件的其他组件处理。
下面是场景:我有一个导航组件:
import {Component, Output, EventEmitter} from 'angular2/core';
@Component({
// other properties left out for brevity
events : ['navchange'],
template:`
<div class="nav-item" (click)="selectedNavItem(1)"></div>
`
})
export class Navigation {
@Output() navchange: EventEmitter<number> = new EventEmitter();
selectedNavItem(item: number) {
console.log('selected nav item ' + item);
this.navchange.emit(item)
}
}
下面是观察组件:
export class ObservingComponent {
// How do I observe the event ?
// <----------Observe/Register Event ?-------->
public selectedNavItem(item: number) {
console.log('item index changed!');
}
}
关键问题是,如何让观察组件观察问题中的事件?
我找到了另一个解决方案,没有使用Reactivex服务。我其实很喜欢rxjx API,但我认为它在解决异步和/或复杂函数时最好。用这种方式使用它,对我来说太过分了。
我想你想要的是广播。只是这一点。我找到了这个解决方案:
<app>
<app-nav (selectedTab)="onSelectedTab($event)"></app-nav>
// This component bellow wants to know when a tab is selected
// broadcast here is a property of app component
<app-interested [broadcast]="broadcast"></app-interested>
</app>
@Component class App {
broadcast: EventEmitter<tab>;
constructor() {
this.broadcast = new EventEmitter<tab>();
}
onSelectedTab(tab) {
this.broadcast.emit(tab)
}
}
@Component class AppInterestedComponent implements OnInit {
broadcast: EventEmitter<Tab>();
doSomethingWhenTab(tab){
...
}
ngOnInit() {
this.broadcast.subscribe((tab) => this.doSomethingWhenTab(tab))
}
}
这是一个完整的工作示例:
https://plnkr.co/edit/xGVuFBOpk2GP0pRBImsE
你可以使用如上所述的行为主体,或者还有一种方法:
你可以这样处理EventEmitter:
首先添加一个选择器
import {Component, Output, EventEmitter} from 'angular2/core';
@Component({
// other properties left out for brevity
selector: 'app-nav-component', //declaring selector
template:`
<div class="nav-item" (click)="selectedNavItem(1)"></div>
`
})
export class Navigation {
@Output() navchange: EventEmitter<number> = new EventEmitter();
selectedNavItem(item: number) {
console.log('selected nav item ' + item);
this.navchange.emit(item)
}
}
现在你可以像这样处理这个事件
让我们假设observer.component.html是Observer组件的视图
<app-nav-component (navchange)="recieveIdFromNav($event)"></app-nav-component>
然后在ObservingComponent.ts中
export class ObservingComponent {
//method to recieve the value from nav component
public recieveIdFromNav(id: number) {
console.log('here is the id sent from nav component ', id);
}
}
你可以使用任何一种:
行为主体:
行为主体是主体的一种类型,主体是一种特殊类型的可观察对象,它可以充当被观察对象和观察者
你可以像订阅其他可观察对象一样订阅消息,在订阅时,它会返回主题的最后一个值
由源可观察对象触发:
优点:在组件之间传递数据不需要亲子关系等关系。
导航服务
import {Injectable} from '@angular/core'
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
@Injectable()
export class NavService {
private navSubject$ = new BehaviorSubject<number>(0);
constructor() { }
// Event New Item Clicked
navItemClicked(navItem: number) {
this.navSubject$.next(number);
}
// Allowing Observer component to subscribe emitted data only
getNavItemClicked$() {
return this.navSubject$.asObservable();
}
}
导航组件
@Component({
selector: 'navbar-list',
template:`
<ul>
<li><a (click)="navItemClicked(1)">Item-1 Clicked</a></li>
<li><a (click)="navItemClicked(2)">Item-2 Clicked</a></li>
<li><a (click)="navItemClicked(3)">Item-3 Clicked</a></li>
<li><a (click)="navItemClicked(4)">Item-4 Clicked</a></li>
</ul>
})
export class Navigation {
constructor(private navService:NavService) {}
navItemClicked(item: number) {
this.navService.navItemClicked(item);
}
}
观察组件
@Component({
selector: 'obs-comp',
template: `obs component, item: {{item}}`
})
export class ObservingComponent {
item: number;
itemClickedSubcription:any
constructor(private navService:NavService) {}
ngOnInit() {
this.itemClickedSubcription = this.navService
.getNavItemClicked$
.subscribe(
item => this.selectedNavItem(item)
);
}
selectedNavItem(item: number) {
this.item = item;
}
ngOnDestroy() {
this.itemClickedSubcription.unsubscribe();
}
}
第二种方法是向上的child -> parent的事件委托
使用@Input和@Output装饰器,父组件将数据传递给子组件,子组件通知父组件
回答:@Ashish Sharma。
突发新闻:我添加了另一个答案,使用Observable而不是EventEmitter。我推荐这个答案而不是这个。实际上,在服务中使用EventEmitter是不好的做法。
原来的答案:(不要这样做)
将EventEmitter放入一个服务中,这允许ObservingComponent直接订阅(和取消订阅)事件:
import {EventEmitter} from 'angular2/core';
export class NavService {
navchange: EventEmitter<number> = new EventEmitter();
constructor() {}
emit(number) {
this.navchange.emit(number);
}
subscribe(component, callback) {
// set 'this' to component when callback is called
return this.navchange.subscribe(data => call.callback(component, data));
}
}
@Component({
selector: 'obs-comp',
template: 'obs component, index: {{index}}'
})
export class ObservingComponent {
item: number;
subscription: any;
constructor(private navService:NavService) {
this.subscription = this.navService.subscribe(this, this.selectedNavItem);
}
selectedNavItem(item: number) {
console.log('item index changed!', item);
this.item = item;
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
@Component({
selector: 'my-nav',
template:`
<div class="nav-item" (click)="selectedNavItem(1)">item 1 (click me)</div>
`,
})
export class Navigation {
constructor(private navService:NavService) {}
selectedNavItem(item: number) {
console.log('selected nav item ' + item);
this.navService.emit(item);
}
}
如果你尝试Plunker,我不喜欢这种方法:
ObservingComponent在销毁时需要取消订阅
我们必须将组件传递给subscribe(),以便在调用回调时设置正确的this
更新:解决第二个问题的另一种方法是让ObservingComponent直接订阅navchange EventEmitter属性:
constructor(private navService:NavService) {
this.subscription = this.navService.navchange.subscribe(data =>
this.selectedNavItem(data));
}
如果我们直接订阅,那么我们就不需要NavService上的subscribe()方法。
为了使NavService更加封装,你可以添加一个getNavChangeEmitter()方法并使用它:
getNavChangeEmitter() { return this.navchange; } // in NavService
constructor(private navService:NavService) { // in ObservingComponent
this.subscription = this.navService.getNavChangeEmitter().subscribe(data =>
this.selectedNavItem(data));
}
我找到了另一个解决方案,没有使用Reactivex服务。我其实很喜欢rxjx API,但我认为它在解决异步和/或复杂函数时最好。用这种方式使用它,对我来说太过分了。
我想你想要的是广播。只是这一点。我找到了这个解决方案:
<app>
<app-nav (selectedTab)="onSelectedTab($event)"></app-nav>
// This component bellow wants to know when a tab is selected
// broadcast here is a property of app component
<app-interested [broadcast]="broadcast"></app-interested>
</app>
@Component class App {
broadcast: EventEmitter<tab>;
constructor() {
this.broadcast = new EventEmitter<tab>();
}
onSelectedTab(tab) {
this.broadcast.emit(tab)
}
}
@Component class AppInterestedComponent implements OnInit {
broadcast: EventEmitter<Tab>();
doSomethingWhenTab(tab){
...
}
ngOnInit() {
this.broadcast.subscribe((tab) => this.doSomethingWhenTab(tab))
}
}
这是一个完整的工作示例:
https://plnkr.co/edit/xGVuFBOpk2GP0pRBImsE