计划:
1、创建一个angular模版的运用程序
2、引用bootstrap
3、引用@ngrx
ng new pet-tags-ngrx
cd pet-tags-ngrx
npm install bootstrap --save
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/pet-tags-ngrx",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
"aot": true,
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.css",
"node_modules/bootstrap/dist/css/bootstrap.min.css"
],
"scripts": []
},
A、在app下添加文件夹core,把app.component的ts、css、html等4个文件移入core文件夹中间。
B、修改app.module.ts中AppComponent的存放位置为
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './core/app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
ng add @ngrx/store
ng add @ngrx/store-devtools
ng add @ngrx/effects
ng add @ngrx/router-store
添加之后,package.json文件中,dependencies节点为
"dependencies": {
"@angular/animations": "~11.2.4",
"@angular/common": "~11.2.4",
"@angular/compiler": "~11.2.4",
"@angular/core": "~11.2.4",
"@angular/forms": "~11.2.4",
"@angular/platform-browser": "~11.2.4",
"@angular/platform-browser-dynamic": "~11.2.4",
"@angular/router": "~11.2.4",
"@ngrx/effects": "^11.0.1",
"@ngrx/router-store": "^11.0.1",
"@ngrx/store": "^11.0.1",
"@ngrx/store-devtools": "^11.0.1",
"bootstrap": "^4.6.0",
"rxjs": "~6.6.0",
"tslib": "^2.0.0",
"zone.js": "~0.11.3"
},
app.module.ts文件也添加好引用
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './core/app.component';
import { StoreModule } from '@ngrx/store';
import { StoreDevtoolsModule } from '@ngrx/store-devtools';
import { environment } from '../environments/environment';
import { EffectsModule } from '@ngrx/effects';
import { StoreRouterConnectingModule } from '@ngrx/router-store';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule,
StoreModule.forRoot({}, {}),
StoreDevtoolsModule.instrument({ maxAge: 25, logOnly: environment.production }),
EffectsModule.forRoot([]),
StoreRouterConnectingModule.forRoot()
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
ng serve --open
完美,如计划般的完美。
计划:
1、创建一个带路由的模块,再在模块中构建计算器组件。
2、修改模块和跟模块的路由器,正确导航到计算器组件。
ng g m example --routing
在app下增加example文件夹,同时增加example.module.ts(模块文件)、example-routing.module.ts(路由文件)
ng g c example/containers/counter -m example
A、在example下,创建文件夹containers
B、在containers下,创建文件夹counter
C、在文件夹counter下,创建counter组件的四个文件
D、修改模块文件example.module.ts,增加对counter组件的引用和声明.
修改example-routing.module.ts 模块:
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { CounterComponent } from './containers/counter/counter.component';
const routes: Routes = [
{ path: '', redirectTo: 'counter', pathMatch: 'full' },
{ path: 'counter', component: CounterComponent },
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class ExampleRoutingModule { }
引入CounterComponent组件,在routes数组中定义空路由和counter路由.
修改app-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
const routes: Routes = [
{ path: '', redirectTo: 'example', pathMatch: 'full' },
{path:'example',loadChildren:()=>import('./example/example.module').then(m=>m.ExampleModule)},
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
<div class="container-fluid">
<div class="row">
<div class="col bg-dark text-white">
<a class="navbar-brand">ngrx入门</a>
</div>
</div>
<router-outlet></router-outlet>
</div>
<div class="row">
<div class="col mt-2">
<p class="title">计数器: 没有数据请求的ngrx demo</p>
</div>
</div>
<div class="row">
<div class="col">
<div class=" col btn-group m-2">
<button class="btn btn-primary">加</button>
<button class="btn btn-danger">减</button>
<button class="btn btn-secondary">重置</button>
</div>
</div>
</div>
<div class="row">
<div class="col">
<div>
<span style="font-size: 18px;font-weight: bold">当前数:18</span>
</div>
</div>
</div>
完美,如计划般一样的完美。
计划:使用基本的事件绑定机制,实现计数器组件的功能。
counter.component.html
<div class="row">
<div class="col mt-2">
<p class="title">计数器: 没有数据请求的ngrx demo</p>
</div>
</div>
<div class="row">
<div class="col">
<div class=" col btn-group m-2">
<button class="btn btn-primary" (click)="increment()">加</button>
<button class="btn btn-danger" (click)="decrement()">减</button>
<button class="btn btn-secondary" (click)="reset()">重置</button>
</div>
</div>
</div>
<div class="row">
<div class="col">
<div>
<span style="font-size: 18px;font-weight: bold">当前数:{{counter$}}</span>
</div>
</div>
</div>
counter.component.ts
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-counter',
templateUrl: './counter.component.html',
styleUrls: ['./counter.component.css']
})
export class CounterComponent implements OnInit {
counter$: number = 0;
constructor() { }
ngOnInit(): void {
}
increment() {
this.counter$ +=1 ;
}
decrement() {
this.counter$ -=1;
}
reset() {
this.counter$ = 0;
}
}
完美,和计划一样的完美
1、构建action
ng g class example/actions/counter.actions
A、example模块的所有action文件都存放在actions文件夹下
B、修改counter.actions.ts为
import { Action } from '@ngrx/store';
// 为“加-减-重置”三个动作,定义三个字符串常量,用于action中区分.
export const INCREMENT = '[Counter] Increment'; // 这个是唯一的,不能写重复哦
export const DECREMENT = '[Counter] Decrement';
export const RESET = '[Counter] Reset';
//为“加-减-重置”三个动作,分别定义action
export class IncrementAction implements Action {
readonly type = INCREMENT;
constructor() { }
}
export class DecrementAction implements Action {
readonly type = DECREMENT;
constructor() { }
}
export class ResetAction implements Action {
readonly type = RESET;
constructor() { }
}
// 导出定义的Action
export type Actions
= IncrementAction
| DecrementAction
| ResetAction;
ng g class example/reducer/counter.reducer
A、example模块的所有reducer文件都存放在reducer文件夹下
B、修改counter.reducer.ts为
import * as counter from '../actions/counter.actions';
import * as counterState from '../state/counter.state';
// 定义reducer 根据action type来处理状态,返回对应数据
export function reducer(state = counterState.initialState, action: counter.Actions): counterState.State {
switch (action.type) {
case counter.INCREMENT: // 根据你传的行为的类型,来判断做什么事情
return {
counter: state.counter + 1
};
case counter.DECREMENT:
return {
counter: state.counter - 1
};
case counter.RESET:
return {
counter: 0
};
default:
return { ...state };
}
}
// 导出去
export const getCounter = (state: counterState.State) => state.counter;
3、构建index.ts
ng g class example/reducer/index
修改index.ts为
import {createFeatureSelector, createSelector} from '@ngrx/store';
import * as fromCounter from './counter.reducer';
export interface State {
counter: fromCounter.State;
}
export const reducers = {
counter: fromCounter.reducer,
};
export const getExampleState = createFeatureSelector<State>('example');
// 计数器
export const getCounterState = createSelector(getExampleState, (state: State) => state.counter);
export const getCounterCounter = createSelector(getCounterState, fromCounter.getCounter);
4、在模块中导入reduecer
example.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ExampleRoutingModule } from './example-routing.module';
import { CounterComponent } from './containers/counter/counter.component';
import { StoreModule } from '@ngrx/store';
import { reducers } from './reducer';
@NgModule({
declarations: [CounterComponent],
imports: [
CommonModule,
ExampleRoutingModule,
StoreModule.forFeature('example', reducers), // 挂在在state上
]
})
export class ExampleModule { }
5、修改计时器组件的html
counter.component.html
<div class="row">
<div class="col mt-2">
<p class="title">计数器: 没有数据请求的ngrx demo</p>
</div>
</div>
<div class="row">
<div class="col">
<div class=" col btn-group m-2">
<button class="btn btn-primary" (click)="increment()">加</button>
<button class="btn btn-danger" (click)="decrement()">减</button>
<button class="btn btn-secondary" (click)="reset()">重置</button>
</div>
</div>
</div>
<div class="row">
<div class="col">
<div>
<span style="font-size: 18px;font-weight: bold">当前数:{{counter$ | async}}</span>
</div>
</div>
</div>
6、修改计时器组件的ts
counter.component.ts
import { Component, OnInit } from '@angular/core';
import * as fromExample from '../../reducer';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs';
import * as counterAction from '../../actions/counter.actions';
import { OnDestroy } from '@angular/core';
@Component({
selector: 'app-counter',
templateUrl: './counter.component.html',
styleUrls: ['./counter.component.css']
})
export class CounterComponent implements OnInit , OnDestroy {
counter$: Observable<number>;
constructor(private store: Store<fromExample.State>) {
this.counter$ = store.select(fromExample.getCounterCounter);
}
ngOnDestroy(): void {
throw new Error('Method not implemented.');
}
ngOnInit(): void {
}
increment() {
this.store.dispatch(new counterAction.IncrementAction());
}
decrement() {
this.store.dispatch(new counterAction.DecrementAction());
}
reset() {
this.store.dispatch(new counterAction.ResetAction());
}
}
代码参看:
https://gitee.com/lxhjh2015/ngrx/tree/step1/