2017-01-31 77 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[]>([]); 
      }); 
    }