如何使用异步从 Store 访问和打印我的状态属性?

问题描述 投票:0回答:1

这是我的简单计数器的 Angular 代码,在 ngRx 上练习。

counter.reducer.ts:

import { createReducer, on } from '@ngrx/store';
import { plus, minus, reset } from './counter.actions';

export interface CounterState{
  counter: number;
};

export const initialState: CounterState = {
  counter: 0
};

export const CounterReducer = createReducer(
  initialState,
  on(plus, (state) => ( {...state, counter: state.counter + 1}) ),
  on(minus, (state) => ( {counter: state.counter - 1}) ),
  on(reset, (state) => ({counter: 0})),
);

counter.actions.ts:

import { createAction } from '@ngrx/store';

export const plus = createAction('counter +');
export const minus = createAction('counter -');
export const reset = createAction('counter Reset');

counter.component.ts:

import { Component } from '@angular/core';
import { Store, select } from '@ngrx/store';
import { CounterState } from '../counter.reducer';
import { minus, plus, reset } from '../counter.actions';
import { Observable, map } from 'rxjs';
import { AsyncPipe, CommonModule } from '@angular/common';


@Component({
  selector: 'counter',
  standalone: true,
  templateUrl: './counter.component.html',
  styleUrls: ['./counter.component.css'],
  imports: [CommonModule],
})
export class Counter {
  //c!:number;
  counterFinal$: Observable<number>; //= this.store.select("counter");
  
  constructor(private store: Store<CounterState>) {
    //this.store.select('counter').subscribe(data => this.c = data.counter);
    this.counterFinal$ = this.store.select("counter");
    
  }

  plus() {
    //this.count = this.count + 1;
    console.log('+');
    this.store.dispatch(plus());
  }

  minus() {
    //this.count = this.count - 1;
    console.log('-');
    this.store.dispatch(minus());
  }

  reset() {
    //this.count = 0;
    console.log('0');
    this.store.dispatch(reset());
  }
}

我在我的 main.ts 上提供商店,如下所示:

bootstrapApplication(App, {
  providers: [
    provideStore({ counter: CounterReducer }),
    provideStoreDevtools({
      maxAge: 25, // Retains last 25 states
      logOnly: !isDevMode(), // Restrict extension to log-only mode
      autoPause: true, // Pauses recording actions and state changes when the extension window is not open
      trace: false, //  If set to true, will include stack trace for every dispatched action, so you can see it in trace tab jumping directly to that part of code
      traceLimit: 75, // maximum stack trace frames to be stored (in case trace option was provided as true)
      connectInZone: true, // If set to true, the connection is established within the Angular zone
    }),
  ],
});

在 counter.component.html

<div>Counτer : {{ counterFinal$ | async }}</div>
<button id="plus" (click)="plus()">+</button>
<button id="minus" (click)="minus()">-</button>
<button id="reset" (click)="reset()">Reset</button>

结果我得到了这个:

results

在 ngRx 开发工具上,我的商店看起来运行良好。我只是不知道如何获取计数器属性并将其显示在 html 上(使用异步)

enter image description here

如果我将 counter.component.html 更改为:

<div>Counτer : {{ (counterFinal$ | async).counter }}</div>
<button id="plus" (click)="plus()">+</button>
<button id="minus" (click)="minus()">-</button>
<button id="reset" (click)="reset()">Reset</button>

我收到此错误:

NG9: Property 'counter' does not exist on type 'number'. [plugin angular-compiler]

src/counter/counter.component.html:1:41:
  1 │ <div>Counτer : {{ (counterFinal$ |async).counter }}</div>
    ╵                                         ~~~~~~~

Error occurs in the template of component Counter.

src/counter/counter.component.ts:12:15:
  12 │   templateUrl: './counter.component.html',

使用带有独立组件的 Angular 17 和最新的 ngrx。 预先感谢!

angular rxjs ngrx
1个回答
0
投票

select
方法中设置返回状态的类型似乎可以解决该问题。

我们还可以定义一个包含状态属性类型详细信息的接口。

  ...
  counterFinal$: Observable<CounterState>; //= this.store.select("counter");

  constructor(private store: Store<{ counter: CounterState }>) {
    //this.store.select('counter').subscribe(data => this.c = data.counter);
    this.counterFinal$ = this.store.select('counter');
  }
  ...

完整代码:

import { Component } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import 'zone.js';
import {
  Action,
  ActionReducer,
  createReducer,
  on,
  provideStore,
} from '@ngrx/store';
import { Store, select } from '@ngrx/store';
import { Observable, map } from 'rxjs';
import { AsyncPipe, CommonModule } from '@angular/common';
import { createAction } from '@ngrx/store';

export interface CounterState {
  counter: number;
}

export const initialState: CounterState = {
  counter: 0,
};

export const plus = createAction('counter +');
export const minus = createAction('counter -');
export const reset = createAction('counter Reset');

export const CounterReducer = createReducer<
  CounterState,
  Action,
  ActionReducer<CounterState, Action>
>(
  initialState,
  on(plus, (state) => ({ ...state, counter: state.counter + 1 })),
  on(minus, (state) => ({ counter: state.counter - 1 })),
  on(reset, (state) => ({ counter: 0 }))
);

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [CommonModule],
  template: `
    <div>Counτer : {{ (counterFinal$ | async)?.counter || 0 }}</div>
    <button id="plus" (click)="plus()">+</button>
    <button id="minus" (click)="minus()">-</button>
    <button id="reset" (click)="reset()">Reset</button>
  `,
})
export class App {
  //c!:number;
  counterFinal$: Observable<CounterState>; //= this.store.select("counter");

  constructor(private store: Store<{ counter: CounterState }>) {
    //this.store.select('counter').subscribe(data => this.c = data.counter);
    this.counterFinal$ = this.store.select('counter');
  }

  plus() {
    //this.count = this.count + 1;
    console.log('+');
    this.store.dispatch(plus());
  }

  minus() {
    //this.count = this.count - 1;
    console.log('-');
    this.store.dispatch(minus());
  }

  reset() {
    //this.count = 0;
    console.log('0');
    this.store.dispatch(reset());
  }
}

bootstrapApplication(App, {
  providers: [provideStore({ counter: CounterReducer })],
});

Stackblitz 演示

© www.soinside.com 2019 - 2024. All rights reserved.