Angular+ngrx:如何在路由器 url 遍历中保持存储状态?

Posted

技术标签:

【中文标题】Angular+ngrx:如何在路由器 url 遍历中保持存储状态?【英文标题】:Angular+ngrx: how to keep store state on router url traversing? 【发布时间】:2018-07-13 19:58:58 【问题描述】:

我知道每次用户刷新页面时商店都会重置(所谓的“硬刷新”),但我希望当用户使用角度路由器遍历页面时它保持不变。但是,正如我所见,它无论如何都会重置。

我错了吗?有什么方法可以在不保存到 LocalStorage 或使用其他黑客的情况下保留存储空间?

app.module.ts

    import  NgModule  from '@angular/core';
    import  CommonModule  from '@angular/common';
    import  HttpClientModule  from '@angular/common/http';
    import  BrowserModule  from '@angular/platform-browser';
    import  BrowserAnimationsModule  from '@angular/platform-browser/animations';

    // Routing
    import  AppRoutingModule  from './app-routing.module';

    // Ngrx
    import  StoreModule  from '@ngrx/store';
    import 
      StoreRouterConnectingModule,
      RouterStateSerializer,
     from '@ngrx/router-store';
    import  StoreDevtoolsModule  from '@ngrx/store-devtools';
    import  EffectsModule  from '@ngrx/effects';

    // Other
    import  reducers, metaReducers  from './core/reducers';
    import  CustomRouterStateSerializer  from './core/utils/router.utils';
    import  MaterialModule  from './material/material.module';

    // Environment
    import  environment  from '../environments/environment';

    // Components
    import  AppComponent  from './app.component';
    import  NotFoundComponent  from './core/components/not-found.component';
    import  NotAuthorizedComponent  from './core/components/not-authorized.component';
    import  HomeComponent  from './core/components/home.component';
    import  UsersModule  from './users/users.module';
    import  EventsModule  from "./events/events.module";

    @NgModule(
      declarations: [
        AppComponent,
        NotFoundComponent,
        HomeComponent,
        NotAuthorizedComponent,
      ],
      imports: [
        CommonModule,
        HttpClientModule,
        BrowserModule,
        BrowserAnimationsModule,
        AppRoutingModule,
        MaterialModule,
        StoreModule.forRoot(reducers,  metaReducers ), 
        StoreRouterConnectingModule.forRoot(stateKey: 'router'),
        StoreDevtoolsModule.instrument(),
        EffectsModule.forRoot([]),
        UsersModule,
        EventsModule

  ],
  providers: [
     provide: RouterStateSerializer, useClass: CustomRouterStateSerializer ,
  ],
  bootstrap: [AppComponent]
)
export class AppModule  

./core/reducers/index.ts

import 
  ActionReducerMap,
  ActionReducer,
  MetaReducer, createFeatureSelector, createSelector,
 from '@ngrx/store';
import  environment  from '../../../environments/environment';
import  RouterStateUrl  from '../utils/router.utils';
import * as fromRouter from '@ngrx/router-store';
import * as fromAuth from '../../users/reducers/auth.reducer';

import  storeFreeze  from 'ngrx-store-freeze';

export interface State 
  router: fromRouter.RouterReducerState<RouterStateUrl>,
  auth: fromAuth.State;


export const reducers: ActionReducerMap<State> = 
  router: fromRouter.routerReducer,
  auth: fromAuth.reducer
;

export function logger(reducer: ActionReducer<State>): ActionReducer<State> 
  return function(state: State, action: any): State 
    console.log('state', state);
    console.log('action', action);

    return reducer(state, action);
  ;


export const metaReducers: MetaReducer<State>[] = !environment.production
  ? [logger, storeFreeze]
  : [];

// AUTH reducers
export const selectAuthState = createFeatureSelector<fromAuth.State>('auth');

export const getUser = createSelector(
  selectAuthState,
  fromAuth.getUser
);
export const getLoaded = createSelector(
  selectAuthState,
  fromAuth.getLoaded
);
export const getError = createSelector(
  selectAuthState,
  fromAuth.getError
);

这个想法是通过 HTTP GET 从服务器获取用户凭据,将它们放在存储中,然后从存储而不是服务器获取它们。所以我有一个守卫:

@Injectable()
export class AuthGuard implements CanActivate 

  constructor(private store: Store<State>, protected router: Router) 
  

  protected getFromStoreOrAPI(): Observable<any> 
    return this.store
      .select(getUser)
      .do(user => 
        if (!user) this.store.dispatch(new Signin());
      )
      .filter(user => user)
      .take(1);
  

  canActivate(): Observable<boolean> 
    return this.getFromStoreOrAPI()
      .switchMap(() => Observable.of(true))
      .catch(() =>  Observable.of(false));
  

this.store.dispatch(new Signin()); 上,它发出 HTTP 调用并将数据放入存储中。

问题是,每次用户进入新页面时它都会进行 HTTP 调用。

【问题讨论】:

如果您仅从路由器添加的组件或此类组件提供的服务中引用存储,则在您导航时将重新创建它。如果你在 AppModule 提供的服务中持有 store,它不会丢失它的状态。 如果我在不同的模块中使用商店并通过`StoreModule.forFeature('storeName', reducers)`组合它们? 为什么你认为这会有所不同?如果它不起作用,请将代码添加到您的问题中,以演示您尝试了什么以及什么不起作用。 更新了我的问题 导航到不同路线时会丢失什么状态? 【参考方案1】:

我太傻了!使用&lt;a href="/link"&gt;Link&lt;/a&gt; 而不是&lt;a routerLink="/link"&gt;Link&lt;/a&gt;

【讨论】:

【参考方案2】:

检查你的减速器,我确定你犯了忘记在你的 switch 语句中返回状态的错误:

switch(action.type)
       ...
        default:
            return state;

【讨论】:

不,返回状态

以上是关于Angular+ngrx:如何在路由器 url 遍历中保持存储状态?的主要内容,如果未能解决你的问题,请参考以下文章

NGRX - 如何在 Angular 防护中访问我的应用程序状态?

Angular 7、Ngrx、Rxjs 6 - 访问延迟加载模块之间的状态

在单元测试中使用参数模拟 ngrx 存储选择器(Angular)

在 Angular 中使用 ngrx 进行状态管理时,控制台记录状态数据

如何通过 Angular 中的 NGRX 减速器使用数组更新状态?

如何使用 ngrx-router-store 在 ngrx 效果中获取路由参数?