angular使用service+rxjs做状态管理实例

对于一般的中小angular项目,使用类似ngrx或者mobx等第三方库作为状态管理反而会更麻烦,那么我们就可以使用框架自带的rxjs+service来做一个简单的状态管理。下面讲解一下利用rxjs+service作状态管理实例,基本能够解决大部分问题,如果应用相对复杂,状态和数据量大,还是建议使用ngrx。

首先初始化建立一个项目,建立一个module: state-manage来演示,引入antd库(网上很多教程,此处略过啦哈~)

建立一个service,将其注入到root

// src/app/state-manage/state.service.ts

import { Injectable, EventEmitter} from '@angular/core';

export declare interface HeroDefind {
  id: string;
  name: string;
  age: number;
  power: string;
}

@Injectable({
  providedIn: 'root'
})
export class StateService {
  heroList: HeroDefind[] = [
    {
      id: '1',
      name: 'Iron Man',
      age: 32,
      power: 'so much money'
    },
    {
      id: '2',
      name: 'Spider Man',
      age: 18,
      power: 'With great power comes great responsibility.'
    },
    {
      id: '3',
      name: 'Captain America',
      age: 32,
      power: 'strong'
    },
    {
      id: '4',
      name: 'Black Widow',
      age: 32,
      power: 'beautify'
    }
  ];

  notifyChange$ = new EventEmitter<any>();
  constructor() { }

}

conpoment中注入该服务

// src/app/state-manage/index/index.component.ts

import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import {StateService, HeroDefind} from './../state.service';


@Component({
  selector: 'app-state-index',
  templateUrl: './index.component.html',
  styleUrls: ['./index.component.less']
})
export class IndexComponent implements OnInit {
  heros: HeroDefind[] = [];

  constructor(
    private stateService: StateService,
    private route: Router
  ) {
    this.heros = this.stateService.heroList;
  }

  ngOnInit() {

  }

  goEditor(hero: HeroDefind) {
    this.route.navigateByUrl(`/s/edit?id=${hero.id}`);
  }

  godetail(hero: HeroDefind) {
    this.route.navigateByUrl(`/s/detail?id=${hero.id}`);
  }
}

模板渲染

// src/app/state-manage/index/index.component.html

<h3 [ngStyle]="{ margin: '16px 0' }">英雄列表</h3>
<nz-table #basicTable [nzData]="heros">
    <thead>
      <tr>
        <th>Name</th>
        <th>Age</th>
        <th>Power</th>
        <th>Action</th>
      </tr>
    </thead>
    <tbody>
        <tr *ngFor="let data of basicTable.data">
            <td>{{ data.name }}</td>
            <td>{{ data.age }}</td>
            <td>{{ data.power }}</td>
            <td>
                <a (click)="goEditor(data)">编辑</a>
                <nz-divider nzType="vertical"></nz-divider>
                <a (click)="godetail(data)">详情</a>
            </td>
        </tr>
    </tbody>
</nz-table>

结果图如下:
在这里插入图片描述

好了,下面我们尝试跳转页面,修改英雄数据

// src/app/state-manage/edit/edit.component.ts

import { Component, OnInit } from '@angular/core';

import { FormBuilder, FormGroup } from '@angular/forms';
import { ActivatedRoute } from '@angular/router';
import { StateService, HeroDefind } from '../state.service';

@Component({
  selector: 'app-state-edit',
  templateUrl: './edit.component.html',
  styleUrls: ['./edit.component.less']
})
export class EditComponent implements OnInit {
  validateForm: FormGroup;

  constructor(
    private fb: FormBuilder,
    private stateService: StateService,
    private route: ActivatedRoute,
    ) { }

  ngOnInit(): void {
    const heroId = this.route.snapshot.queryParams.id;
    const currentHero = this.stateService.heroList.find((hero: HeroDefind) => hero.id === heroId);

    this.validateForm = this.fb.group({
      id: [currentHero.id],
      name: [currentHero.name],
      age: [currentHero.age],
      power: [currentHero.power],
    });
  }

