Search code examples
angularasynchronousangular-ng-if

Angular4 ngIf is not updating


The template is not detecting changes to the stock model when checkbox variable is set to true. I've tried use change detection but not sure if I was implementing correctly, so I've removed from my code example for now. Oddly enough, I wasn't having an issue before I started pulling data from a remote service.

Here is my plunker example: http://plnkr.co/edit/0EfkaSpk3Ag9p5NH5jJ2?p=preview

Here is my control:

    <tr *ngFor="let stock of stocksObservable | async;">
      <td>
        <input [checked]="stock.isChecked" (change)="stock.isChecked = !stock.isChecked;onSelectStock($event);" type="checkbox"> <a href="">{{ stock.number }}</a>  {{stock.isChecked}} 
      </td>
    </tr>

Within my ng-container, I should see stocks that I have selected. Instead, I see no changes or updates...

<table>
  <tr>
    <th>Is Checked</th>
  </tr>
  <ng-container *ngFor="let stock of stocksObservable | async;">  
    <tr *ngIf="stock.isChecked">
      <td>
        <a href="">{{stock.number}}</a> {{stock.isChecked}} 
      </td>
    </tr>
  </ng-container>
</table>

Here's my class:

//services
branchesObservable : Observable<object> ; 
stocksObservable : Observable<object> ; 

isChecked: boolean;
selectable: boolean;

constructor( private stocksService: StocksService ) { 
  this.branchesObservable = this.stocksService.get_branches();
}

//fetch stock data into table
getBranchStocks(value){
  this.stocksObservable = this.stocksService.get_stocks(value);

}

//select stocks
onSelectStock(event) {
  this.isChecked = !this.isChecked;
}

Solution

  • I think you are using the observables in a wrong way. Observables are used to emit objects. So, when your object inside the observable is updated when checkbox is clicked its not the same object which is used in the next ngFor and hence not reflected as its subscription model, kind of two subscriptions.

    I created a local variable stocks which is updated or assigned to via subscribe method.

    I have modified plnkr code: http://plnkr.co/edit/7WBNDX53pCbf44QnlRo4?p=preview.

        // import { Component, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
        import { Component, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
        import { HttpClient } from '@angular/common/http';
    
        import { StocksService } from './stocks.service';
        import { Observable } from 'rxjs/Observable';
    
    
    
        @Component({
          selector: 'app-root',
          template: `
            <div class="wrapper">
              <select #branch (change)="getBranchStocks(branch.value)">
                <option value="null">select a branch</option>
                <option *ngFor="let branch of branchesObservable | async" value="{{ branch.branchId }}">{{ branch.name }}</option>
              </select>
              <table style="width:100%;">
                <tr>
                  <th>Stock</th>
                </tr>
                <tr *ngFor="let stock of stocks">
                  <td>
                    <input [checked]="stock.isChecked" (change)="stock.isChecked = !stock.isChecked;onSelectStock($event);" type="checkbox"> <a href="">{{ stock.number }}</a>  {{stock.isChecked}} 
                  </td>
                </tr>
              </table>
              <table style="width:49%;float:left;border:0;">
                <tr>
                  <th>Is Checked</th>
                </tr>
                <ng-container *ngFor="let stock of stocks">
                  <tr *ngIf="stock.isChecked">
                    <td>
                      <a href="">{{stock.number}}</a> {{stock.isChecked}} 
                    </td>
                  </tr>
                </ng-container>
              </table>
            </div>
          `,
        })
    
        export class AppComponent  {
    
            //services
            branchesObservable : Observable<object> ; 
            stocksObservable : Observable<object> ; 
            stocks:object;
    
            isChecked: boolean;
            selectable: boolean;
    
            constructor( private stocksService: StocksService ) { 
              this.branchesObservable = this.stocksService.get_branches();
            }
    
            //fetch stock data into table
            getBranchStocks(value){
              this.stocksObservable = this.stocksService.get_stocks(value);
              this.stocksObservable.subscribe(data=>this.stocks=data);
            }
    
            //select stocks
            onSelectStock(event) {
              this.isChecked = !this.isChecked;
            }
    
    
        }
    

    I guess you are dealing with stocks which needs local updates as well as continuous updates from subscription as well. So, the ideal way would be to use BehaviorSubject in RxJS. A nice example of it can be found on https://coryrylan.com/blog/angular-observable-data-services