Search code examples
angularangular-servicesangular-componentsangular-event-emitter

Emit an event From services Angular


Componet Code

import { Component, OnInit, EventEmitter, Output, Input } from '@angular/core';
import { Socket } from 'ngx-socket-io';
import { CommonService } from 'src/app/services/common.service';
import { FormioService } from 'src/app/services/formio.service';
@Component({
  selector: 'app-initialrecord',
  templateUrl: './initialrecord.component.html',
  styleUrls: ['./initialrecord.component.sass']
})
export class InitialRecordComponent implements OnInit {
  form: any = "";
  rendered = false;
  showForm: any = false;
  showTable: any = false;
  showAdd: any = true;
  showClose: any = false;
  patientData: any = {};
  currentTab = "";
  submitFunction = 'onSubmit';
  formId: any = ''
  rows: any = [];
  cols: any = [];
  formioJson: any;
  patientid = "";
  patientName = ""
  @Output() refreshForm = new EventEmitter();
  @Input() patientDetails: any = {};
  constructor(
    private FormioService: FormioService,
    private commonService: CommonService,
    private socket: Socket ) { }

  ngOnInit() {
    this.patientid=JSON.parse(this.commonService.getValue("patientDetails"))._id
    this.patientName =JSON.parse(this.commonService.getValue("patientDetails")).data.fname
    this.formioJson = JSON.parse(sessionStorage.getItem("formioJson"));
    this.listFormData('vitals')
  }

  listFormData(formName: string) {
    this.formId = formName;
    this.rows = [];
    this.cols = []
    this.toggleView(true, false, true, false)
    this.currentTab = this.formioJson[formName].name;
    let path = this.formioJson[formName].path
    let fromName = this.formioJson[formName].name
    this.FormioService.loadForms(path).subscribe(data => {
      this.FormioService.checkNested(data, true);
      this.cols = this.FormioService.cols;
    })
//calling service function
    this.FormioService.getFormData(path, formName, this.patientid, "data.patientId")
// subscribe the event
    this.FormioService.receivedRow.subscribe((param: any) => {
      this.rows = param.row;
      this.patientData[this.formioJson[formName].name] = param.data;
    });
  }

}

service code

import { Injectable , EventEmitter} from '@angular/core';
import { Socket } from 'ngx-socket-io';
import { environment } from '../../environments/environment';
import { ToastrService } from 'ngx-toastr';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { CommonService } from 'src/app/services/common.service';
import  moment from 'moment';

@Injectable({
  providedIn: 'root'
})
export class FormioService {
  headers: any;
  row :any[];
  cols:any[];
  receivedRow: EventEmitter<any>;
  patientData: any ={};
  constructor(private socket: Socket, private toaster: ToastrService,
    private httpClient: HttpClient,private commonService: CommonService) {
      this.receivedRow = new EventEmitter<any>()
      this.headers = new HttpHeaders({'Content-Type':'application/json; charset=utf-8','x-jwt-token':this.commonService.getValue("formioToken")})
     }
  public FORMIO_BASE_URL = environment.formioBaseUrl;
  public SOCKET_IO_URL = environment.nodeServerUrl


  getFormData(formUrl: string, tabName: string, patientId: string,keyName:string) {
    let form: any = new Object();
    form.roomId = this.commonService.roomId();
    form.token = this.commonService.getToken();
    form.eventName = tabName;
    form.formId = formUrl;
    form.query = keyName+"=" + patientId
    this.socket.emit('list', form);
    this.socket.on(tabName, async data => {
      this.row=[]
      data.forEach(element => {
        element.data['date'] =moment(element.created).format('DD-MM-YY HH:mm');
        console.log( element.data['date'] =moment(element.created).format('DD-MM-YY HH:mm'))
        this.row.push(element.data);
      });
      this.receivedRow.emit({row:this.row,data:data})
      console.log(this.row,"Formioservice")
    });
  }
  checkNested(obj: any,flag) {
    if(flag){
      this.cols =[]
      this.cols.push(JSON.parse( '{"field":"date","header":"Date"}'))
    }
    for (let prop in obj) {
      if (obj.hasOwnProperty(prop)) {
        if (typeof obj[prop] == "object") {
          //console.log('Key: ${prop}')
          this.checkNested(obj[prop],false);
        } else {
          //console.log("key >>> " + key + " >> props >>> " + obj[prop]);
          if (obj['tableView']) {
            //  console.log(`Key: ${prop} 'key_value: ${obj['key']} 'label_value: ${obj['label']} 'table_view_value: ${obj['tableView']} `)
            let temp: string = '{"field":"' + obj['key'] + '","header":"' + this.capitalize(obj['label']) + '"}';
            //  console.log(JSON.parse(temp));
            this.cols.push(JSON.parse(temp));
            break;
          }
        }
      }
    }

  }
  capitalize = (s: any) => {
    if (typeof s !== 'string') return ''
    return s.charAt(0).toUpperCase() + s.slice(1)
  }
}


