@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());
}
}
然后,我们将有我们的 SearchActions
。 Load
在我们的组件初始化时触发,从 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
参数中包含我们的搜索字符串的那些。我们在 contents
和 searchedContents
中都保存了第一个获取的内容。然后,在搜索时,我们将更新 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
将自动更新我们组件中的内容。
我正在开发一个新的 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());
}
}
然后,我们将有我们的 SearchActions
。 Load
在我们的组件初始化时触发,从 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
参数中包含我们的搜索字符串的那些。我们在 contents
和 searchedContents
中都保存了第一个获取的内容。然后,在搜索时,我们将更新 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
将自动更新我们组件中的内容。