angular - 收听来自组件的 ngrx 操作
问题描述
我正在使用 ngrx/angular8 构建一个应用程序,在一种情况下,我想响应来自不同组件的操作。传统的方法是向 store 添加另一个属性并为其创建 reducer/selector。问题是我希望其他组件响应事件,即使它具有相同的值。例如让我们分解:
- 我单击了其中一个组件中的按钮,它调度了动作
this.store.dispatch( LayoutActions.scrollToSession({id: session_id})
- 我希望 3 个不同的组件以某种方式响应此操作,即使它们
session_id
是相同的。因此,如果我在减速器中创建了一个属性,则选择器将仅在第一次获得更新,并且不会为后续操作触发。
我的解决方案是简单地调度动作,并在组件中监听动作:
this.actions$.pipe(
ofType(LayoutActions.scrollToSession),
takeUntil(this.unsubscribe)
).subscribe(id => {
if ((!id.id) || (!this.messages_list)) return;
this.messages_list.scrollToElement(`#session-${id.id}`, {left: null, top: null});
});
我的问题是,这是正确的方法吗?直接监听组件中的操作,如果有的话,我有什么选择?我虽然在调度操作时添加了一个随机前缀,以更改存储状态并稍后在选择器中将其删除,但这感觉不对。
解决方案
更新
正确的方法是始终依赖存储状态,而不是其操作。
可能的解决方案
store.ts
import {Injectable} from '@angular/core';
import {Actions, createEffect, ofType} from '@ngrx/effects';
import {Action, createAction, createFeatureSelector, createReducer, createSelector, on, props} from '@ngrx/store';
import {delay, map} from 'rxjs/operators';
// actions
export const setScroll = createAction('scroll', props<{id?: string, shaker?: number}>());
export const causeTask = createAction('task', props<{scrollId: string}>());
// reducer
export interface State {
scroll?: {
id: string,
shaker: number,
};
}
const reducer = createReducer(
{},
on(setScroll, (state, {id, shaker}) => ({
...state,
scroll: id ? {id, shaker} : undefined,
})),
);
export function coreReducer(state: State, action: Action): State {
return reducer(state, action);
}
export const selectState = createFeatureSelector<State>('core');
export const selectFlag = createSelector(
selectState,
state => state.scroll,
);
// effects
@Injectable()
export class Effects {
public readonly effect$ = createEffect(() => this.actions$.pipe(
ofType(causeTask),
delay(5000),
map(({scrollId}) => setScroll({id: scrollId, shaker: Math.random()})),
));
constructor(protected readonly actions$: Actions) {}
}
app.component.ts
import {ChangeDetectionStrategy, Component, OnInit} from '@angular/core';
import {Store} from '@ngrx/store';
import {filter, map} from 'rxjs/operators';
import {causeTask, selectFlag, setScroll} from 'src/app/store';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AppComponent implements OnInit {
constructor(protected store: Store) {
}
public ngOnInit(): void {
// reset of the scrolling state
this.store.dispatch(setScroll({}));
this.store.select(selectFlag).pipe(
filter(f => !!f),
map(f => f.id),
).subscribe(value => {
this.store.dispatch(setScroll({})); // reset
alert(value); // <- here you should use the scrolling.
});
// some long task which result should cause scrolling to id.id.
this.store.dispatch(causeTask({scrollId: 'value of id.id'}));
this.store.dispatch(causeTask({scrollId: 'value of id.id'}));
}
}
app.module.ts
import {NgModule} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {EffectsModule} from '@ngrx/effects';
import {StoreModule} from '@ngrx/store';
import {coreReducer, Effects} from 'src/app/store';
import {AppComponent} from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
StoreModule.forRoot({
core: coreReducer,
}),
EffectsModule.forRoot([
Effects,
]),
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
原来的
如果您需要这些操作,您可以使用他们的流。
import {StoreActions, StoreState} from '@core/store';
...
constructor(
protected readonly storeActions: StoreActions,
) {}
...
...
// listening on success action of company change request.
this.storeActions
.ofType(CompanyProfileActions.UpdateBaseSuccess)
.pipe(takeUntil(this.destroy$))
.subscribe();
...
推荐阅读
- python-3.x - 如何按时间顺序单击页面上的多个“+”按钮并使用python从中提取数据?
- flutter - 如何使用 Flutter navigator 为两个屏幕进行滑动过渡?
- mysql - SequelizeEagerLoadingError:角色未与用户关联
- html - 如何更改选择多个元素中活动项目的字体颜色?
- xml - 如何有条件地实例化 xml 配置中的 bean?
- javascript - PayPal JavaScript SDK 客户端不发布到服务器
- azure-active-directory - AcquireTokenByAuthorizationCodeAsync 后如何处理 MsalUiRequiredException
- python - 如何使用scrapy解析xml
- json - Jackson registerSubtypes 在 Kotlin 中不起作用
- python - 在本地网络上运行 Bokeh Server