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!
source to share
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.
source to share
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.
source to share