【问题标题】:Angular2 service emitter subscribe failAngular2服务发射器订阅失败
【发布时间】:2016-11-08 08:31:33
【问题描述】:

参考Global Events in Angular 2 我正在尝试实现一个购物车服务,该服务会在将新商品添加到购物车时发出一个事件。我在另一个名为 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 等可以自动更新。

出了什么问题?

【问题讨论】:

  • 您在哪里提供这项服务?
  • 我没有正确理解您的问题,但在我的导航栏组件中,我已将其添加到 [providers] 中。我用那个更新了我的问题。购物车服务与导航栏直接无关。

标签: angular angular2-services


【解决方案1】:

我认为你的错误是概念错误。

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

【讨论】:

  • 在我的情况下,我的导航栏组件中没有 addItemToCart(id:number) 方法,我从另一个组件调用它。在导航栏组件中,我简单地获取购物车元素,并可以删除它们。添加到购物车发生在我的不同产品组件中。
【解决方案2】:

你应该只为@Output() 使用EventEmitter

对于您的场景,完全适合 Observable ..

创建Subject并调用next(),类似于emit()

import { Subject } from 'rxjs';

// ..

   public itemAdded$ = new Subject<any>();

// ..

   this.itemAdded$.next(id);

订阅部分应该没问题。

【讨论】:

  • 从'rxjs'导入{主题};给我错误说 404 未找到。但是我有 import { Observable } from 'rxjs/Observable';效果很好..
  • 然后试试这个:import { Subject } from 'rxjs/Subject';
【解决方案3】:

服务是单一的每个提供者。您将CartService 的不同实例注入到导航栏以及调用addToCart 函数的人。

我希望Navbar 显示在您的应用程序的每个页面中,所以这里是如何使CartService 全局单例。

@NgModule({
  declarations: [
  ],
  imports: [
  ],
  providers: [CartService], //<--- added here
  bootstrap: [AppComponent]
})
export class AppModule { }

从模块和组件中删除任何其他providers: [CartService](如果您不缩进以创建另一个实例)

【讨论】:

  • 参考以上两个答案,哪个更适合您的解决方案?事件发射器还是使用 Subject 和 next()?
  • 您需要像我在两种方式中显示的那样使您的服务单例。
猜你喜欢
  • 1970-01-01
  • 2016-06-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-13
相关资源
最近更新 更多