2016-02-23 24 views
11

Possiedo un campo di input e quando l'utente digita una stringa di ricerca, desidero attendere che l'utente interrompa la digitazione per almeno 300 millisecondi (antirimbalzo) prima di eseguire una richiesta http di _heroService. Solo i valori di ricerca modificati passano attraverso il servizio (distinctUntilChanged). SwitchMap restituisce un nuovo osservabile che combina questi osservabili _heroService, li riordina nell'ordine di richiesta originale e consegna agli iscritti solo i risultati di ricerca più recenti.Angular2 @ TypeScript Errore osservabile

Sto usando Angular 2.0.0-beta.0 e TypeScript 1.7.5.

Come faccio a funzionare correttamente?

ottengo errore di compilazione:

Error:(33, 20) TS2345: Argument of type '(value: string) => Subscription<Hero[]>' is not assignable to parameter of type '(x: {}, ix: number) => Observable<any>'.Type 'Subscription<Hero[]>' is not assignable to type 'Observable<any>'. Property 'source' is missing in type 'Subscription<Hero[]>'. 
Error:(36, 31) TS2322: Type 'Hero[]' is not assignable to type 'Observable<Hero[]>'. Property 'source' is missing in type 'Hero[]'. 

errore di tempo Run (dopo aver effettuato prima chiave nel campo di ricerca):

