Angular2 - 使用Switchmap

时间:2017-01-31 10:06:33

标签: angular typescript

我正在使用Angular 2进行术语搜索。它与英雄教程中使用的代码相同。

我的服务:

search(term: string = null, page: string = null, limit: string = null): Observable<Bibliographie[]> {
    let params = new URLSearchParams();
    if (term) params.set('q', term);
    if (page) params.set('_page', page);
    if (limit) params.set('_limit', limit);
    return this.http
        .get('http://localhost:3000/bibliographie', {search: params})
        .map(response => response.json() as Bibliographie[])
        .catch(this.handleError);
}

我的组件:

ngOnInit(): void {

    this.bibliographies = this.searchTerms
        .debounceTime(300)
        .switchMap(term => term
            ? this.bibliographieSearchService.search(term)
            : this.bibliographieSearchService.search(null, "1", "20"))
        .catch(error => {
            console.log(error);
            return Observable.of<Bibliographie[]>([]);
        });
}

如果我在输入对话框中输入一些文本,它会很好用。问题是,我想在输入任何内容之前得到一些结果。在上面的代码中,我添加了行

: this.bibliographieSearchService.search(null, "1", "20"))

为此目的,希望当term为null时显示一页结果。但它只在我清除输入对话框时才有效。我还尝试使用其他服务功能在init上加载数据。但是,搜索不再起作用了。

有什么想法吗?

2 个答案:

答案 0 :(得分:1)

你的解决方案的问题是this.searchTerms可能是valueChanges的Observable,所以在输入内的值没有改变之前,任何事件都会被发出。

假设您希望每次输入被聚焦时触发搜索,并且每次值都在改变时触发搜索。

因此,在这种情况下,您还需要监听焦点事件,以便在输入触发焦点事件时发出值。但是你总是需要取valueChanged发出的最后一个值。在这种情况下,combineLatest运算符是有意义的,因此您可以同时关注焦点和值的变化。 combineLatest运算符会发出每个源Observable的最新值,因此即使focus事件是发出的事件,您也会收到来自this.searchTerms的最后一个事件。

为了能够收听焦点事件,您需要在输入中使用@ViewChild,以获得组件中的引用。

// html
<input ... #myComponent>

// ts

export class MyComponent {
  @ViewChild('myComponent') myComponent;
  myComponentFocus$: Observable;

  constructor () {}
}

然后你可以在ngOnInit函数中创建输入焦点事件的observable,

ngOnInit(): void {
  this.myComponentFocus = Observable.fromEvent(this.myComponent.nativeElement, 'focus')
  ...
}

现在你的参考书目会像:

this.bibliographies = Observable.combineLatest(this.searchTerms, this.myComponentFocus)
    .debounceTime(300)
    .switchMap(([focusEvt, term]) => term
        ? this.bibliographieSearchService.search(term)
        : this.bibliographieSearchService.search(null, "1", "20"))
    .catch(error => {
        console.log(error);
        return Observable.of<Bibliographie[]>([]);
    });

但是,此解决方案仍然存在问题。 combineLatest 不会发出,直到所有Observable组合都发出任何值,因此我们的this.bibliographies仍然需要在发出任何值之前发出值更改。

要解决这个问题,我们可以创建一个Observable of null ,然后将this.searchTerms连接到此null Observable。这意味着新的Observable将从头开始具有值(null),因此当发出focus事件时,this.bibliographies Observable也会发出。

ngOnInit(): void {
      this.myComponentFocus = Observable.fromEvent(this.myComponent.nativeElement, 'focus')

      this.searchTermsWithNull = Observable.of(null).concat(this.searchTerms)

      this.bibliographies =     Observable.combineLatest(this.searchTermsWithNull, this.myComponentFocus)
        .debounceTime(300)
        .switchMap(([focusEvt, term]) => term
            ? this.bibliographieSearchService.search(term)
            : this.bibliographieSearchService.search(null, "1", "20"))
        .catch(error => {
            console.log(error);
            return Observable.of<Bibliographie[]>([]);
        });
      }

例如,如果您只想使用事件的第一个焦点而不是所有其他焦点,则可以向焦点事件observable添加take(1)

答案 1 :(得分:0)

上面答案的最后一个元素没有焦点部分(我认为因为没有子元素)!这是我的新ngOnInit函数:

...
private searchTermsWithNull;
...

ngOnInit(): void {

        this.searchTermsWithNull = Observable.of(null).concat(this.searchTerms);

        this.bibliographies = this.searchTermsWithNull
            .debounceTime(300)
            .switchMap(term => term
                ? this.bibliographieSearchService.search(term)
                : this.bibliographieSearchService.search(null, "1", "20"))
            .catch(error => {
                console.log(error);
                return Observable.of<Bibliographie[]>([]);
            });
    }