user9427453
user9427453

Reputation: 45

Angular textarea does not update data on time but the console shows it correctly

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 }







  

Upvotes: 0

Views: 130

Answers (2)

Constantine Poltyrev
Constantine Poltyrev

Reputation: 1034

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

Upvotes: 0

huan feng
huan feng

Reputation: 8623

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

Upvotes: 0

Related Questions