在这个Angular 2应用程序中,这个交叉组件通信“模式”究竟是如何工作的?

时间:2017-08-09 15:30:42

标签: javascript angular javascript-framework angular-services angular-components

我是 Angular 2 中的新手,我对使用服务的交叉组件通信的使用方式有何疑问。

在我的应用程序中,我有 RecipeService 服务类:

@Injectable()
export class RecipeService {

  // Hold a Recipe object to be emitted to another component to implement cross component comunication:
  recipeSelected = new EventEmitter<Recipe>();

  // List of all recipes (maybe later can be obtained by a web service)
  private recipes: Recipe[] = [
    new Recipe(
      'Tasty Schnitzel',
      'A super-tasty Schnitzel - just awesome!',
      'https://upload.wikimedia.org/wikipedia/commons/7/72/Schnitzel.JPG',
      [
        new Ingredient('Meat', 1),
        new Ingredient('French Fries', 20)
      ]),
    new Recipe('Big Fat Burger',
      'What else you need to say?',
      'https://upload.wikimedia.org/wikipedia/commons/b/be/Burger_King_Angus_Bacon_%26_Cheese_Steak_Burger.jpg',
      [
        new Ingredient('Buns', 2),
        new Ingredient('Meat', 1)
      ])
  ];

  // Inject a sub service:
  constructor(private slService: ShoppingListService) {}

  /**
   * Return a copy of the reipes array.
   * @returns {Recipe[]}
   */
  getRecipes() {
    return this.recipes.slice();
  }

  addIngredientsToShoppingList(ingredients: Ingredient[]) {
    this.slService.addIngredients(ingredients);
  }
}

此类由2个不同的组件使用,以实现此发射器的交叉组件通信:

recipeSelected = new EventEmitter<Recipe>();

根据我的理解(如果我做错了断言,请更正我)这个 recipeSelected 发出一个事件,该信息包含 Recipe 对象中包含的信息(它包含一些字符串)字段)。

然后我有了 RecipeItemComponent 组件(它代表一个配方,它的视图显示了与特定配方相关的信息):

@Component({
  selector: 'app-recipe-item',
  templateUrl: './recipe-item.component.html',
  styleUrls: ['./recipe-item.component.css']
})
export class RecipeItemComponent implements OnInit {
  @Input() recipe: Recipe;

  // Inkect the RecipeService to use it in this component:
  constructor(private recipeService: RecipeService) { }

  ngOnInit() {
  }

  /**
   * When a specific recipe is selected in the page it emit the selected recipe to comunicate
   * with another component
   */
  onSelected() {
    this.recipeService.recipeSelected.emit(this.recipe);
  }

}

当用户点击与此 RecipeItemComponent 相关的视图的链接时,将执行此类的 onSelected()方法。

据我所知,它只会发出与此 Recipe 对象相关的事件。所以我认为对其他人来说射击这个对象的内容,其他人应该是另一个组件(因此它实现了跨组件通信概念)。

然后我有另外 RecipesComponent 组件类:

@Component({
  selector: 'app-recipes',
  templateUrl: './recipes.component.html',
  styleUrls: ['./recipes.component.css'],
  providers: [RecipeService]
})
export class RecipesComponent implements OnInit {
  selectedRecipe: Recipe;

  /**
   * Inject the RecupeService to use it in this component
   * @param recipeService
   */
  constructor(private recipeService: RecipeService) { }

  /**
   * Subscribe on the event emitted when a recipe is selected:
   */
  ngOnInit() {
    this.recipeService.recipeSelected
      .subscribe(
        (recipe: Recipe) => {
          this.selectedRecipe = recipe;
        }
      );
  }

}

根据我的理解,我正在通过以下方式将这类事件的“聆听者”(它是一个listerner?)注册到 ngOnInit()方法中:

  ngOnInit() {
    this.recipeService.recipeSelected
      .subscribe(
        (recipe: Recipe) => {
          this.selectedRecipe = recipe;
        }
      );
  }

因此,实际上,每次 RecipeItemComponent 组件发出包含 Recipe 对象的事件时, RecipesComponent 都会收到此信息使用它的组件。是吗?

然后我怀疑这个sintax:

(recipe: Recipe) => {
    this.selectedRecipe = recipe;
}

究竟意味着什么?我认为食谱:食谱是收到的事件的内容。这是一种声明函数的隐式方法吗? (我来自Java,我不是那种语法)。

另一个疑问是:为什么将此代码声明为 ngOnInit()?我的想法是,它在创建此组件时声明一个侦听器,然后此侦听器对可能在第二次发生的事件做出反应。是吗?

2 个答案:

答案 0 :(得分:2)

EventEmitter应该在服务中使用。

请参阅此帖子:What is the proper use of an EventEmitter?

从那篇文章:

Use by directives and components to emit custom Events.

不用于服务。正如@Pablo所提到的,即使对于组件,建议您使用@O​​utput公开您的事件。

对于服务,通常Angular的更改检测将处理对服务数据的更改。所以你需要做的就是暴露这些数据。我在这里有一个例子:

https://blogs.msmvps.com/deborahk/build-a-simple-angular-service-to-share-data/

这里有一个相应的傻瓜:https://plnkr.co/edit/KT4JLmpcwGBM2xdZQeI9?p=preview

import { Injectable } from '@angular/core';

@Injectable() 
export class DataService {
  serviceData: string; 
}

所以这个:

@Injectable()
export class RecipeService {

  recipeSelected = new EventEmitter<Recipe>();

成为这个:

@Injectable()
export class RecipeService {

  recipeSelected: Recipe;

而且:

  onSelected() {
    this.recipeService.recipeSelected.emit(this.recipe);
  }

成为这个:

  onSelected() {
    this.recipeService.recipeSelected=this.recipe;
  }

而且:

export class RecipesComponent implements OnInit {
  selectedRecipe: Recipe;

  ngOnInit() {
    this.recipeService.recipeSelected
      .subscribe(
        (recipe: Recipe) => {
          this.selectedRecipe = recipe;
        }
      );
  }

}

成为这个:

export class RecipesComponent implements OnInit {
  get selectedRecipe(): Recipe {
     return this.recipeService.recipeSelected;
  };   
}

每次recipeSelected更改时,都会通知角度更改检测,并且UI会重新绑定到当前值selectedRecipe

答案 1 :(得分:1)

我认为你已经确定了那条代码的描述。我想我不会使用服务来发布食谱,而只是@Output属性,但无论如何你的分析是正确的。

关于箭头符号,我建议您阅读MDN documentation

关于ngOnInit():通常在Angular中,构造函数仅用于注入依赖项,因为主要的初始化逻辑是在ngOnInit方法中设置的,因为所有属性都用{{1}修饰在调用此方法之前初始化,因此在调用此方法之前,不会完成组件的可视“构造”。