2016-02-23 13 views
11

Ich habe ein Eingabefeld und wenn der Benutzer einen Suchbegriff eingibt, möchte ich warten, bis der Benutzer die Eingabe für mindestens 300 Millisekunden (debounce) vor einer _heroService HTTP-Anfrage zu stoppen. Nur geänderte Suchwerte schaffen es bis zum Service (distinctUntilChanged). Die switchMap gibt eine neue Observable zurück, die diese _heroService-Observablen kombiniert, sie in ihrer ursprünglichen Anforderungsreihenfolge neu anordnet und den Abonnenten nur die neuesten Suchergebnisse liefert.Angular2 @ TypeScript Observable Fehler

Ich verwende Angular 2.0.0-Beta.0 und TypeScript 1.7.5.

Wie bekomme ich dieses Ding richtig funktioniert?

ich Fehler erhalten kompilieren:

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[]'. 

Laufzeitfehler (nach dem ersten Schlüssel im Sucheingabefeld eingeben):

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> 

Hier ist eine andere Version 'test2.ts', das funktioniert gut eine HTTP-Anfrage nach jedem (keyup) tun:

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

Sie könnten gut tun, um Sie eckig 2 Referenzen zu aktualisieren, da es nicht mehr in Beta ist – bilpor

Antwort

17

.subscribe(...) gibt ein Subscription, kein Observable. Entfernen Sie die subscribe(...) oder ersetzen Sie sie durch eine .map(...) und verwenden Sie .subscribe(...), wenn Sie darauf zugreifen, um die Werte zu erhalten.

+0

Vielen Dank für Ihre Hilfe. Können Sie etwas genauer sein? Observable sind sehr schwierig für mich. Wo stelle ich den '.subscribe (..)' Block? –

+0

Sie haben viel Code zur Verfügung gestellt, ich habe es nicht vollständig untersucht. Ich fand '* ngFor =" # Helden von Helden "', die mit '* ngFor =" # Helden von Helden | async "funktionieren sollten und async macht das' subscribe() 'für dich, sonst wo du auf die Werte zugreifen willst 'this.heroes.subscribe (...)'. –

+0

Vielen Dank für Ihre Geduld, ich bin ein Neuling ;-) Wo und wie genau lege ich 'this.heroes.subscribe (...)'? Ich vermutete den Lifecycle-Hook 'ngOnChanges() {this.heroes.subscribe (...)}' aber das gibt Laufzeitfehler: 'EXCEPTION: Kann ein anderes unterstützendes Objekt '[object Object]' in [Helden im Test nicht finden @ 4: 16] 'und Kompilierfehler:' Fehler: (39, 23) TS2322: Type 'Hero []' kann nicht dem Typ 'Observable ' zugewiesen werden. Die Eigenschaft 'source' fehlt im Typ 'Hero []'. ' –

Verwandte Themen