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