1

I am trying to extract firstname fields from a json response service with angular and return an array full of firstnames.

this the the json object :

{
    "hits": {
        "hits": [
            {
                "_id": "BKZujHgB0urOc7uDCrf5",
                "_index": "names",
                "_score": 1.0,
                "_source": {
                    "firstname": "Alicia"
                },
                "_type": "_doc"
            },
            {
                "_id": "BaZujHgB0urOc7uDL7e2",
                "_index": "names",
                "_score": 1.0,
                "_source": {
                    "firstname": "Alice"
                },
                "_type": "_doc"
            }
        ]
        
}

i have created a service to consume the webservice that returns the json object without using observables. appService.ts

public autoComplete(name: string) {

    const params = new HttpParams()
        .set('name', name);
    return this.httpClient.get(this.host, { params});
  

and in the app.component.ts I Have created a function that calls the service and implemented it in ngOnInit() ,i have followed the autocomplete angular material with a little modification . but i am always having errors of Cannot read property 'hits' of undefined

ngOnInit() {
   this.seachText.valueChanges.pipe(
      startWith(''),
      // delay emits
      debounceTime(300),
      // use switch map so as to cancel previous subscribed events, before creating new once
      map(value => this.lookup(value))
      ).subscribe(value => this.results$ = value);
   this.names = this.results$.hits.hits.map(h => h._source.firstname);

   console.log('resultat :', this.names);

  } 

i dont know how to correct the error but i guess that i am wrong with the way that i am using to extract the data from the json object comming from the service

2 Answers2

1

The problem is that your stream is asychronous, so when names is assigned, is certainly never has emitted yet.

To fix that, you can use a second map operator for example. This way you change the output to your liking and the end value is what you expected.

this.searchText.valueChanges
      .pipe(
        startWith(""),
        debounceTime(300),
        map(value => this.appService.autoComplete(value)),
        map(results => results.hits.hits.map(hit => hit._source.firstname))
      )
      .subscribe(value => (this.results = value));

Check out this repoduction on StackBlitz : https://stackblitz.com/edit/stackoverflow-67142850

If ever you want your autoComplete method to return an observable, just replace map with switchMap when consuming it.

Simon
  • 81
  • 6
  • thanks for ur response , but still the same problem **Cannot read property 'hits' of undefined** this autocomplete method `public autoComplete(name: string) { const params = new HttpParams() .set('name', name); return this.httpClient.get(this.host, { params}); }` i dont know where the problem exist – Med Nadhir afsa Apr 17 '21 at 23:19
  • I'm afraid that without providing more context we won't be able to help more on that. – Simon Apr 17 '21 at 23:30
  • ok @Simon , this is my app.component.html ` {{option}} ` tslint make an underline under hits and tell me that **"Property 'hits' does not exist on type 'Observable '"** – Med Nadhir afsa Apr 17 '21 at 23:56
  • For the lint, you should either strongly type your object or use any. As for the runtime error, firstly you should remove the async pipe since results is not an Observable. – Simon Apr 18 '21 at 08:50
0

First, let me use a code snippet to simplfied the error.

let a;

of(2).subscribe((res) => {
   a = res;
   console.log("res");  // Async code, executed after sync code
})

console.log(a); // syn code. This log is excuted first

The output will be

// undefiend 
// 2;

In your case, you use results.hits before it receives a value. What it actually do is read hits of undefiend. This is where the error occurs.

To fix the error, you need to consider move this.names = this.results$.hits.hits.map(h => h._source.firstname); to inside of your subscription to obverable or use obersable operators to set the value asynchrounsly.

Xie Dongfeng
  • 131
  • 3
  • i tried to move `this.names = this.results$.hits.hits.map(h => h._source.firstname);`to inside of my subscription but still the same error **Cannot read property 'hits' of undefined**this is my full ngOnInit() `ngOnInit() {this.searchText.valueChanges.pipe( startWith(''),debounceTime(300),map(value => this.appService.autoComplete(value)),).subscribe((value) => {this.results = value; this.names = this.results.hits.hits.map(h => h._source.firstname);});} ` – Med Nadhir afsa Apr 18 '21 at 06:43