2017-08-15 104 views
3

我一整天都在努力讓我的Angular應用程序與ngrx/store 4一起工作。在經歷了很多不成功的時間之後,我決定創建一個新的絕對最小應用程序來檢查是否有其他問題。但最小的應用程序根本不工作:(ngrx商店4選擇不工作

在Chrome Redux devtools中,我可以看到,商店已經正確更新,但UI沒有更新,而且app.component.ts中的日誌語句this.age$.subscribe(console.log))永遠不會執行。我真的不知道我錯過了什麼。

下面是相關的代碼。

的package.json

{ 
    "dependencies": { 
    "@angular/animations": "^4.2.4", 
    "@angular/common": "^4.2.4", 
    "@angular/compiler": "^4.2.4", 
    "@angular/core": "^4.2.4", 
    "@angular/forms": "^4.2.4", 
    "@angular/http": "^4.2.4", 
    "@angular/platform-browser": "^4.2.4", 
    "@angular/platform-browser-dynamic": "^4.2.4", 
    "@angular/router": "^4.2.4", 
    "@ngrx/store": "^4.0.2", 
    "@ngrx/store-devtools": "^4.0.0", 
    "rxjs": "^5.4.2", 
    "zone.js": "^0.8.14" 
    }, 
    "devDependencies": { 
    "@types/node": "^8.0.22", 
    "typescript": "^2.4.2" 
    } 
} 

個app.module.ts

import { AppState } from './state'; 
import { BrowserModule } from '@angular/platform-browser'; 
import { NgModule } from '@angular/core'; 
import { StoreModule } from '@ngrx/store'; 
import { StoreDevtoolsModule } from '@ngrx/store-devtools' 
import { reducer } from './reducer'; 

import { AppComponent } from './app.component'; 

@NgModule({ 
    declarations: [ 
    AppComponent 
    ], 
    imports: [ 
    BrowserModule, 
    StoreModule.forRoot({ reducer }), 
    StoreDevtoolsModule.instrument(), 
    ], 
    providers: [], 
    bootstrap: [AppComponent] 
}) 
export class AppModule { } 

state.ts

export interface AppState { 
    age: number 
} 

export const INITIAL_STATE: AppState = { 
    age: 1 
} 

actions.ts

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

export const ACTION_TYPE = 'CHANGE_AGE_ACTION'; 

export class ChangeAgeAction implements Action { 
    readonly type: string = ACTION_TYPE; 

    constructor(public payload?: number) { 
    } 
} 

reducer.ts

import { ChangeAgeAction } from './actions'; 
import { Action } from '@ngrx/store'; 
import { AppState, INITIAL_STATE } from './state'; 
import { ACTION_TYPE } from './actions'; 

function changeName(state: AppState, action: ChangeAgeAction): AppState { 
    return { 
    age: action.payload 
    } 
} 

export function reducer(state: AppState = INITIAL_STATE, action: Action) { 
    switch (action.type) { 
    case ACTION_TYPE: 
     return changeName(state, action); 

    default: 
     return state; 
    } 
} 

app.component.ts

import { ChangeAgeAction } from './actions'; 
import { AppState } from './state'; 
import { Component } from '@angular/core'; 
import { Observable } from 'rxjs/Observable'; 
import { Store } from '@ngrx/store'; 
import { map } from 'rxjs/operator/map'; 

@Component({ 
    selector: 'app-root', 
    templateUrl: './app.component.html', 
    styleUrls: ['./app.component.css'] 
}) 
export class AppComponent { 
    age$: Observable<number>; 

    constructor(private store: Store<AppState>) { 
    this.age$ = this.store.select<number>(state => state.age); 
    this.age$.subscribe(console.log); 
    } 

    changeAge() { 
    this.store.dispatch(new ChangeAgeAction(Math.random())); 
    } 
} 

app.component.html

<h3>Age: {{ age$ | async }}</h3> 
<button (click)="changeAge()">Change Age</button> 

回答

4

您的應用程序狀態不正確建模。實際上有一個包含你年齡的減速器對象。下面是我所做的更改,以使應用程序的工作:

在state.ts

export interface AppState { 
    reducer: { 
    age: number 
    } 
} 

在應用組件:

export class AppComponent { 
    age$: Observable<number>; 

    constructor(private store: Store<AppState>) { 
    this.age$ = this.store.select<number>(state => state.reducer.age); 
    this.age$.subscribe(console.log); 
    } 

    changeAge() { 
    this.store.dispatch(new ChangeAgeAction(Math.random())); 
    } 
} 

注意,您必須在「減速」對象的原因,你的AppState是因爲當你設置這個時:StoreModule.forRoot({reducer})。它從減速器獲得「減速器」的名稱。

在,如果你不喜歡的東西StoreModule.forRoot({} ageReducer),那麼你的應用程序的狀態應該是這樣的。換句話說:

export interface AppState { 
    ageReducer: { 
    age: number 
    } 
} 

下面的代碼的工作版本:

https://stackblitz.com/edit/angular-jxszwh

+0

哇,這是訣竅!非常感謝你!我想我會浪費幾天時間來發現問題。在我看來,這在ngrx/store文檔中沒有足夠的表達能力。我從來不會得出結論,減速器名稱必須與州名相同。 –

+0

我同意這是令人困惑的。我不止一次地絆倒了自己。 – seescode

0

另請注意,如果您使用角度異步管道,則無需在您的可觀察值上調用.subscribe。異步管道處理訂閱/取消訂閱。