2017-01-31 33 views
0

我正在使用Angular 2进行术语搜索。它与Heroes教程中使用的代码大致相同。Angular2 - 使用Switchmap从init服务获取数据

我的服务:

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上加载数据。但是,搜索不再有效。

有什么想法?

+0

为此,您需要更改查询。 –

+0

问题是:在初始化时,没有数据通过我的代码从服务中加载。我试图用另一个函数直接在ngOnInit()中加载数据,但术语搜索不再有效... – Fabien

回答

1

您的解决方案的问题是,this.searchTerms可能是valueChanges的可观察值,所以直到输入内的值没有改变,任何事件正在发射。

假设您想在每次输入被聚焦时触发搜索,并且每次该值都在变化。

因此,在这种情况下,您还需要侦听焦点事件,以便在焦点事件由输入触发时能够发出值。但你总是需要拿出valueChanged发出的最后一个值。在这种情况下,combineLatest操作符会有意义,因此您可以同时听取焦点和值的更改。 combineLatest运算符会发出每个源Observable的最新值,因此即使在focus事件发生时,您也会收到this.searchTerms中的最后一个值。

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

// html 
<input ... #myComponent> 

// ts 

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

    constructor() {} 
} 

然后您可以创建一个可观察的ngOnInit功能时,您输入的焦点事件,

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不会发射,直到所有Observables合并发射任何价值,所以我们this.bibliographies仍然需要在发射任何东西之前发生的价值变化。为了解决这个问题,我们可以创建一个可观察的null,然后concat this.searchTerms到这个null Observable。这意味着新的Observable从头开始会有一个值(null),所以当发出focus事件时,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[]>([]); 
     }); 
     } 

如果,例如,你只需要使用该事件的第一焦点,而不是所有的人,你可以一个take(1)添加到焦点事件观察到。

+0

你是一个天才。 searchTermsWithNull独自工作! – Fabien

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[]>([]); 
      }); 
    }