@ngrx 4 如何过滤当前加载的数据

@ngrx 4 how to filter current loaded data

我正在开发一个新的 angular 4 plus @ngrx 4 项目。

我希望对加载的数据有搜索功能。

例如组件中已经加载了所有的联系人信息。 联系人列表将过滤出与搜索文本匹配的联系人姓名。

Please see screenshot

由于存储中存在数据,我不想再次调用 Web api 服务。

任何想法或演示代码将不胜感激。

ngrx 存储是您存储数据的方式的一部分。 ngrx 商店是可观察的,因此您的应用程序流程是

容器 -> 组件

Container - 将从存储中 select 数据的包装器组件。 示例:

const contacts$: Observable<contact> = this.store.pluck('contacts');

//*contacts$ - 因为美元是 Observable 的惯例 *//

组件 - 数据可视化组件,数据将作为Input()。示例:

Input() contacts: Array<contact>;

this convention is called sometime SmartComponent(Container) and DumbComponent(component)

现在对于数据 transform/mapping 您可以使用反应式方法 (Rxjs) 或函数式编程或任何您想要的但它与 ngrx 无关,因为在您的联系人组件中存在数据。

您的场景演示:

contacts.container.ts

@Component({
    selector: 'contacts-container',
    template: `
    <contacts-list [contacts]="contacts$ | async"></contacts-list>
    `
})

export class ContactsContainer {
    contacts$: Observable<[]contact> = this.store.pluck('contacts');
    constructor(
        private store: Store<applicationState>
    ) { }
}

接触-list.component.ts

@Component({
    selector: 'contacts-list',
    template: `
        <input type="text" placeholder="write query" #query>
        <ul>
            <li *ngFor="contact of contacts | searchPipe: query.target.value">
            </li>
        </ul
    `
})

export class ContactsListComponent {
    contcats: Array<contact> = [];
    constructor() { }

}

我使用 searchPipe 进行数据转换(自定义管道),但这只是数据转换的示例,您可以使用其他方法。

祝你好运!

您可以按照此流程在已获取的内容上搜索您需要的内容:

在您的输入中使用类似 '(input)'='searchInputChange$.next(search)' 的内容。所以,每次用户改变输入,都会触发我们的研究。

然后,在您的组件上,在构造函数上,每次 searchInputChange$ 更改时,我们都会触发一个新的 SearchAction。然后,我们将更改 reducer 上的过滤内容,结果将插入 contents$。在 ngOnInit 上,我们只是第一次从 api 加载数据。

我正在使用一个名为 Content 的模型,这只是一个示例,它有一个字符串参数 title。我们将使用此字段根据搜索输入过滤我们的内容。

import { Component, OnInit } from '@angular/core';
import { Store } from '@ngrx/store';
import { Subject } from 'rxjs/Subject';
import {of} from 'rxjs/observable/of';

/** ngrx **/
import {AppState} from '../../app-state.interface';
import * as searchActions from './actions/search.actions';

/** App Models **/
import { Content } from './models/content.model';

export class SearchComponent implements OnInit {

    searchInputChange$ = new Subject<string>();
    contents$: Observable<Array<Content>>;

    constructor(private _store: Store<AppState>) {
      this.searchInputChange$
        .switchMap((text: string) => of(text))
        .subscribe((text: string) => this._store.dispatch(new searchActions.SearchAction(text)));
      this.contents$ = this._store.select(getSearchedContents);
    }

    ngOnInit() {
        this._store.dispatch(new searchActions.LoadAction());
    }

}

然后,我们将有我们的 SearchActionsLoad 在我们的组件初始化时触发,从 api 获取一些内容。 LoadSuccess 是根据加载操作的效果发出的,以便用获取的数据填充我们的 reducer 并将其显示在我们的第一个组件中,它有一个内容数组的有效负载。 Search 将在我们的输入字段更改时触发,这将有一个包含搜索字符串的字符串有效负载。

import { Action } from '@ngrx/store';

/** App Models **/
import { Content } from '../models/content.model';

export const LOAD = '[Search] Load';
export const LOAD_SUCCESS = '[Search] Load Success';
export const SEARCH = '[Search] Search';

export class LoadAction implements Action {
  readonly type = LOAD;
  constructor() { }
}

export class LoadActionSuccess implements Action {
  readonly type = LOAD_SUCCESS;
  constructor(public payload: Content[]) { }
}

export class SearchAction implements Action {
  readonly type =  SEARCH;
  constructor(public payload: string) {}
}

export type All
  = LoadAction
  | LoadActionSuccess
  | SearchAction;

SearchEffect 只会从 api:

获取内容
import { Injectable } from '@angular/core';
import { Actions, Effect } from '@ngrx/effects';

/** rxjs **/
import {of} from 'rxjs/observable/of';
import {map} from 'rxjs/operators/map';
import {mergeMap} from 'rxjs/operators/mergeMap';
import {catchError} from 'rxjs/operators/catchError';

/** ngrx **/
import * as searchActions from '../actions/search.actions';

/** App Services **/
import { SomeService } from '../services/some.service';

/** App Model **/
import {Content} from '../models/content.model';

@Injectable()
export class SearchEffects {

  @Effect() load$ = this.actions$
    .ofType(searchActions.LOAD)
      .pipe(
        mergeMap(() => {
          return this.someService.getContentsFromApi()
            .pipe(
              map((contents: Content[]) => {
                return new searchActions.LoadActionSuccess(contents);
              }),
              catchError(() => {
                // do something
              })
            );
        })
    )
  ;

  constructor(private someService: SomeService, private actions$: Actions) { }
}

SearchReducer 将在我们从 api 成功获取内容时处理 LoadSuccess 并且 Search 操作会将我们获取的内容过滤为 return 只有在内容的 title 参数中包含我们的搜索字符串的那些。我们在 contentssearchedContents 中都保存了第一个获取的内容。然后,在搜索时,我们将更新 searchedContents 以仅包含 contents,其中 content.title 包含搜索到的字符串。

import { isEmpty } from 'lodash';

/** ngrx **/
import {createFeatureSelector} from '@ngrx/store';
import {createSelector} from '@ngrx/store';

/** App Models **/
import { Content } from '../models/content.model';

/** ngrx **/
import * as searchActions from '../actions/search.actions';

export type Action = searchActions.All;

export interface SearchsState {
  contents: Content[];
  searchedContents: Content[];
}

export const initialState: SearchsState = {
  contents: [],
  searchedContents: []
};

/ -------------------------------------------------------------------
// Selectors
// -------------------------------------------------------------------
export const selectContents      = createFeatureSelector<SearchsState>('search');
export const getSearchedContents = createSelector(selectContents, (state: searchedContents) => {
  return state.searchedContents;
});

export function contentsReducer(state: searchedContents = initialState, action: Action): searchedContents {
  switch (action.type) {
    case contentsActions.LOAD_SUCCESS:
      const loadContents = action.payload.map(content => new Content(content));
      return {
               contents: loadContents,
               searchedContents: loadContents
      };
    case contentsActions.SEARCH:
      const keywordContents = isEmpty(action.payload) ? state.contents :
          state.contents.filter(content => content.title.includes(action.payload));
      return {
               contents : state.contents,
               searchedContents : keywordContents
      };
    default: {
      return state;
    }
  }
}

因此,更新 searchedContents 将自动更新我们组件中的内容。