  submitForm(value: { anem: string; age: string; power: string, id: string}): void {
    // tslint:disable-next-line:forin
    for (const key in this.validateForm.controls) {
      this.validateForm.controls[key].markAsDirty();
      this.validateForm.controls[key].updateValueAndValidity();
    }
  }
}

 // src/app/state-manage/edit/edit.component.html
 
 <form nz-form [formGroup]="validateForm" (ngSubmit)="submitForm(validateForm.value)">
    <nz-form-item>
      <nz-form-label [nzSpan]="7" nzRequired>Name</nz-form-label>
      <nz-form-control [nzSpan]="12" nzHasFeedback nzValidatingTip="Validating...">
        <input nz-input formControlName="name"/>
      </nz-form-control>
    </nz-form-item>
    <nz-form-item>
        <nz-form-label [nzSpan]="7" nzRequired>Age</nz-form-label>
        <nz-form-control [nzSpan]="12" nzHasFeedback nzValidatingTip="Validating...">
          <input nz-input formControlName="age"/>
        </nz-form-control>
    </nz-form-item>
    <nz-form-item>
      <nz-form-label [nzSpan]="7" nzRequired>Power</nz-form-label>
      <nz-form-control [nzSpan]="12" nzErrorTip="Please write something here!">
        <input nz-input formControlName="power"/>
      </nz-form-control>
    </nz-form-item>
    <nz-form-item>
      <nz-form-control [nzOffset]="7" [nzSpan]="12">
        <button nz-button nzType="primary" [disabled]="!validateForm.valid">Submit</button>
      </nz-form-control>
    </nz-form-item>
</form>

在这里插入图片描述
现在我们希望在submit的时候,通知列表页更新修改后的数据,如何做到呢?我们只是单纯修改service里的数据,是无法更新列表页的,此时需要rxjs出场了。
回到我们的列表,修改index.component.ts

// src/app/state-manage/index/index.component.ts
  ngOnInit() {
    this.heros = this.stateService.heroList;
+    this.stateService.notifyChange$.subscribe((hero: HeroDefind) => {
+     this.heros = this.stateService.heroList;
+    });
  }

修改edit.conpoment.ts

submitForm(value: { name: string; age: number; power: string, id: string}): void {
    // tslint:disable-next-line:forin
    for (const key in this.validateForm.controls) {
      this.validateForm.controls[key].markAsDirty();
      this.validateForm.controls[key].updateValueAndValidity();
    }
+    const currentHeroIndex = this.stateService.heroList.findIndex((hero: HeroDefind) => hero.id === value.id);
+    this.stateService.heroList[currentHeroIndex] = value;
+    this.stateService.notifyChange$.emit(value);
+    history.back();
}

现在,我们修改英雄数据
在这里插入图片描述
返回列表页,发现数据已经做了修改
在这里插入图片描述
即使跳到详情页,也是修改后的数据
在这里插入图片描述
这说明,列表页,编辑页,详情页,都是使用同一个service实例,实现数据的共享,我们要做的,就是在数据更新之后,能够通知其他组件更新视图。

但是还有一个问题,就是我们的英雄数据是在服务器拉取的,存在异步问题,那么列表页如何异步从service读取数据呢?其实不用过多修改,在上面的代码之上修改service

import { Injectable, EventEmitter} from '@angular/core';

export declare interface HeroDefind {
  id: string;
  name: string;
  age: number;
  power: string;
}

@Injectable({
  providedIn: 'root'
})
export class StateService {
  heroList: HeroDefind[] = [];

  notifyChange$ = new EventEmitter<any>();
  constructor() {
    setTimeout(() => {
      this.heroList = [
        {
          id: '1',
          name: 'Iron Man',
          age: 32,
          power: 'so much money'
        },
        {
          id: '2',
          name: 'Spider Man',
          age: 18,
          power: 'With great power comes great responsibility.'
        },
        {
          id: '3',
          name: 'Captain America',
          age: 32,
          power: 'strong'
        },
        {
          id: '4',
          name: 'Black Widow',
          age: 32,
          power: 'beautify'
        }
      ];
      // 因为列表页index.component.ts已经订阅接受英雄数据的更新发布,
      // 此处直接发布订阅就可以更新列表数据啦
+      this.notifyChange$.emit();
    }, 3000);
  }

}

当然,你可以不做页面缓存,在返回列表页的时候重新拉取服务器数据。
至于rxjs的用法和原理,网上很多教程,也更详细,不做累赘啦哈~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值