Angular 4 Subscribe method call multiple times

29,298

Solution 1

I think i would be easier to use the async pipe inside your modal component:

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();
  }
}

and inside your template for example:

(modal$ | async)?.title

This ensures that Angular is cleaning up the subscription by itself on component destruction.

For your subscription when creating the modal you can use the take Operator, which completes the subscription if x values where emitted.

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

I'm assuming you only want 1 value (as its a simple dialog).

Solution 2

Just don't forget to unsubscribe modalService when your component is destroyed.. eg.

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

  constructor(private modalService: ModalService){}

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

What's could be happened is that every time you open the modal the subscription keeps persisted and will be executed +1 time, but using destroy when the modal is closed or destroyed, you'll be removing the subscription as well.

Or even better, basically every time you subscribe you're creating a new observable, but you could reuse the first one you create, applying a simple validation (heads up, you shouldn't use unsubscribe in this case). eg.

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

There's many ways to prevent triggered subscribers more than once.

Share:
29,298
Juan
Author by

Juan

Learning about software and web development. Always trying something new in the field of information technology. Want to improve and grow professionally and let the fear fears you.

Updated on August 04, 2020

Comments

  • Juan
    Juan over 3 years

    im creating a global modal component. My problem is when i call the subscribe method it call multiple times base on the number of the modal was called. How can i prevent mutiple calls on observable subscribe method? Please check my code below. Thanks in advance.

    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();
      }
    }
    

    calling the modal component

    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');
      });
    }
    

    Here is the screenshot of the output on the console log. console log output

  • Juan
    Juan almost 7 years
    Thank you for your suggestion, but still not working.
  • cyr_x
    cyr_x almost 7 years
    Subscribing inside a component constructor without cleaning it up inside the ngOnDestroy lifecyclehook or via takeWhile or takeUntil is a bad practice.
  • vidalsasoon
    vidalsasoon almost 7 years
    it was more to troubleshoot his multiple subscribes firing. anyway, good point. updated.
  • cyr_x
    cyr_x almost 7 years
    Maybe your application has not all operators imported, you could import the operator with: import 'rxjs/add/operator/take';
  • Juan
    Juan almost 7 years
    Thanks, your code is working, the take() method did the trick. However the modal will popup if i click the button twice. any idea?
  • Anil Uttani
    Anil Uttani over 5 years
    The second method is showing error ,type Observable<any> does not have property observers.