Angular component does not update correctly using @Input and a Service

Viewed 39

I'm using Angular version 7 with a component that has a serie of divs being rendering according to the data coming from an @Input and it should be updated due the service response, nevertheless the component is not correctly updated, the loader remains loading (loading=true) until I click into the text input and then I click elsewhere.

Template:

<div class="row" *ngFor="let message of messages">
  <div>{{message.date}}</div>
  <div>{{message.body}}</div>
</div>
<div class="row" *ngIf="thread.isOpen">
  <div class="panel" >
    <form (ngSubmit)="onSubmit()" [formGroup]="messageForm">
      <textarea class="textarea" name="message" id="message" formControlName="message"></textarea>
      <button class="btn"">
      <span *ngIf="!loading; else loadingTemplate">send</span>
      <ng-template #loadingTemplate>
        <i class="fa fa-spin"></i>
      </ng-template>
    </button>
 </form>
</div>

Component:

@Input() messages;
messageForm: FormGroup;
loading: boolean = false;

ngOnInit(): void {
    this.loadFromControls();
}

loadFromControls(): void {
  this.messageForm = this.formBuilder.group({
    message: this.formBuilder.control('', [])
  });
}

onSubmit() {
  if (!this.messageForm.valid) return;
    this.loading = true;
    const message = this.messageForm.controls['message'].value;
  
    this.service.sendMessage(this.thread._id, message).subscribe(() => {
      if (this.messages) {
        this.messages.push({
            isFromExtranet: true,
            date: new Date(),
            body: message
        });
      }

      this.messageForm.controls['message'].setValue("");
      this.loading = false;
    }, (err) => this.errorMessage(err));
  }

Service

sendMessage(id: string, message: string): Observable<any> {
  return this.http.post(`/api/messages/${id}`, { message })
        .map((res) => res.json())
        .catch((error) => Observable.throw(error.json().code || 'error'));
}

I've been dealing with this a few hours and maybe I'm just missing something very basic.

2 Answers

Can you try update the code as per below and make sure that you're getting correct value from @Input decorator.

<div *ngIf="isDataloaded>    
<div class="row" *ngFor="let message of messages">
      <div>{{message.date}}</div>
      <div>{{message.body}}</div>
    </div>
    <div class="row" *ngIf="thread.isOpen">
      <div class="panel" >
        <form (ngSubmit)="onSubmit()" [formGroup]="messageForm">
          <textarea class="textarea" name="message" id="message" formControlName="message"></textarea>
          <button class="btn"">
          <span *ngIf="!loading; else loadingTemplate">send</span>
          <ng-template #loadingTemplate>
            <i class="fa fa-spin"></i>
          </ng-template>
        </button>
     </form>
    </div>
</div> 

Component.ts

 @Input() messages;
    isDataloaded = flase;
    messageForm: FormGroup;
    loading: boolean = false;
    
    ngOnInit(): void {
        this.loadFromControls();
    }
    
    loadFromControls(): void {
      this.messageForm = this.formBuilder.group({
        message: this.formBuilder.control('', [])
      });
    }
    
    onSubmit() {
      this.loading = true;
      if (!this.messageForm.valid) return;
        const message = this.messageForm.controls['message'].value;
      
        this.service.sendMessage(this.thread._id, message).subscribe(() => {
          if (this.event.data.discussionThread.messages) {
            this.event.data.discussionThread.messages.push({
                isFromExtranet: true,
                date: new Date(),
                body: message
            });
          }
    
          this.messageForm.controls['message'].setValue("");
          this.loading = false;
        }, (err) => this.errorMessage(err));
         this.isDataloaded = true;
      }

I fixed using a BehaviorSubject, as:

loading$: BehaviorSubject<boolean> = new BehaviorSubject(false);
...
this.loading$.next(true);

instead of:

loading: boolean = false;
...
this.loading = true;

and in the template with async as:

<span *ngIf="!(loading$ | async); else loadingTemplate">
Related