0

I use service socket.io to emit and receive data. When the send button is clicked it emits data and shows "FENEmitInfo" then receives the data and shows "FENSocketioGetCnt" and "FENSocketioFilterCnt". The console shows data correctly and on time but the textarea does not update all the data on time, there is a delay on showing the data the next time. How can I fix this?
Thanks in advance.

Data showing when the send button is clicked the first time.

Data showing when the send button is clicked the first time

Data showing when the send button is clicked the second time.

Data showing when the send button is clicked the second time

Data showing when the send button is clicked the third time.

Data showing when the send button is clicked the third time

This is app.component.html

1 <div><textarea id="txtarlogAPI" class="border border-red-500 rounded w-full h-96" [value]="logAPIraw"></textarea></div> 2 <div><button id="btnsendemit01" class="p-1 px-1 border border-red-500 rounded" (click)="sendemit01()">send</button></div> 

This is app.component.ts

1 import { Component, OnInit } from '@angular/core'; 2 import {FormsModule} from '@angular/forms'; 3 import {SvsocketService} from './svsocket.service'; 4 5 @Component({ 6 selector: 'app-root', 7 standalone: true, 8 imports: [FormsModule], 9 templateUrl: './app.component.html', 10 styleUrl: './app.component.css' 11 }) 12 13 export class AppComponent { 14 15 constructor( 16 private socketService: SvsocketService 17 ){} 18 19 SocketioGetCnt: number = 0; 20 SocketioFilterCnt: number = 0; 21 SocketioEmitCnt: number = 0; 22 logAPIraw: string = ""; 23 24 ngOnInit() { 25 26 this.socketService.onMsgSocketAPIEmitInfo().subscribe((dt: string) => { 27 28 this.SocketioGetCnt = this.SocketioGetCnt + 1; 29 console.log('FENSocketioGetCnt:'+this.SocketioGetCnt+', dataget:'+dt); 30 this.logAPIraw += 'FENSocketioGetCnt:'+this.SocketioGetCnt+', dataget:'+dt+'\n'; 31 32 if((dt.substring(0, 13))=="API_Info_test"){ //(dt=="API_Info_test") 33 this.SocketioFilterCnt = this.SocketioFilterCnt + 1; 34 console.log('FENSocketioFilterCnt:'+this.SocketioFilterCnt+', datafilter:'+dt); 35 this.logAPIraw += 'FENSocketioFilterCnt:'+this.SocketioFilterCnt+', datafilter:'+dt+'\n'; 36 } 37 38 }); 39 } 40 41 public sendemit01() { 42 this.SocketioEmitCnt = this.SocketioEmitCnt + 1; 43 this.socketService.sendMsgSocketFENEmitInfo("FEN_Info_test_" + this.SocketioEmitCnt.toString()); 44 console.log('FENEmitInfo' + " FEN_Info_test_" + this.SocketioEmitCnt.toString() + ' Finished'); 45 this.logAPIraw += 'FENEmitInfo' + " FEN_Info_test_" + this.SocketioEmitCnt.toString() + ' Finished'+'\n'; 46 } 47 48 } 

This is svsocket.service.ts

1 import { ApplicationRef, inject, Injectable } from '@angular/core'; 2 3 import { first, Observable } from 'rxjs'; 4 import {io, Socket} from 'socket.io-client'; 5 6 @Injectable({ 7 providedIn: 'root' 8 }) 9 export class SvsocketService { 10 11 private socket: Socket; 12 13 constructor() { 14 15 this.socket = io('http://localhost:3000', { autoConnect: false }); 16 17 inject(ApplicationRef).isStable.pipe( 18 first((isStable) => isStable)) 19 .subscribe(() => { this.socket.connect() }); 20 21 } 22 23 sendMsgSocketFENEmitInfo(message: string): void { 24 this.socket.emit('FENEmitInfo', message); 25 } 26 27 onMsgSocketAPIEmitInfo(): Observable<string> { 28 return new Observable((observer) => { 29 this.socket.on('APIEmitInfo', (message: string) => { 30 observer.next(message); 31 }); 32 }); 33 } 34 35 36 } 
1
  • run code inside zoneCommentedAug 18, 2024 at 23:11

2 Answers 2

0

Beside using async pipe, you could also use checkDetector markForChange

export class AppComponent { logAPIraw: string; constructor(private cdr: ChangeDetectorRef) {} sendemit01() { // Your logic that updates the model this.logAPIraw = 'newValue'; // Mark for check this.cdr.markForCheck(); } } 
    0

    It looks like the zone.js change detection is not catching the change of the value immediately. A blunt workaround would be to to wrap the values update in the NgZone.run() callback or perhaps wrapping the assignment into setTimeout() with no interval parameter will be enough to make changes immediately visible.

    However I would recommend using an async pipe in the template and not to subscribe to an observable in the component (or at least add takeUntilDestroyed to the pipe).

    Template part:

    <div><textarea id="txtarlogAPI" class="border border-red-500 rounded w-full h-96" [value]="logAPIraw$ | async"></textarea></div> 

    Component part:

    logAPIraw$: Observable<string>; .... this.logAPIraw$ = this.socketService.onMsgSocketAPIEmitInfo().pipe( scan((currentState, dt: string) => { currentState.log += `FENSocketioGetCnt: ${++currentState.count}, dataget: ${dt}\n`; return currentState; }, {count: 0, log: ''}), map(({log}) => log), ); 

    Here is a working StackBlitz example: https://stackblitz.com/edit/stackblitz-starters-9bsnfu?file=src%2Fmain.ts

    2
    • Thank you for answering. I am newbie about this. I tried you solution "currentState" value show in console but does not show in the textarea.CommentedAug 22, 2024 at 1:09
    • Sorry for that. It definitely needed to be scan instead of reduce. Updated the answer.CommentedAug 22, 2024 at 21:22

    Start asking to get answers

    Find the answer to your question by asking.

    Ask question

    Explore related questions

    See similar questions with these tags.