web-dev-qa-db-de.com

Angular2 @ TypeScript Beobachtbarer Fehler

Ich habe ein Eingabefeld und wenn der Benutzer eine Suchzeichenfolge eingibt, möchte ich warten, bis der Benutzer die Eingabe für mindestens 300 Millisekunden (Entprellen) abgebrochen hat, bevor ich eine _heroService-http-Anfrage mache. Nur geänderte Suchwerte gelangen zum Service (distinctUntilChanged). Die switchMap gibt eine neue Observable zurück, die diese _heroService-Observables kombiniert, in der ursprünglichen Anforderungsreihenfolge neu anordnet und den Abonnenten nur die neuesten Suchergebnisse liefert.

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

Wie kann ich dieses Ding richtig machen?

Ich bekomme einen Kompilierungsfehler:

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 Eingabe der ersten Taste im Suchfeld):

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', die nach jedem (keyup) -Ereignis eine http-Anforderung ausführt:

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);
12
Herman Fransen

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

18