on reading this link able to know its not good practice to use eventemitter in servies .The above code working perfectly my issue is I have common function for formData which fetch value of form depend on tabclick value.1st time event is emitting as expect when click other tab i.e second click its emitting two time,when third time its emitting 3 times 4 means 4 times can any one suggest me the best approach I am ne to angular


Solution

  • You are initiating a new subscription each time the listFormData function is called. Instead you could subscribe in the ngOnInit() hook once.

    ngOnInit() {
      this.patientid=JSON.parse(this.commonService.getValue("patientDetails"))._id
      this.patientName =JSON.parse(this.commonService.getValue("patientDetails")).data.fname
      this.formioJson = JSON.parse(sessionStorage.getItem("formioJson"));
      this.listFormData('vitals')
      // subscribe the event
      this.FormioService.receivedRow.subscribe((param: any) => {
        this.rows = param.row;
        this.patientData[this.formioJson[formName].name] = param.data;
      });
    }
    
    listFormData(formName: string) {
      this.formId = formName;
      this.rows = [];
      this.cols = []
      this.toggleView(true, false, true, false)
      this.currentTab = this.formioJson[formName].name;
      let path = this.formioJson[formName].path
      let fromName = this.formioJson[formName].name
      this.FormioService.loadForms(path).subscribe(data => {
        this.FormioService.checkNested(data, true);
        this.cols = this.FormioService.cols;
      });
      //calling service function
      this.FormioService.getFormData(path, formName, this.patientid, "data.patientId");
    }
    

    Also like you've mentioned in the question, EventEmitter is not designed as an Angular specific implementation of a multicast observable. It's purpose is to provide custom events to components with parent-child relationships.

    If you look at the source of the EventEmitter, it is an interface extension of the RxJS Subject. So if we require a multicast observable in a service, we could use it directly.

    Service

    import { Injectable} from '@angular/core';
    
    import { Subject } from 'rxjs';
    
    @Injectable({ providedIn: 'root' })
    export class FormioService {
      ...
      receivedRowSource = new Subject<any>();
      receivedRow$ = this.receivedRowSource.asObservable();
    
      getFormData(formUrl: string, tabName: string, patientId: string,keyName:string) {
        ...
        this.socket.on(tabName, async data => {
          ...
          this.receivedRowSource.next({row:this.row,data:data});       // <-- use `next` to push new notification
        });
      }
    }
    

    You also need to close any active subscriptions upon closing of the component to avoid potential memory leaks. You could either call assign the subscription to a member variable an call unsubscribe() in the ngOnDestroy() hook or use RxJS takeUntil() operator.

    import { Subject } from 'rxjs';
    import { takeUntil } from 'rxjs/operators';
    
    export class InitialRecordComponent implements OnInit, OnDestroy {
      ...
      completed$ = new Subject<any>();
    
      ngOnInit() {
        this.FormioService.receivedRow$.pipe(
          takeUntil(this.completed$)        // <-- pipe in the `takeUntil()` here
        ).subscribe((param: any) => {
          this.rows = param.row;
          this.patientData[this.formioJson[formName].name] = param.data;
        });
      }
    
      ngOnDestroy() {
        this.completed$.next();
        this.completed$.complete();
      }
    }