EXCEPTION: TypeError: unknown type returned 
STACKTRACE: 
TypeError: unknown type returned 
at Object.subscribeToResult (http://localhost:3000/rxjs/bundles/Rx.js:7082:25) 
at SwitchMapSubscriber._next (http://localhost:3000/rxjs/bundles/Rx.js:5523:63) 
at SwitchMapSubscriber.Subscriber.next (http://localhost:3000/rxjs/bundles/Rx.js:9500:14) 
... 
-----async gap----- Error at _getStacktraceWithUncaughtError 
EXCEPTION: Invalid argument '[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object]' for pipe 'AsyncPipe' in [heroes | async in [email protected]:16] 

test1.ts

import {bootstrap}   from 'angular2/platform/browser'; 
import {Component}   from 'angular2/core'; 
import {HTTP_PROVIDERS} from 'angular2/http'; 

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

import {Hero}    from './hero'; 
import {HeroService}  from './hero.service'; 

@Component({ 
    selector: 'my-app', 
    template: ` 
     <h3>Test</h3> 
     Search <input #inputUser (keyup)="search(inputUser.value)"/><br> 
     <ul> 
      <li *ngFor="#hero of heroes | async">{{hero.name}}</li> 
     </ul> 
    `, 
    providers: [HeroService, HTTP_PROVIDERS] 
}) 

export class Test { 

    public errorMessage: string; 

    private _searchTermStream = new Subject<string>(); 

    private heroes: Observable<Hero[]> = this._searchTermStream 
     .debounceTime(300) 
     .distinctUntilChanged() 
     .switchMap((value: string) => 
      this._heroService.searchHeroes(value) 
       .subscribe(
        heroes => this.heroes = heroes, 
        error => this.errorMessage = <any>error) 
     ) 

    constructor (private _heroService: HeroService) {} 

    search(value: string) { 
     this._searchTermStream.next(value); 
    } 
} 

bootstrap(Test); 

hero.ts

export interface Hero { 
    _id: number, 
    name: string 
} 

hero.service.ts

import {Injectable}  from 'angular2/core'; 
import {Http, Response} from 'angular2/http'; 
import {Headers, RequestOptions} from 'angular2/http'; 
import {Observable}  from 'rxjs/Observable'; 
import 'rxjs/Rx'; 

import {Hero}   from './hero'; 

@Injectable() 

export class HeroService { 

    private _heroesUrl = 'api/heroes'; 

    constructor (private http: Http) {} 

    getHeroes() { 
     return this.http.get(this._heroesUrl) 
      .map(res => <Hero[]> res.json()) 
      .do(data => console.log(data)) 
      .catch(this.handleError); 
    } 

    searchHeroes (value) { 
     return this.http.get(this._heroesUrl + '/search/' + value) 
      .map(res => <Hero[]> res.json()) 
      .do(data => console.log(data)) 
      .catch(this.handleError); 
    } 

    addHero (name: string) : Observable<Hero> { 

     let body = JSON.stringify({name}); 
     let headers = new Headers({ 'Content-Type': 'application/json' }); 
     let options = new RequestOptions({ headers: headers }); 

     return this.http.post(this._heroesUrl, body, options) 
      .map(res => <Hero> res.json()) 
      .do(data => console.log(data)) 
      .catch(this.handleError) 
    } 

    private handleError (error: Response) { 
     // in a real world app, we may send the server to some remote logging infrastructure 
     // instead of just logging it to the console 
     console.log(error); 
     return Observable.throw('Internal server error'); 
    } 
} 

index.html

<!DOCTYPE html> 
<html> 
    <head> 
    <base href="/"> 
    <script src="angular2/bundles/angular2-polyfills.js"></script> 
    <script src="typescript/lib/typescript.js"></script> 
    <script src="systemjs/dist/system.js"></script> 
    <script src="angular2/bundles/router.dev.js"></script> 
    <script src="rxjs/bundles/Rx.js"></script> 
    <script src="angular2/bundles/angular2.js"></script> 
    <script src="angular2/bundles/http.dev.js"></script> 
    <link rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.min.css"> 
    <script> 
     System.config({ 
     transpiler: 'typescript', 
     typescriptOptions: { emitDecoratorMetadata: true }, 
     packages: {'components': {defaultExtension: 'ts'}} 
     }); 
     System.import('components/test1') 
      .then(null, console.error.bind(console)); 
    </script> 
    </head> 
    <body> 
    <my-app>Loading...</my-app> 
    </body> 
</html> 

Ecco un'altra versione 'test2.ts' che funziona bene fare una richiesta HTTP dopo ogni (keyup) evento:

import {bootstrap}   from 'angular2/platform/browser'; 
import {Component}   from 'angular2/core'; 
import {HTTP_PROVIDERS} from 'angular2/http'; 

import {Hero}    from './hero'; 
import {HeroService}  from './hero.service'; 

@Component({ 
    selector: 'my-app', 
    template: ` 
     <h3>Test</h3> 
     Search <input #inputUser (keyup)="search(inputUser.value)"/><br> 
     <ul> 
      <li *ngFor="#hero of heroes">{{hero.name}}</li> 
     </ul> 
    `, 
    providers: [HeroService, HTTP_PROVIDERS] 
}) 

export class Test { 

    public heroes:Hero[] = []; 
    public errorMessage: string; 

    constructor (private _heroService: HeroService) {} 

    search(value: string) { 
     if (value) { 
      this._heroService.searchHeroes(value) 
       .subscribe(
        heroes => this.heroes = heroes, 
        error => this.errorMessage = <any>error); 
     } 
     else { 
      this.heroes = []; 
     } 
    } 
} 

bootstrap(Test); 
+0

Potrebbe fare bene aggiornare i riferimenti angolari 2 poiché non è più in Beta – bilpor

risposta

17

.subscribe(...) restituisce un Subscription, non un Observable. Rimuovere il subscribe(...) o sostituirlo con un .map(...) e utilizzare .subscribe(...) quando si accede per ottenere i valori.

+0

Grazie mille per il vostro aiuto. Puoi essere un po 'più specifico? Gli osservabili sono molto difficili per me. Dove inserisco il blocco '.subscribe (..)'? –

+0

Hai fornito molto codice, non l'ho studiato completamente. Ho trovato '* ngFor =" # eroe di eroi "' che dovrebbe funzionare con '* ngFor =" # eroe di eroi | async "' e async esegue 'subscribe()' per te, altrimenti dove vuoi accedere ai valori 'this.heroes.subscribe (...)'. –

+0

Grazie per la vostra pazienza, sono un novizio ;-) Dove e quanto esatto metto il 'this.heroes.subscribe (...)'? Ho indovinato l'hook del ciclo di vita 'ngOnChanges() {this.heroes.subscribe (...)}' ma questo dà errore di runtime: 'ECCEZIONE: Impossibile trovare un oggetto di supporto diverso '[oggetto oggetto]' in [heroes in Test @ 4: 16] 'e errore di compilazione:' Errore: (39, 23) TS2322: il tipo 'Eroe []' non è assegnabile al tipo 'Osservabile '. La 'fonte' di proprietà manca nel tipo 'Eroe []'. ' –