Redux cho Angular

Trong bài biết trước mình đã đề cập đến việc cần thiết của quản lí trạng thái (state management) trong Angular. Để tiếp tục, trong bài biết này, mình sẽ triển khai cách cài đặt Redux cho ứng dụng Angular.

Mình sẽ làm một ví dụ cực kì đơn giản đó là ứng dụng tăng giảm biến đếm. ?

Không áp dụng Redux:

Đầu tiên, nếu không áp dụng Redux thì ví dụ trên là cực kì đơn giản, chỉ vài dòng code là làm được ngay.

Đầu tiên ta tạo tập tin counter.component.html như sau:

<div class="container">
   <button (click)="increment()">Increment</button>
    <button (click)="decrement()">Decrement</button>
    <div class="counter">{{counter}}</div>
</div>

Tiếp đến, là tạo tập tin counter.component.ts. Tâp tin này đơn giản là khai báo một biến counter và hai hàm để tăng giảm:

import { Component } from '@angular/core';
@Component({
  selector: 'my-counter',
  templateUrl: './counter.component.html'
})
export class CounterComponent {

  counter: number = 0;

    increment(){
    this.counter++;
    }
    
    decrement(){
    this.counter--;
    }
}

Toàn bộ source code của project có thể xem tại đây.

Áp dụng Redux:

Trên đây, ta thấy không áp dụng Redux thì project cực kì đơn giản. Để “phức tạp hóa” ta sẽ áp dụng Redux vào. Hít thật sâu để xem tiếp phần dưới nào.

Nếu bạn nào chưa biết Redux là gì thì xem lại này viết trước nhé.

Thự viện áp dụng Redux cho Angular thì có 2 repository được đánh giá cao là ngrx/store và @angular-redux/store. Trong bài này, mình sẽ áp dụng @angular-redux/store.

Bước 1: Cài đặt thư viện

Ta cài đặt hai thư viện là redux và @angular-redux/store bằng command sau:

npm install --save redux ng2-redux

Sau đó, tất nhiên là phải import vào app.module.ts rồi:

//... another code here


@NgModule({
  imports:  [ BrowserModule, FormsModule, 
   NgReduxModule
  ],
  declarations: [ AppComponent, CounterComponent],
  bootstrap:    [ AppComponent ]
})

// another code here

Bước 2: Triển khai Redux

Sau đây là những bước cực kì phức tạp để triển khai Redux. Nếu khó theo phần code bên dưới quá thì các bạn co thể xem source code hoàn chỉnh tại đây.

Tạo một file có tên là action.ts để chứa tên các hành động (hằng số):

export const INCREMENT = 'INCREMENT';
export const DECREMENT = 'DECREMENT';

Tạo tập tin store.ts trong thư mục app như sau:

import {INCREMENT , DECREMENT} from './actions';

export interface IAppState{
  counter: number;
}

export const INITIAL_STATE : IAppState = { 
  counter : 0
}

export function rootReducer(state : IAppState, action): IAppState{
  switch(action.type){
    case  INCREMENT : return {counter: state.counter + 1};
    case  DECREMENT : return {counter: state.counter - 1};
  }


  return state;
}

Như đã đề cập thì store trong Redux có thể được xem là database chứa trạng thái của toàn bộ ứng dụng.

Ta cần tạo một interface có tên là IAppState chứa biến counter.

Ta khai báo thêm biến INITIAL_STATE để khởi tạo giá trị ban đầu của các biến.

Đồng thời, ta thiết kế reducer đề xử lí trạng thái. Do component chỉ có hai hàm là tăng và giảm, ta chỉ cần thiết kế hai trường hợp để xử lí. Ta thấy rằng, reducer sẽ giống như một cái công tắc với hàm điều kiện là switch.

Bước 3: Tạo component counter

Để cho code rõ ràng, ta tạo 1 folder có tên là counter chứa 2 file là counter.component.tscounter.component.html.

Nội dung của counter.component.ts như sau:

