这个 Angular 2 应用程序中的跨组件通信 "pattern" 到底是如何工作的?
How exactly works this cross components comunication "pattern" in this Angular 2 application?
我是 Angular 2 的新手,我对这种使用服务的跨组件通信的确切工作原理有疑问。
在我的应用程序中,我有这个 RecipeService 服务 class:
@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);
}
}
此 class 由 2 个不同的组件使用,以通过此发射器实现跨组件通信:
recipeSelected = new EventEmitter<Recipe>();
根据我的理解(如果我做错了断言,请纠正我)这个 recipeSelected 发出的事件保存包含在 Recipe[=60 中的信息=] 对象(它包含一些字符串字段)。
然后我有这个 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);
}
}
当用户点击一个link进入与这个RecipeItemComponent相关的视图时这个onSelected()方法class 已执行。
据我所知,它只是发出一个与此 Recipe 对象相关的事件。所以我认为是把这个对象的内容给别人,别人应该是另一个组件(所以实现了跨组件通信的概念)。
然后我有另一个 RecipesComponent 组件 class:
@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;
}
);
}
}
据我所知,我正在将此类事件的 "listener"(它是监听器吗?)注册到 ngOnInit() 方法中,方法是:
ngOnInit() {
this.recipeService.recipeSelected
.subscribe(
(recipe: Recipe) => {
this.selectedRecipe = recipe;
}
);
}
因此,在实践中,每次 RecipeItemComponent 组件发出一个包含 Recipe 对象的事件时,此信息都会被RecipesComponent 使用它的组件。是吗?
那么我对这个语法有疑问:
(recipe: Recipe) => {
this.selectedRecipe = recipe;
}
具体是什么意思?我认为 recipe: Recipe 是接收事件的内容。这有点像声明函数的隐式方式? (我来自 Java,我不太喜欢这种语法)。
另外一个疑惑是:为什么这段代码声明到ngOnInit()中?我的想法是,它在创建此组件时声明一个侦听器,然后此侦听器对可能第二次出现的事件做出反应。是吗?
我认为您已经准确描述了那段代码。我想我不会使用服务来发出食谱,而只是一个 @Output
属性,但无论如何你的分析是正确的。
关于箭头符号,我建议你阅读MDN documentation。
关于ngOnInit()
:通常在Angular中构造函数只用于注入依赖,因为主要的初始化逻辑设置在ngOnInit
方法中只是因为所有的属性用 @Input
装饰的是在调用此方法之前初始化的,因此组件的视觉 "construction" 不会在调用此方法之前完成。
EventEmitter
不应该在服务中使用。
看到这个post:
来自 post:
Use by directives and components to emit custom Events.
不适用于服务。正如@Pablo 提到的,即使对于组件,也建议您使用@Output 来公开您的事件。
对于服务,通常 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
变化时,都会通知Angular变化检测,UI会重新绑定到selectedRecipe
的当前值。
我是 Angular 2 的新手,我对这种使用服务的跨组件通信的确切工作原理有疑问。
在我的应用程序中,我有这个 RecipeService 服务 class:
@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);
}
}
此 class 由 2 个不同的组件使用,以通过此发射器实现跨组件通信:
recipeSelected = new EventEmitter<Recipe>();
根据我的理解(如果我做错了断言,请纠正我)这个 recipeSelected 发出的事件保存包含在 Recipe[=60 中的信息=] 对象(它包含一些字符串字段)。
然后我有这个 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);
}
}
当用户点击一个link进入与这个RecipeItemComponent相关的视图时这个onSelected()方法class 已执行。
据我所知,它只是发出一个与此 Recipe 对象相关的事件。所以我认为是把这个对象的内容给别人,别人应该是另一个组件(所以实现了跨组件通信的概念)。
然后我有另一个 RecipesComponent 组件 class:
@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;
}
);
}
}
据我所知,我正在将此类事件的 "listener"(它是监听器吗?)注册到 ngOnInit() 方法中,方法是:
ngOnInit() {
this.recipeService.recipeSelected
.subscribe(
(recipe: Recipe) => {
this.selectedRecipe = recipe;
}
);
}
因此,在实践中,每次 RecipeItemComponent 组件发出一个包含 Recipe 对象的事件时,此信息都会被RecipesComponent 使用它的组件。是吗?
那么我对这个语法有疑问:
(recipe: Recipe) => {
this.selectedRecipe = recipe;
}
具体是什么意思?我认为 recipe: Recipe 是接收事件的内容。这有点像声明函数的隐式方式? (我来自 Java,我不太喜欢这种语法)。
另外一个疑惑是:为什么这段代码声明到ngOnInit()中?我的想法是,它在创建此组件时声明一个侦听器,然后此侦听器对可能第二次出现的事件做出反应。是吗?
我认为您已经准确描述了那段代码。我想我不会使用服务来发出食谱,而只是一个 @Output
属性,但无论如何你的分析是正确的。
关于箭头符号,我建议你阅读MDN documentation。
关于ngOnInit()
:通常在Angular中构造函数只用于注入依赖,因为主要的初始化逻辑设置在ngOnInit
方法中只是因为所有的属性用 @Input
装饰的是在调用此方法之前初始化的,因此组件的视觉 "construction" 不会在调用此方法之前完成。
EventEmitter
不应该在服务中使用。
看到这个post:
来自 post:
Use by directives and components to emit custom Events.
不适用于服务。正如@Pablo 提到的,即使对于组件,也建议您使用@Output 来公开您的事件。
对于服务,通常 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
变化时,都会通知Angular变化检测,UI会重新绑定到selectedRecipe
的当前值。