Как именно работает этот «шаблон» взаимодействия компонентов в этом приложении Angular 2?

1

Я очень новичок в 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()? Моя идея заключается в том, что он объявляет слушателя, когда этот компонент создается, а затем этот слушатель реагирует на события, которые могут появиться во второй раз. Это?

  • 0
    Если вы пришли из Java, проверьте лямбда-выражения, функции стрелки там уже присутствуют.
Теги:
angular
angular-services
angular-components
javascript-framework

2 ответа

2
Лучший ответ

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.

1

Я думаю, вы прикрепили описание этого фрагмента кода. Я думаю, что не использовал бы сервис, чтобы испускать рецепт, но только атрибут @Output, но в любом случае ваш анализ правильный.

О обозначении стрелки, я рекомендую вам прочитать документацию MDN.

А насчет ngOnInit(): Обычно в Angular конструктор используется для ввода зависимостей только потому, что основная логика инициализации устанавливается в методе ngOnInit только потому, что все атрибуты, украшенные @Input, инициализируются непосредственно перед вызовом этого метода, поэтому визуальный "Конструкция" компонента не будет выполняться до вызова этого метода.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню