Angular v4: Search by name with observable

I am using Angular v4 and I want to add the ability to search by name, so I created a service that allows me to get a list of movies:

import { Injectable }     from '@angular/core';
import { Http, Response } from '@angular/http';
import { Observable }     from 'rxjs/Observable';
import { Movies }         from '../models/movies';
import { environment }    from '../../../environments/environment';

import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';
import 'rxjs/add/observable/throw';

@Injectable()
export class SearchService {

  constructor(private http: Http) { }

  searchMovie(query: string): Observable<Movies> {
    return this.http.get(environment.api.baseUrl + environment.api.searchMovie.uri + "?query=" + query)
                    .map((res:Response) => res.json() as Movies)  // Process the success response object
                    .catch((error:any) => Observable.throw(error.json().errors || 'Server error'));  // Process the error response object
  }

}

      

This service is very simple because it sends lookups to the server and returns an Observable. After this my html, I use reactive form:

<section class="search-section">
  <div class="container">
    <div class="row">
      <div class="col-xs-12">
        <form class="form-search" [formGroup]="searchForm" novalidate>
          <div class="form-group input-group">
            <span class="input-group-addon" id="basic-addon1"> <span class="glyphicon glyphicon-search" aria-hidden="true"></span> </span>
            <input type="text" id="key" class="form-control" aria-describedby="basic-addon1" placeholder="Search for a movie..." formControlName="key">
          </div>
        </form>
      </div>
    </div>
  </div>
</section>

      

Now this is my component:

import { Component, OnInit }      from '@angular/core';
import { FormBuilder, FormGroup } from "@angular/forms";

import { Movies }        from '../../models/movies';
import { Movie }         from '../../models/movie';
import { SearchService } from '../../services/search.service';

import { Observable } from 'rxjs/Observable';
import { Subject }    from 'rxjs/Subject';

import 'rxjs/Rx';

@Component({
  selector: 'search',
  templateUrl: './search.component.html',
  styleUrls: ['./search.component.scss']
})
export class SearchComponent implements OnInit {

  private searchForm: FormGroup;
  private searchTerms = new Subject<string>();
  private errorMessage: string;
  private matchedMovies: Array<Movie> = [];

  constructor(private fb: FormBuilder, private searchService: SearchService) {
    this.createForm();    
  }

  createForm(): void {
    this.searchForm = this.fb.group({
      key: ['']
    });

    // Check the changes
    this.searchForm.valueChanges
        .debounceTime(300)
        .distinctUntilChanged()   // ignore if next search query is same as previous
        .switchMap(query => this.searchService.searchMovie(query.key))
        .subscribe(
          (result) => {
            console.log(result);
          },
          error =>  {
            this.errorMessage = <any>error;
            console.log(this.errorMessage);
          },
          () =>  {
            console.log("onCompleted");
          });
  }

  ngOnInit() { }

}

      

When I search for a movie, it works very well, but when the user submits an empty string, I get a 422 error from the server (that's correct), but the subscription doesn't work after that error. I expect it should work ... It's a quick and easy plunker: http://plnkr.co/edit/Apb3x30Fbggpw4FsEAwJ?p=preview Thanks!

+3


source to share


2 answers


You have not caught the error caused by this.searchService.searchMovie(query.key)

You must catch the error or filter the input value before sending it to the server.

catch example:

this.searchField.valueChanges
      .debounceTime(400)
        .switchMap(term => this.searchService.search(term).catch(e=>Observable.empty()))
        .subscribe(result => {
            console.log(result);
        });

      



filtering example:

this.searchField.valueChanges
      .debounceTime(400)
      .filter(term=>term && term.trim().length > 0)
        .switchMap(term => this.searchService.search(term)))
        .subscribe(result => {
            console.log(result);
        });

      

Or even better, you can use both methods :)

here is your plunker edited.

+2


source


You just need a catch:

  search(term: string) {
    return this.http.get("https://api.themoviedb.org/3/search/movie?api_key=fed69657ba4cc6e1078d2a6a95f51c8c&query=" + term)
      .map(response => response.json())
      .catch(error => '')
  }

      



Here's the updated plunker: http://plnkr.co/edit/j1ggvND1wBYipbkZVOv2?p=preview

Basically, the catch allows us to safely recover from an api error, whereas without the catch, the application breaks.

+1


source







All Articles