Angular EventEmitter вызывается несколько раз

Это действительно странно и трудно объяснимо. Я использовал EventEmitter в нескольких своих сервисах и использовал его для изменения данных в своих представлениях. У меня была проблема с маршрутами изменений (через ссылки или через историю), когда они, кажется, срабатывали несколько раз, и из-за этого это портило мою логику.

Поэтому я создал тест на stackblitz, чтобы посмотреть, смогу ли я его воссоздать. Я сделал простой сервис:

import { Injectable, Output, EventEmitter } from '@angular/core';

@Injectable()
export class ListService {
@Output() listChanged: EventEmitter<any[]> = new EventEmitter<any[]>()

  constructor() { }

  list() {
    this.listChanged.emit([]);
  }
}

а затем в одном из моих маршрутов я просто делаю это:

import { Component, OnInit } from '@angular/core';

import { ListService } from '../list.service';

@Component({
  selector: 'app-products',
  templateUrl: './products.component.html',
  styleUrls: ['./products.component.css']
})
export class ProductsComponent implements OnInit {
  count: any[] = []

  constructor(
    private listService: ListService
  ) { }

  ngOnInit() {
    this.listService.listChanged.subscribe(() => {
      this.count.push('invoked');
      console.log('invoked');
      console.log('------');
    });
    this.listService.list();
  }
}

Затем я создал простой шаблон, подобный этому:

<p>
  Invoke should only be called once
</p>

<ul>
  <li *ngFor="let item of count">{{ item }}</li>
</ul>

Когда вы перемещаетесь между маршрутами, похоже, что он работает так, как ожидалось (в ngFor всегда должен быть только один элемент), но если вы откроете консоль и посмотрите, вы увидите, что каждый раз, когда вы перейти от одного вида и обратно, это срабатывает дополнительное время.

  • Итак, при первом посещении вы увидите вывод консоли один раз.
  • Очистите консоль и перемещайтесь между представлениями, и вы дважды увидите вывод консоли.
  • Очистите консоль и перемещайтесь между представлениями, и вы увидите вывод консоли три раза.....

Это будет происходить каждый раз, когда вы меняете просмотры. Вот ссылка на stackblitz, так что вы можете увидеть это сами.

https://stackblitz.com/edit/angular-sthags

Может ли кто-нибудь сказать мне, почему это происходит и как это остановить?


person r3plica    schedule 27.11.2018    source источник


Ответы (2)


Это связано с тем, что вы не убиваете подписку, когда ваш компонент уничтожается (вызывая утечку памяти, создавая новые подписки каждый раз, когда ваш компонент продуктов инициализируется).

Назначьте подписку переменной класса, используйте хук ngOnDestroy(), чтобы убить подписку.

subsVar: Subscription;

ngOnInit() {
    this.subsVar = this.listService.listChanged.subscribe(() => {
      this.count.push('invoked');
      console.log('invoked');
      console.log('------');
    });
}

ngOnDestroy() {
   if (this.subsVar) {
      this.subsVar.unsubscribe()
    }
}

https://stackblitz.com/edit/angular-9rvxgv?file=src/app/products/products.component.ts

person Ashish Ranjan    schedule 27.11.2018

У меня есть короткий способ решить вашу проблему

export class ProductsComponent implements OnInit {
    count: any[] = [];
    isAlowToSub: boolean = true;

    constructor(
        private listService: ListService
    ) { }
    
    ngOnInit() {
        this.listService.listChanged.subscribe(() => {
            if(this.isAlowToSub == true)
            {
                this.count.push('invoked');
                console.log('invoked');
                console.log('------');
                this.isAlowToSub = false;
            }
        });
        this.listService.list();
    }
}
person rem 1980    schedule 09.07.2021