Angular 4 вызов метода подписки несколько раз

Я создаю глобальный модальный компонент. Моя проблема в том, что когда я вызываю метод подписки, который он вызывает несколько раз, в зависимости от номера модального окна. Как я могу предотвратить множественные вызовы наблюдаемого метода подписки? Пожалуйста, проверьте мой код ниже. Заранее спасибо.

modal.model.ts

export class Modal {
  title: string;
  message: string;
  visible: boolean = false;
}

modal.service

import { Injectable } from '@angular/core';
import { Modal } from './modal.model';
import { Observable } from 'rxjs';
import { Subject } from 'rxjs/Subject';

@Injectable()
export class ModalService {
  static readonly YES = 1;
  static readonly NO = 0;

  private modal = new Modal();
  private subject = new Subject<Modal>();
  private action = new Subject<number>();

  confirmationDialog(message) {
    this.modal.title = 'Confirmation';
    this.modal.message = message;
    return this;
  }

  show() {
    this.modal.visible = true;
    this.setModal(this.modal);
    return this;
  }

  setAction(action: number) {
    this.action.next(<number>action);
  }

  getAction(): Observable<any> {
    return this.action.asObservable();
  }

  setModal(alert: Modal) {
    this.subject.next(<Modal>alert);
    return this;
  }

  getModal(): Observable<any> {
    return this.subject.asObservable();
  }
}

modal.component

import { Component, OnInit } from '@angular/core';
import { ModalService } from './modal.service';
import { Modal } from './modal.model';

@Component({
  selector: 'modal',
  templateUrl: './modal.component.html',
  styleUrls: ['./modal.component.css']
})
export class ModalComponent implements OnInit {
  public modal: Modal;

  constructor(private modalService: ModalService){}

  ngOnInit() {
    this.modalService.getModal().subscribe((modal: Modal) => {
      this.modal = modal;
      console.log(modal);
    });
  }

  no() {
    this.modalService.setAction(0);
    this.modalService.close();
  }

  yes() {
    this.modalService.setAction(1);
    this.modalService.close();
  }
}

вызов модального компонента

showModal() {
  this.modalService.confirmationDialog('Are you sure you want to save this record?').show();
  this.modalService.getAction().subscribe(response => {
    if(response === ModalService.NO) {
      return;
    }
    console.log('call mutiple times');
  });
}

Вот скриншот вывода журнала консоли. вывод журнала консоли


person Juan    schedule 26.07.2017    source источник


Ответы (3)


Я думаю, мне было бы проще использовать канал async внутри вашего модального компонента:

import { Component } from '@angular/core';
import { Observable } from 'rxjs';

import { ModalService } from './modal.service';
import { Modal } from './modal.model';

@Component({
  selector: 'modal',
  templateUrl: './modal.component.html',
  styleUrls: ['./modal.component.css']
})
export class ModalComponent {
  public modal$: Observable<Modal>;

  constructor(private modalService: ModalService){
      this.modal$ = this.modalService.getModal();
  }

  no() {
    this.modalService.setAction(0);
    this.modalService.close();
  }

  yes() {
    this.modalService.setAction(1);
    this.modalService.close();
  }
}

и внутри вашего шаблона, например:

(modal$ | async)?.title

Это гарантирует, что Angular самостоятельно очищает подписку при уничтожении компонента.

Для вашей подписки при создании модального окна вы можете использовать take Operator, который завершает подписку, если отправлены значения x.

this.modalService.getAction().take(1).subscribe(response => {
    if(response === ModalService.NO) {
      return;
    }
    console.log('call mutiple times');
});

Я предполагаю, что вам нужно только 1 значение (как простой диалог).

person cyr_x    schedule 26.07.2017
comment
Возможно, в ваше приложение импортированы не все операторы, вы можете импортировать оператор с помощью: import 'rxjs/add/operator/take'; - person cyr_x; 26.07.2017
comment
Спасибо, ваш код работает, метод take () помог. Однако модальное окно появится, если я дважды нажму кнопку. любая идея? - person Juan; 26.07.2017

Только не забудьте отказаться от подписки на modalService, когда ваш компонент будет уничтожен .. например.

...
export class ModalComponent implements OnInit, OnDestroy {
  public modal: Modal;

  constructor(private modalService: ModalService){}

  public ngOnDestroy(): void {
    this.modalService.unsubscribe(); // or something similar
  }
}

Что может случиться, так это то, что каждый раз, когда вы открываете модальное окно, подписка остается неизменной и будет выполняться +1 раз, но с помощью команды destroy, когда модальное окно закрыто или уничтожено, вы также удалите подписку.

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

if (this.modalService.getModal().observers.length === 0) {
    this.modalService.getModal().subscribe((modal: Modal) => {
        this.modal = modal;
        console.log(modal);
    });
}

Есть много способов предотвратить срабатывание подписчиков более одного раза.

person Jack Fiallos    schedule 03.08.2018
comment
Второй метод показывает ошибку, тип Observable ‹any› не имеет наблюдателей свойств. - person Anil Uttani; 04.09.2018

Переместите подписку на ctor. Бывший:

    private unsubscribe: Subject<void> = new Subject<void>();

    constructor(private modalService: ModalService){
        this.modalService.getModal()
          .takeUntil(this.unsubscribe)
          .subscribe((modal: Modal) => {
               this.modal = modal;
               console.log(modal);
        });
    }

    ngOnInit() {

    }

    ngOnDestroy() {
         this.unsubscribe.next();
         this.unsubscribe.complete();
    }
person vidalsasoon    schedule 26.07.2017
comment
Спасибо за предложение, но все еще не работает. - person Juan; 26.07.2017
comment
Подписка внутри конструктора компонента без его очистки внутри жизненного цикла ngOnDestroy или через takeWhile или takeUntil - плохая практика. - person cyr_x; 26.07.2017
comment
это было больше для устранения неполадок, связанных с увольнением его нескольких подписчиков. в любом случае, хороший момент. обновлено. - person vidalsasoon; 26.07.2017