Explosion Pills Explosion Pills - 17 days ago 6
TypeScript Question

Call ViewChild methods with updated nested value in Angular2

I am using the angular2-progressbar library to display some circular progress bars in my app. I have a list of inventory items and a service that updates the inventory list. Each inventory item component gets the updated inventory value from a key in the inventory list like so:

export class InventoryComponent implements OnInit {
@ViewChild(CircleProgressComponent): circleComp: CircleProgressComponent;
@Input() deviceKey: string;
private circleOptions: ShapeOptions { /* from angular2-progressbar example */ }
constructor(private inventoryService: InventoryService) { }
ngOnInit(): void {
this.deviceList = this.inventoryService.deviceList;
setInterval(() => {
// this value will be 0 or 1
this.circleComp.animate(this.deviceList[this.deviceKey]);
}, 2000);
}
}


While this does work, it seems very hacky to me to have the interval check only after every 2 seconds when the actual value in the template itself gets updated immediately whenever
inventoryService
updates its device list.

I tried implementing
OnChange
, but there are two problems:


  1. The initial
    change
    fires before the view init lifecycle, i.e. before
    circleComp
    is available. This breaks things.

  2. Because
    this.deviceList
    is an object and its properties get updated internall,
    OnChange
    does not get fired anyway. Instead I can do an additional check using
    ngDoCheck
    -- this does work, but it is a lot more verbose and #1 still causes a problem.



Is there any way to start performing checks after the view has loaded?

Answer

Is there any way to start performing checks after the view has loaded?

Use the AfterViewInit lifecycle hook (override ngAfterViewInit). This is when the @ViewChild is available

I think there may be a better solution than polling deviceList. You could use a Subject in the service. This will act like an observer and observable, so you can subscribe to it on one end, and publish on the other. For example

import { BehaviorSubject } from 'rxjs/BehaviorSubject'

class InventoryService {
  private _inventoryList = new BahaviorSubject<any>({ initialize with data })

  inventoryList$ = this._inventoryList.asObservable();

  set inventoryList(list: any) {
    this._inventoryList.next(list);
  }
}

Now you can just subscribe to the list. And whenever you want to update it, just use the setter, and all the subscribers will get the updated list. No need to poll.

Subscriber

service.inventoryList$.subscribe(list => {})

Publisher

service.inventoryList = newList

See also:

Comments