The assignment of Type Observable<Observable<any[]>> to Observable<any[]> is not valid

Working on implementing autocomplete using data from a database service:

@Injectable()
export class SchoolService {
  constructor(private db: AngularFirestore) {
  }

  getSchools(): Observable<School[]> {
    return this.db.collection<School>('schools').valueChanges();
  }
}

In my component:

export class SchoolComponent implements OnInit {
  formControl: FormControl = new FormControl();
  schools: Observable<School[]>;
  filteredSchools: Observable<School[]>;

  constructor(private schoolService: SchoolService) {
  }

  ngOnInit() {
    this.schools = this.schoolService.getSchools();

    //Encountering error "Type Observable<Observable<School[]>> is not assignable to type Observable<School[]>" with the line below.
    this.filteredSchools = this.formControl.valueChanges.pipe(
      startWith(''),
      map(name => this.filterSchools(name))
    );
  }

  filterSchools(name: string): Observable<School[]> {
    return this.schools.map(school => school.filter(s => s.name.toLowerCase().includes(name)));
  }
}

Here's the corresponding html code:

<form>
  <mat-form-field>
    <input type="text" matInput placeholder="Type Your School to Continue" [matAutocomplete]="auto"
           [formControl]="formControl">
    <mat-autocomplete #auto="matAutocomplete">
      <mat-option *ngFor="let school of filteredSchools | async" [value]="school">
        <span>{{school.name}}</span> |
        <small>{{school.city}}</small>
      </mat-option>
    </mat-autocomplete>
  </mat-form-field>
</form>

The issue causing

Type Observable<Observable<School[]>> is not assignable to type Observable<School[]>
error is in the ngOnInit function. How should I resolve this?

Answer №1

The issue is clear from the error message. You initially defined:

filteredSchools: Observable<School[]>;

However, later on you created an Observable that emits (maps each value) results of

filterSchools(name: string): Observable<School[]>
, which also returns an Observable. This leads to a chain of Observables as
Observable<Observable<School[]>>
.

It seems like what you actually intended was to use switchMap (or possibly mergeMap or concatMap) instead of map. Using switchMap will subscribe to the nested Observable and simplify the chain to Observable<School[]>:

this.filteredSchools = this.formControl.valueChanges.pipe(
  startWith(''),
  switchMap(name => this.filterSchools(name))
);

Answer №2

If you are looking for a suggestion, I would recommend utilizing Observable.combineLatest(...)

public ngOnInit(): void {
   this.schools = this.schoolService.getSchools();

   this.filteredSchools = Observable.combineLatest(
      this.formControl.valueChanges.startWith(''),
      this.schools
   ).map(([filter, schools]) => {
       if (!filter || filter === '') {
          return schools;
       }

       return schools.filter(s => s.name.toLowerCase().includes(name));
   });
}

By having a cold observable at the end, it will only be triggered when one of the observables emits new data.

On a different note, although not related to your initial question, make sure to utilize a displayWith function for autocomplete functionality in your templates.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Tips for providing a base URL in Angular to fetch images from an API

How do I access the API URL to retrieve images from a database? <table class="table"> <tr> <th>Id</th> <th>Image</th> </tr> ...

What is the best way to link three different types of http requests in succession and adaptively?

Is it possible to chain together 3 types of http requests correctly, where each request depends on data from the previous one and the number of required requests can vary? In my database, there is a team table with a corresponding businessId, a supervisor ...

Error encountered in Angular/Ramda while using mapAccum with an array of objects in Typescript

