这是我的简单计数器的 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>
结果我得到了这个:
在 ngRx 开发工具上,我的商店看起来运行良好。我只是不知道如何获取计数器属性并将其显示在 html 上(使用异步)
如果我将 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。 预先感谢!
在
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 })],
});