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