I am having difficulties implementing Ramda in an Angular 6 / TypeScript environment. "ramda": "^0.25.0", "@types/ramda": "^0.25.24" This is how I have started: const addP = (p1,p2) => ({ x: p1.x+p2.x,y: p1.y+p2.y }); const accum = (a,b) => [add ...

Tips for designing a custom TypeScript 5 property decorator

I have a decorator in TypeScript: const bindMethod = (method: any): PropertyDecorator => ((target: any, name?: PropertyKey): any => { if(name === undefined) { throw new Error('Bound decorator must be used with a property name.& ...

Angular and PrimeNG's P-dialog positioning feature seem to be having trouble coordinating effectively,

I've been attempting this with no success, utilizing Angular 8 and Primeng version 9.0.0-rc.4. Thank you for your help. <p-dialog position="right" header="Change Password" (visible)]="display"> Content </p-dialog> Check out more her ...

"Encountering issues with Angular2's FormBuilder and accessing nested object properties,

As I dip my toes into TypeScript and Angular2, I find myself grappling with a nested object structure in an API. My goal is to align my model closely with the API resource. Here's how I've defined the "Inquiry" model in TypeScript: // inquiry.ts ...

Discovering class methods in typescript

Currently, I am running TypeScript unit tests using Mocha Chai after configuring the compiler options to ts-node. Within one of my unit tests, I am seeking a way to retrieve all methods from a utility class that I have designed and execute the same set of ...

Tips for efficiently saving data using await in Mongoose

Currently, the code above is functional, but I am interested in utilizing only async/await for better readability. So, my query is: How can I convert cat.save().then(() => console.log('Saved in db')); to utilize await instead? The purpose of ...

How to Retrieve and Display HTTP Response Body in Angular 8

Previously, when I was using Angular 7, I would retrieve the body of the response like this: import { Http } from '@angular/http'; constructor(public http: Http...) this.http.get(myURL).subscribe( (data) => { const myURL_body = data[& ...

How to specify the file path for importing a custom module?

I am currently learning Angular 2 and encountering an issue with importing a custom module that contains interface declarations. Here is my folder structure: https://i.stack.imgur.com/heIvn.png The goal is to import product.interface.ts into a component ...

405 we're sorry, but the POST method is not allowed on this page. This page does

I'm currently working on a small Form using the kit feature Actions. However, I'm facing an issue when trying to submit the form - I keep receiving a "405 POST method not allowed. No actions exist for this page" error message. My code is quite st ...

Tips for showcasing nested objects in Angular components

I'm faced with a situation where there is an object containing several nested objects, each with their own set of values. How can I display the key values from this complex data structure? I suspect using *ngFor might not provide the solution. const d ...

When sending a JSON string in an HTTP request with Express, the req.body variable may be empty

I'm facing an issue where Express is receiving an empty JSON string {} and I've been struggling to identify the cause. I've attempted using both bodyParser and express.json for the JSON parser, but the result remains the same. I've also ...

Angular: merging object values into a single string by looping over them

i am dealing with the following data : "commercialRanges": [ { "rangeId": "700", "rangeName": "POSTPAID" }, { "rangeId": "500", "rangeName": "PREPAID" }, ] In my view, I am aiming to display the ...

Hiding a specific tag with vanilla JavaScript based on its content

I am facing a challenge with my code that is supposed to hide div elements containing a specific word along with additional text. I have tried multiple solutions but none seem to work effectively. Any assistance on how to hide divs properly will be greatl ...

Modifying an Angular Component Template following an API Response

As someone relatively new to Angular, I come with some experience from working with other JavaScript frameworks like Vue and React. Currently, I am developing an Angular Lab application that interacts with 2 APIs to retrieve information. After receiving th ...

Optimizing TypeScript/JavaScript for both browser and Node environments through efficient tree-shaking

I am currently tackling a TypeScript project that includes multiple modules shared between a browser client and a Node-based server. Our goal is to bundle and tree-shake these modules using webpack/rollup for the browser, but this requires configuring the ...

In React Typescript, the input type="checkbox" does not show any value in the value attribute

I'm facing an issue with displaying text next to a checkbox in React Typescript. When I try to use the value attribute, it doesn't seem to work as expected. Also, attempting to set innerHTML throws an error stating that input is a void element ta ...

Upon executing the `npm start` command, the application experiences a crash

When I tried following the steps of the Angular quickstart guide, I encountered some errors after running "npm start". Here are the errors displayed: node_modules/@angular/common/src/directives/ng_class.d.ts(46,34): error TS2304: Cannot find name 'Se ...

Make sure that the input for a function is a valid key within a specific interface, and that the corresponding value in the interface is

I'm a beginner in TypeScript and I've hit a roadblock with this issue. Despite searching extensively, I haven't found a solution yet. My goal is to create a well-typed sorting function that takes two parameters: an array of objects and the ...