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

Posted

技术标签:

【中文标题】在这个 Angular 2 应用程序中,这种跨组件通信“模式”究竟是如何工作的?【英文标题】:How exactly works this cross components comunication "pattern" in this Angular 2 application? 【发布时间】:2018-01-17 14:15:51 【问题描述】:

我是 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);
  

这个类被两个不同的组件用来实现这个发射器的跨组件通信:

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


据我所知,我正在将此类事件的“侦听器”(它是侦听器吗?)注册到 ngOnInit() 方法中,通过:

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

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

那我对这个sintax有疑问:

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

具体是什么意思?我认为 recipe: Recipe 是接收到的事件的内容。这有点像声明函数的隐式方式? (我来自 Java,我不太喜欢这种语法)。

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

【问题讨论】:

如果您来自 Java,请检查 lambda 表达式,箭头函数已经存在。 【参考方案1】:

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/

还有一个对应的 plunker: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 更改时,都会通知Angular 更改检测并且UI 将重新绑定到selectedRecipe 的当前值。

【讨论】:

【参考方案2】:

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

关于箭头符号,我推荐你阅读MDN documentation。

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

【讨论】:

以上是关于在这个 Angular 2 应用程序中,这种跨组件通信“模式”究竟是如何工作的?的主要内容,如果未能解决你的问题,请参考以下文章

我可以跨应用重用组件吗?

Angular 基础 2 - 组件相关

在 Angular 6 中创建用于跨字段验证(确认密码)的自定义指令

Angular 1 和 Angular 2 集成:无缝升级的方法

Angular 2:是不是可以在动态组件中绑定 var?

Svelte入门——Web Components实现跨框架组件复用