Angular2 服务发射器订阅失败
Angular2 service emitter subscribe fail
参考 我正在尝试实现一个购物车服务,该服务会在将新商品添加到购物车时发出一个事件。我在另一个名为 navbar(不是购物车的子项)的组件中订阅了此服务事件,我在其中显示了我的购物车商品数量等。
我的购物车服务:
export class CartService {
public itemAdded$: EventEmitter<any>;
...
constructor(private http: Http){
this.itemAdded$ = new EventEmitter();
this.cart=[];
}
addToCart(id: any): any {
this.itemAdded$.emit(id);
return this.http.get( this.myUrl + 'add' + '/' + id + '/', { withCredentials:true})
.toPromise()
.then(response => response.json())
}
...
...
}
我的导航栏组件:
@Component({
directives:[ROUTER_DIRECTIVES, Cart],
providers:[CartService],
})
export class Navbar implements OnInit{
...
totalCost: any;
cartItemCount : any;
addedItem: any;
constructor(private cartService: CartService){
this.cartService.itemAdded$.subscribe( (id: any) => {
alert(id);
this.onItemAdded(id);
this.fetchCartElements();
});
}
private onItemAdded(item: any): void {
// do something with added item
this.addedItem = item;
}
...
...
}
但是,每当我添加一个项目时,导航栏中没有任何反应,即 alert(id)
或 onItemAdded()
不会自动调用,以便 cartItemCount 等可以自动更新。
出了什么问题?
您应该只对 @Output()
使用 EventEmitter
!
您的场景完全符合 Observable
..
创建一个Subject
并调用next()
,类似于emit()
。
import { Subject } from 'rxjs';
// ..
public itemAdded$ = new Subject<any>();
// ..
this.itemAdded$.next(id);
订阅部分应该没问题
每个提供者的服务都是单例的 。 您正在将 CartService
的不同实例注入导航栏以及调用 addToCart
函数的任何人。
我希望 Navbar
显示在您的应用程序的每个页面中,所以这里是如何使 CartService
全局单例。
@NgModule({
declarations: [
],
imports: [
],
providers: [CartService], //<--- added here
bootstrap: [AppComponent]
})
export class AppModule { }
从模块和组件中删除任何其他 providers: [CartService]
(如果您不打算缩进创建另一个实例)
我认为你这里的错误是概念错误。
正如@mxii 所说,您的情况完全符合 Observable
行为,您不应该使用 promise 和事件发射器(在您的项目实际添加到服务器端之前调用)。
有希望的解决方案:
如果你想用 promise 解决你的问题,我认为解决方案是在你得到答案后触发事件:
return this.http.get( this.myUrl + 'add' + '/' + id + '/', { withCredentials:true})
.toPromise()
.then(response => {
this.cart.push(response.json());
this.itemAdded$.emit(id);
});
然后在您的顶级模块中提供它(通常 AppModule
)以确保您在整个应用程序中只获得一个实例。
Observable 解决方案:
addToCart(id: any): Observable<any> {//Don't you have an item interface to type the object you get?
return this.http.get( this.myUrl + 'add' + '/' + id + '/', { withCredentials:true})
.map(res => res.json());
}
然后在你的 NavBar
:
@Component({
directives:[ROUTER_DIRECTIVES, Cart]
//Note that I removed CartService, as I said you HAVE to provide it on the AppModule, else you'll have different instances accross your application and it seems like it's not what you want.
})
export class Navbar implements OnInit{
...
totalCost: any;
cartItemCount : any;
addedItem: any;
constructor(private cartService: CartService){
}
private onItemAdded(item: any): void {
// do something with added item
this.addedItem = item;
}
public addItemToCart(id:number):void{
//I guess you're adding item from a button click, the button should call this method, passing the id of the item as parameter.
this.cartService.addToCart(id).subscribe(this.onItemAdded);
}
...
...
}
参考
我的购物车服务:
export class CartService {
public itemAdded$: EventEmitter<any>;
...
constructor(private http: Http){
this.itemAdded$ = new EventEmitter();
this.cart=[];
}
addToCart(id: any): any {
this.itemAdded$.emit(id);
return this.http.get( this.myUrl + 'add' + '/' + id + '/', { withCredentials:true})
.toPromise()
.then(response => response.json())
}
...
...
}
我的导航栏组件:
@Component({
directives:[ROUTER_DIRECTIVES, Cart],
providers:[CartService],
})
export class Navbar implements OnInit{
...
totalCost: any;
cartItemCount : any;
addedItem: any;
constructor(private cartService: CartService){
this.cartService.itemAdded$.subscribe( (id: any) => {
alert(id);
this.onItemAdded(id);
this.fetchCartElements();
});
}
private onItemAdded(item: any): void {
// do something with added item
this.addedItem = item;
}
...
...
}
但是,每当我添加一个项目时,导航栏中没有任何反应,即 alert(id)
或 onItemAdded()
不会自动调用,以便 cartItemCount 等可以自动更新。
出了什么问题?
您应该只对 @Output()
使用 EventEmitter
!
您的场景完全符合 Observable
..
创建一个Subject
并调用next()
,类似于emit()
。
import { Subject } from 'rxjs';
// ..
public itemAdded$ = new Subject<any>();
// ..
this.itemAdded$.next(id);
订阅部分应该没问题
每个提供者的服务都是单例的 。 您正在将 CartService
的不同实例注入导航栏以及调用 addToCart
函数的任何人。
我希望 Navbar
显示在您的应用程序的每个页面中,所以这里是如何使 CartService
全局单例。
@NgModule({
declarations: [
],
imports: [
],
providers: [CartService], //<--- added here
bootstrap: [AppComponent]
})
export class AppModule { }
从模块和组件中删除任何其他 providers: [CartService]
(如果您不打算缩进创建另一个实例)
我认为你这里的错误是概念错误。
正如@mxii 所说,您的情况完全符合 Observable
行为,您不应该使用 promise 和事件发射器(在您的项目实际添加到服务器端之前调用)。
有希望的解决方案:
如果你想用 promise 解决你的问题,我认为解决方案是在你得到答案后触发事件:
return this.http.get( this.myUrl + 'add' + '/' + id + '/', { withCredentials:true})
.toPromise()
.then(response => {
this.cart.push(response.json());
this.itemAdded$.emit(id);
});
然后在您的顶级模块中提供它(通常 AppModule
)以确保您在整个应用程序中只获得一个实例。
Observable 解决方案:
addToCart(id: any): Observable<any> {//Don't you have an item interface to type the object you get?
return this.http.get( this.myUrl + 'add' + '/' + id + '/', { withCredentials:true})
.map(res => res.json());
}
然后在你的 NavBar
:
@Component({
directives:[ROUTER_DIRECTIVES, Cart]
//Note that I removed CartService, as I said you HAVE to provide it on the AppModule, else you'll have different instances accross your application and it seems like it's not what you want.
})
export class Navbar implements OnInit{
...
totalCost: any;
cartItemCount : any;
addedItem: any;
constructor(private cartService: CartService){
}
private onItemAdded(item: any): void {
// do something with added item
this.addedItem = item;
}
public addItemToCart(id:number):void{
//I guess you're adding item from a button click, the button should call this method, passing the id of the item as parameter.
this.cartService.addToCart(id).subscribe(this.onItemAdded);
}
...
...
}