Cody Tolene
Cody Tolene

Reputation: 127

Initializing *ngFor with a new Array()

Angular v5

My search component retrieves data from a service returning a search result promise. On page load my "searchResults" array is empty (= new Array()) until the search is called in page and the promise is returned.

*ngFor is throwing an error because the above mentioned "searchResults" is empty on page load. How do I circumvent this until the search results are populated?

Service:

getSearchResults(searchTerm: string, resourceType: string, pageSize: number = 9999, pageNumber: number = 1): Promise<object> {
const search = {
  Term: searchTerm,
  ResourceType: resourceType,
  PageSize: pageSize,
  PageNum: pageNumber,
  ModuleId: moduleId
};
const searchHeader = {headers: this.getHttpHeaders()};
const promise = new Promise((resolve, reject): void => {
  this.http.post(this.apiRoot, search, searchHeader).toPromise()
    .then(response => {
      resolve(JSON.parse(response['_body']));
    })
    .catch(error => {
      reject(error);
    });
});
return promise;
}

Component:

submitSearch(): void {
this.siteSearchService.getSearchResults(this.searchString, this.selectedResourceType)
  .then(response => {
    let dto: DTO.SearchResults = <DTO.SearchResults>response;
    this.totalHits = dto.TotalHits;
    this.searchResults = dto.Results;
  }).catch(error => {
    console.log(error);
  });
}

Template:

<div class="row" *ngIf="searchResults" style="margin:25px;">
    <div class="col-xs-12 result-row" *ngFor="let result in searchResults">
      <div class="row">
        <div class="col-xs-12 col-sm-10">
          <a href [href]="result.Url" target="_blank" class="search-result-link">
            <h3>{{result.Title}}</h3>
          </a>
          <p>{{result.Description}}</p>
        </div>
        <div class="col-xs-12 col-sm-2">
          ToDo: Button
        </div>
      </div>
    </div>
  </div> 

Upvotes: 1

Views: 1855

Answers (2)

Srinivas Valekar
Srinivas Valekar

Reputation: 1123

Just try with *ngIf="searchResults" and also in your code replace *ngFor="let result in searchResults" with *ngFor="let result of searchResults"

Hope that solves your problem

Upvotes: 2

BeetleJuice
BeetleJuice

Reputation: 40886

My fix would be to use the async pipe. This will let Angular know to wait for the results to arrive before trying to loop with ngFor. I would turn searchResults into an observable that emits results at the end of each search.

In the view, emit an event each time the button is clicked:

<input #searchInput />
<button (click)="searchSubject.next(searchInput.value)">Search</button>

In the component class:

// Will emit each new search term when user clicks search button
searchSubject = new Subject<string>();

// Will trigger the search each time the searchSubject emits:
searchResults = searchSubject
                 .switchMap(term => this.siteSearchService.getSearchResults(term));
                 .map(response => response.Results)
                 .catch(err => console.error(err));

Finally modify the template to use the pipe:

<div *ngFor="let result of searchResults | async">

The async pipe will subscribe to searchResults and start listening to the click event. Once your component is destroyed, Angular will unsubscribe for you.

Upvotes: 2

Related Questions