import { Component } from '@angular/core';
import {NgRedux} from 'ng2-redux';
import {IAppState} from '../store';
import {INCREMENT , DECREMENT} from '../actions';
import { select } from 'ng2-redux';
import { Observable } from 'rxjs/Observable';

@Component({
  selector: 'my-counter',
  templateUrl: './counter.component.html',
  styleUrls: ['./counter.component.css']
})
export class CounterComponent  {

   @select() counter: Observable<number>;

   constructor(private ngRedux : NgRedux<IAppState>){
    
    }
     ngOnInit() {
     }
    
    increment(){
     this.ngRedux.dispatch({type: INCREMENT});
    }
    
    decrement(){
     this.ngRedux.dispatch({type: DECREMENT});
    }
}

Thay vì khởi tạo biến counter thông thường, ta phải thêm nào @select. Ngoài ra, thay vì khai báo các kiểu dữ liệu như string hay number, ta lại định nghĩa là Observable.

Ta còn cần phải tiêm thành phần liên quan (dependency injection) là ngRedux vào constructor:

constructor(private ngRedux : NgRedux<IAppState>){}

Ta khai báo hai hàm là increment và decrement để xử lí. Điểm khác lạ ở đây là thay vì tăng giảm giá trị của biến counter, ta lại hàm dispatch:

increment(){ 
  this.ngRedux.dispatch({type: INCREMENT}); 
}

Vậy phần xử lí ở đâu rồi? Phần xử lí nằm ở store.ts đã đề cập ở phía trên chứ đâu. ?

Tiếp theo, ta tạo lớp view bằng các tạo tập tin counter.component.html như sau:

<div class="container">
   <button (click)="increment()">Increment</button>
    <button (click)="decrement()">Decrement</button>
    <div class="counter">{{counter | async}}</div>
</div>

Điểm đặc biệt ở đâu là là ta thêm vào directive là async phía sau biến counter.

Ok, sau khi tạo xong counter component thì đừng quên import nó nào app.module.ts nhé:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
import { HelloComponent } from './hello.component';
import{NgReduxModule, NgRedux} from 'ng2-redux';
import {IAppState , rootReducer, INITIAL_STATE} from './store';
import {CounterComponent} from './counter/counter.component';

@NgModule({
  imports:  [ BrowserModule, FormsModule, 
   NgReduxModule
  ],
  declarations: [ AppComponent, HelloComponent , CounterComponent],
  bootstrap:    [ AppComponent ]
})

export class AppModule { 
constructor(ngRedux: NgRedux<IAppState>) {
    ngRedux.configureStore(rootReducer, INITIAL_STATE );
  }

}

Mọi thủ tục xong xuôi rồi, giờ chỉ cần gọi counter component ra thôi. Trong tập tin app.component.html:

<my-counter></my-counter>

Kết luận:

Chỉ một ví dụ đơn giản thôi mà áp dụng Redux vào cực thân quá. Nhưng về lâu dài, việc áp dụng quản lí trạng thái sẽ tạo nên khả năng dễ dàng mở rộng sau này cho ứng dụng.

Ngoài ra, với Redux ta có thể tạo nên các ứng dụng offline: Các thao tác của người sử dụng được lưu vào một cây trạng thái và khi có kết nối Internet, ta có thể tiến hàng được đồng bộ lên server bởi một loạt các sự kiện. Chắc phải viết thêm một bài hướng dẫn triển khai tính năng này mới được. ?


Nhận thấy các bài viết tiếng Việt chuyên về lập trình blockchain còn ít nên tôi quyết định chuyển hướng sang chuyên viết về chủ đề blockchain dành riêng cho lập trình viên. Hi vọng những bài viết này sẽ giúp ích cho các bạn đang muốn theo đuổi lĩnh vực còn khá mới này.

Nếu bạn thấy bài viết hữu ích, bạn có thể ủng hộ tôi vài tách cà phê thông qua MoMo tại đây

Bạn cũng có thể nhờ tôi tư vấn về giải pháp công nghệ thông tin nói chung và blockchain nói riêng (có tính phí) thông qua đây