Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Observable with initial value

Tags:

angular

Using observable, I want to filter and display a list. The input event is fired only when the user starts typing. Therefore the list is not displayed at the first place. How can I assign a default value to the observable this.filterLocation$ until the inputEvent starts to be triggered?

template

<ng-template ngFor let-location [ngForOf]="filterLocation$ | async">
        <a mat-list-item href="#">{{location}}</a>
      </ng-template>

component

ngAfterViewInit() {
const searchBox = document.querySelector('#search-input');
this.filterLocation$ = fromEvent(searchBox, 'input')
  .pipe(
    map((e: any) => {
      const value = e.target.value;
        return value ? this.locations
          .filter(l => l.toLowerCase().includes(value.toLowerCase()))
          : this.locations;
      }),
      startWith(this.locations)
  )
 }
}

Using startWith makes the list to be displayed initially. But the following error is thrown:

Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'ngForOf: null'. Current value: 'ngForOf: name1,name2'.

live code

like image 617
edkeveked Avatar asked May 28 '18 22:05

edkeveked


People also ask

How do you find the initial value of a subject?

If you want a Subject that has an 'initial value', a better RxJS class to use is BehaviorSubject. With BehaviorSubject, you pass your initial value into its constructor. Save this answer.

What is observable value?

An ObservableValue is an entity that wraps a value and allows to observe the value for changes. In general this interface should not be implemented directly but one of its sub-interfaces ( ObservableBooleanValue etc.). The value of the ObservableValue can be requested with getValue() .

What is observable in Angular real time example?

Observable in Angular is a feature that provides support for delivering messages between different parts of your single-page application. This feature is frequently used in Angular because it is responsible for handling multiple values, asynchronous programming in Javascript, and also event handling processes.


1 Answers

Initial value can be provided to an observable with startWith operator, as it was already mentioned in now-deleted answer.

The problem is that filterLocation$ is assigned too late, after filterLocation$ | async was evaluated to null. Since the change occurs on same tick, this results in change detection error (though ExpressionChangedAfterItHasBeenCheckedError can be considered a warning if its occurrence is expected).

The solution is to move the code from ngAfterViewInit to ngOnInit, before change detection was triggered.

This is not always possible. An alternative is to provide a value asynchronously, so it doesn't interfere with initial change detection.

By delaying the whole observable with delay operator (acceptable solution for user input because it's not time critical):

  this.filterLocation$ = fromEvent(searchBox, 'input')
  .pipe(
    map((e: any) => { 
      const value = e.target.value;
        return value ? this.locations
          .filter(l => l.toLowerCase().includes(value.toLowerCase()))
          : this.locations;
    }),
    startWith(this.locations),
    delay(0)
  )

Or by making initial value asynchronous with a scheduler:

import { asyncScheduler } from 'rxjs'
...

  this.filterLocation$ = fromEvent(searchBox, 'input')
  .pipe(
    map((e: any) => { 
      const value = e.target.value;
        return value ? this.locations
          .filter(l => l.toLowerCase().includes(value.toLowerCase()))
          : this.locations;
    }),
    startWith(this.locations, asyncScheduler)
  )
like image 102
Estus Flask Avatar answered Sep 19 '22 06:09

Estus Flask