Я очень новичок в 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 emit event, содержащий информацию, содержащуюся в объекте 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() этого класса.
Из того, что я знаю, это просто испускает событие, связанное с этим объектом Рецепт. Поэтому я думаю, что он стреляет в кого-то еще содержимое этого объекта, где кто-то еще должен быть другим компонентом (поэтому он реализует концепцию взаимодействия кросс-компонентов).
Тогда у меня есть этот другой класс компонентов 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, который его использует. Это?
Тогда я сомневаюсь в этом синтаксе:
(recipe: Recipe) => {
this.selectedRecipe = recipe;
}
Что именно означает? Я думаю, что рецепт: Рецепт - это содержимое принятого события. Это что-то вроде неявного способа объявления функции? (Я пришел из Java, и я не настолько в этом синтаксисе).
Другое сомнение заключается в следующем: почему этот код объявлен в ngOnInit()? Моя идея заключается в том, что он объявляет слушателя, когда этот компонент создается, а затем этот слушатель реагирует на события, которые могут появиться во второй раз. Это?
EventEmitter
не должен использоваться в службе.
См. Это сообщение: Какое правильное использование EventEmitter?
С этого поста:
Use by directives and components to emit custom Events.
Не для использования в услугах. Как отметил @Pablo, даже для компонентов рекомендуется использовать @Output для раскрытия вашего события.
Для службы обычно обнаружение углового изменения будет обрабатывать изменения данных службы. Так что все, что вам нужно сделать, это разоблачить эти данные. У меня есть пример:
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
изменяется, обнаружение с угловым изменением будет уведомлено, и пользовательский интерфейс будет привязан к текущему значению selectedRecipe
.
Я думаю, вы прикрепили описание этого фрагмента кода. Я думаю, что не использовал бы сервис, чтобы испускать рецепт, но только атрибут @Output
, но в любом случае ваш анализ правильный.
О обозначении стрелки, я рекомендую вам прочитать документацию MDN.
А насчет ngOnInit()
: Обычно в Angular конструктор используется для ввода зависимостей только потому, что основная логика инициализации устанавливается в методе ngOnInit
только потому, что все атрибуты, украшенные @Input
, инициализируются непосредственно перед вызовом этого метода, поэтому визуальный "Конструкция" компонента не будет выполняться до вызова этого метода.