greg
greg

Reputation: 1873

Accessing the object of an observable in the component (not the HTML Template)

I am building a MATERIAL autocomplete for an angular project. Modeling after this Stackblitx... https://stackblitz.com/run?file=src%2Fapp%2Fautocomplete-overview-example.ts

The example creates an array of 5 states. I would like to pull an array of accounts from a service.

The example is clear in its intent. There is an observable on the text control value. When the text control value changes, the observable calls a method to filter the larger array of states into an array where the name contains what has been typed so far into the text box.

I am stumbling on the difference in that I do not have a complete array of accounts. I have an observable of a complete array of accounts.

 private _filterStates(value: string): State[] {
    const filterValue = value.toLowerCase();
    ///**** This works b/c states is an array  of states 
    return this.states.filter(state => state.name.toLowerCase().includes(filterValue));
  }

  private _filterAccount(value: string): COA_Account[] {
    const filterValue = value.toLowerCase();
     //this is not working because the Observable of Accounts[] does not have a .filter method.
   //  it isnt an array! its an observable. 
    return this.Accounts$.filter(acct => acct.name.toLowerCase().includes(filterValue)
                           
                      );
  }

My question is, how do I access the content of the observable in the class. I know how to do it in the html. I feel like I am missing something basic here.

My approach so far is to also define an array and populate that when the observable completes

  ngOnInit(): void {

    this.Accounts$ = this.myAcctSvc.getCOAByProjectId(4200).pipe(
         switchMap((data : COA_Header) =>{
         
          //this works, but Im not sure its the correct way
          this.myAccounts  = data.Accounts;

          return of(this.myAccounts);
        }));
  }

MY QUESTION IS Is there a way to access the Account[] array via the Accounts$ observable.

Upvotes: 1

Views: 55

Answers (1)

pthor
pthor

Reputation: 623

I would suggest changing your approach to staying in the observable stream as much as possible. It looks like you probably are already subscribing to Accounts$ in the template. That is great. What you can do is use RXJS to manipulate the stream in a way that will react to the input of the user.

I would suggest using a reactive form for your input, and combine the input stream with the accounts stream like so:

form = this.fb.group({ query: [''] });

// Every new query will retrigger this stream
accounts$ = combineLatest([
  this.myAcctSvc.getCOAByProjectId(4200),
  this.form.controls.query.valueChanges.pipe(
    // Start with initial value ('') as valueChanges doesn't emit anything to start
    startWith(this.form.controls.query.value)
  ),
]).pipe(
  map(([accounts, query]) =>
    // If there is a query, do the filter, else return all accounts query
      ? accounts.filter((acct) =>
          acct.name.toLocaleLowerCase().includes(query)
        )
      : accounts
  )
);

See this stackblitz

Upvotes: 1

Related Questions