iT邦幫忙

2023 iThome 鐵人賽

DAY 15
0
Modern Web

從0開始的的Angular站台架設-Stnadalone 系列 第 15

D14 系統層級的Angular Standalon資料管理,RxJs、Store、Action、Effect、Reducer(1)

  • 分享至 

  • xImage
  •  

之前我們都是先講原理再來呈現實作

新的週期我們顛倒過來,將會優先進行程式開發操作的手順在進行原理的心得分享


首先我們先在core中建立一個資料夾store,並且建立三個資料夾actioneffectreducer

core/
|    |-store
|    |    |-action
|    |    |-effect
|    |    |-reducer

然後在action中建立兩個檔案index.tslayout.action.ts

core/
|    |-store
|    |    |-action
|    |    |    |-index.ts
|    |    |    |-layout.action.ts

然後我們在layout.action.ts中建立兩個Action,分別用在Loading與我們的伸縮側邊表單Menu

layout.action.ts

import { createAction, props } from '@ngrx/store';
import { SlideMenuStatus } from 'src/app/shared/types/layout-setting/layout';

/**
 * 更新系統遮罩狀態
 */
export const UPDATE_LOADING_STATUS = createAction(
  '[LAYOUT] UPDATE_LOADING_STATUS',
  props<{ Status: boolean }>()
);
/**
 * 更新系統選單狀態狀態
 */
export const UPDATE_MENU_STATUS = createAction(
  '[LAYOUT] UPDATE_MENU_STATUS',
  props<{ Status: SlideMenuStatus }>()
);

假如是透過copy+paste貼在自己的檔案中的朋友,這時候@ngrx/store應該出現讀不到的狀況,這是正常的,我們現在需要自己安裝

npm i '@ngrx/store

安裝完之後就可以正常載入了

建立完之後我們在reducer中新增一個檔案叫做layout.reducers.ts並在裡面註冊兩個reducer監聽事件

core/
|    |-store
|    |    |-reducers
|    |    |    |-index.ts
|    |    |    |-layout.reducers.ts

layout.reducers.ts

import { createReducer, on } from '@ngrx/store';
import { UPDATE_LOADING_STATUS, UPDATE_MENU_STATUS } from '../actions/layout.action';
import { SlideMenuStatus } from 'src/app/shared/types/layout-setting/layout';
/**
*layout State初始化狀態
*/
export const initialState: State = {
  Loading: false,
  SlideMenuStatus: {
    isShow: true,
    isExtant: true,
    isMini: false,
  },
};
/**
*layout State Type
*/
export interface State {
  Loading: boolean;
  SlideMenuStatus: SlideMenuStatus;
}
/**
*監聽Reducer
*/
export const reducer = createReducer(
  initialState,
  on(UPDATE_LOADING_STATUS, (state, action) => {
    return {
      ...state,
      Loading: action.Status,
    };
  }),
  on(UPDATE_MENU_STATUS, (state, action) => {
    return {
      ...state,
      SlideMenuStatus: action.Status,
    };
  })
);

接下來就是Angular Standalone作為核心的載入模式了

在之前我們都是使用app.module.ts進行引用的
Angular version<=16

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { StoreModule } from '@ngrx/store';
ort { reducer } from './core/store/reducers';
 
@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    StoreModule.forRoot({ layout: reducer.layout })
  ],
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {}

而在我們的Angular Standalone開發環境中,我們可以在app.config.ts中進行這部分參數的處理

事實上在以Standalone作為核心的系統開發流程中,原先大部分被app.module.ts進行引用與管理的絕大部分都被抽離至app.config.ts進行application層級的使用,而非在原先的NgModule進行管理

Platform
Application
NgModule
Directive / Component

這其實就是我能夠局部還原環境的原因所在,當我原先依賴於root注入的ngModule被抽離至上一層的狀況,我就能更好的將我的功能專注於當前Component與他所依賴的ngModule之中

具體app.config.ts的內容如下
app.config.ts

import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { routes } from './core/routes/app.routes';
import { provideStore } from '@ngrx/store';
import { reducer } from './core/store/reducers';

export const appConfig: ApplicationConfig = {
  providers: [
    provideRouter(routes),
    provideStore({
      layout: reducer.layout,
    }),
  ],
};

然後他會在main.ts進行注入至application層級的動作

import { bootstrapApplication } from '@angular/platform-browser';
import { appConfig } from './app/app.config';
import { AppComponent } from './app/app.component';

bootstrapApplication(AppComponent, appConfig)
  .catch((err) => console.error(err));

這時候在回到我們的layoutComponent中,將Store進行注入,好取得被放入在系統中的資料
layout.component.ts

import { Component, inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { Observable, Subject, takeUntil } from 'rxjs';
import { Store } from '@ngrx/store';
import * as fromPagesReducers from '../../../core/store/reducers';
@Component({
  standalone: true,
  imports: [CommonModule],
  templateUrl: './shopping-home.component.html',
  styleUrls: ['./shopping-home.component.scss'],
})
export class layoutComponent {
  private store = inject(Store<fromPagesReducers.State>);
  private destroy$ = new Subject<void>();
  loading$: Observable<boolean> = this.store
    .select((x) => x.layout.Loading)
    .pipe(takeUntil(this.destroy$));
}

假如有開發或是看過先前版本Angular的朋友有沒有覺得現在的開發多麼簡潔明瞭XD

我不用透過ngModule特別去管理Module,也不用特別透過constructor去做Service的宣告(甚至還有官方建議上限

然後我們在樣板上進行宣告,由於他是一個Observable所以我們需要用async的pipe進行訂閱管理
layout.component.html

<app-header></app-header>
{{ loading$ | async | json }}

這時候在看我們的頁面就會看到在layout.reducers.ts中進行初始化的State中所帶的值,

可以在這裡停一下,想想看究竟是什麼值

答案如下

export const initialState: State = {
  Loading: false, //<== 這個
  SlideMenuStatus: {
    isShow: true,
    isExtant: true,
    isMini: false,
  },
};

很好 我們已經會了怎麼建立起一個系統層級的資料管理脈絡,接下來我將會在後續的內容中講解Observable、Observe、Subject的訂閱狀態,以及在訂閱資料中好用的rxjs小工具與技巧

當然還有Action、Effect、Reducer三大狀態管理管道


上一篇
D13 Angular前端開發基礎專案彙整
下一篇
D15 系統層級的Angular Standalon資料管理,RxJs、Store、Action、Effect、Reducer(2)
系列文
從0開始的的Angular站台架設-Stnadalone 30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言