2016-03-27 65 views
2

我有一個指令app.service.ts存儲應用程序狀態數據,以及我試圖使用從其他組件這個指令,所以我可以獲取和設置應​​用程序的狀態(這是工作)。angular2屬性更改錯誤

但是它給了我這個錯誤,當我嘗試從本指令的屬性綁定到視圖

EXCEPTION: Expression 'loading: {{loadState}} in [email protected]:23' has changed after it was checked. Previous value: 'false'. Current value: 'true' in [loading: {{loadState}} in [email protected]:23]

這裏我想顯示加載文本,當appState.get().loadState == true

應用.service.ts - source

import {Injectable} from 'angular2/core'; 
import {WebpackState} from 'angular2-hmr'; 

@Injectable() 
export class AppState { 
    _state = {}; // you must set the initial value 
    constructor(webpackState: WebpackState) { 
    this._state = webpackState.select('AppState',() => this._state); 
    } 

    get(prop?: any) { 
    return this._state[prop] || this._state; 
    } 

    set(prop: string, value: any) { 
    return this._state[prop] = value; 
    } 
} 

app.ts

import {AppState} from './app.service'; 

export class App{ 

    constructor(public appState: AppState) { 
     this.appState.set('loadState', false); 
    } 
    get loadState() { 
     return this.appState.get().loadState; 
    } 
} 

app.html

<div class="app-inner"> 
    <p>loading: {{loadState}}</p> 
    <header></header> 
    <main layout="column" layout-fill> 
    <router-outlet></router-outlet> 
    </main> 
</div> 

app.ts假設有一個子組件home.ts並加載到視圖

home.ts

export class HomeCmp { 
page; 

constructor(private wp: WPModels, private appState: AppState) {} 

ngOnInit() { 
    var pageId = this.appState.get().config.home_id; 
    this.appState.set('loadState', true); // before http request 

    this.wp.fetch(WPEnpoint.Pages, pageId).subscribe(
    res => { 
     this.page = res; 
     this.appState.set('loadState', false); // after http request 
    }, 
    err => console.log(err) 
    ); 
} 
} 
+0

,可以使用共享對象此。我認爲HTML中的{{loadState}}總是會以這種方式拋出錯誤。而是嘗試綁定在服務中使用的一些共享變量。例如。 _state = {}是一個共享對象。設置一些屬性,然後在HTML中,你可以通過{{appState._state.someProperty}}來訪問它' – micronyks

+0

@micronyks相同的錯誤 –

回答

1

該解決方案是相當簡單的,通過使一個特殊的部件,以從狀態顯示裝載狀態服務app.service.ts,這裏是代碼:

LoaderCmp顯示loadState從直接APPSTATE服務屬性。

import {Component} from 'angular2/core'; 
import {AppState} from "../../app.service"; 

@Component({ 
    selector: 'loader', 
    template: ` 
    loading state : {{appState._state.loadState}} 
    ` 
}) 

export class LoaderCmp{ 
    constructor(private appState: AppState) {} 
} 

app.service.ts持有我們的應用程序的狀態。

import {Injectable} from 'angular2/core'; 
import {WebpackState} from 'angular2-hmr'; 

@Injectable() 
export class AppState { 
    _state = {}; // you must set the initial value 
    constructor(webpackState: WebpackState) { 
    this._state = webpackState.select('AppState',() => this._state); 
    } 

    get(prop?: any) { 
    return this._state[prop] || this._state; 
    } 

    set(prop: string, value: any) { 
    return this._state[prop] = value; 
    } 
} 

app.ts不需要任何配置,只需添加LoaderCmp到應用程序的指令。

@Component({ 
    selector: 'app', 
    directives: [HeaderCmp, LoaderCmp], 
    template: ` 
    <div class="app-inner"> 
    <loader></loader> 
    <header></header> 
    <main> 
     <router-outlet></router-outlet> 
    </main> 
    </div> 
    ` 
}) 
export class App { 
    constructor() { } 
} 

更新應用程序狀態從任何組件,例如:home.ts

export class HomeCmp { 
page; 

constructor(private wp: WPModels, private appState: AppState) {} 

ngOnInit() { 
    var pageId = this.appState.get().config.home_id; 
    this.appState.set('loadState', true); // before http request 

    this.wp.fetch(WPEnpoint.Pages, pageId).subscribe(
    res => { 
     this.page = res; 
     this.appState.set('loadState', false); // after http request 
    }, 
    err => console.log(err) 
    ); 
} 
} 
0

我認爲你可以充分利用ChangeDetectorRef類及其方法detectChanges。

對於此注入到你的應用程序組件並調用該方法在其ngAfterViewInit。

constructor(private cdr: ChangeDetectorRef) {} 

    ngAfterViewInit() { 
     this.cdr.detectChanges(); 
    } 

詳情請參閱此問題:

話雖這麼說,我會用一個觀察特性到您的狀態服務通知組件時,它的讀取。

@Injectable() 
export class AppStateService { 
    state: string; 
    state$: Observable<string>; 
    private stateObserver : Observer<string>; 

    constructor(){ 
    this.state$ = new Observable(observer => this.stateObserver = observer).share(); 
    } 

    updateState(newState) { 
    this.state = newState; 
    this.stateObserver.next(newState); 
    } 
} 

而在組件:

@Component({...}) 
export class SomeComponent { 
    constructor(service: AppStateService) { 
    service.state$.subscribe(newState => { 
     (...) 
    }); 
    } 
} 

請參見以下鏈接瞭解詳情:

+0

我試過這個,但它給了我完全相同的錯誤 –

+0

你的意思是你試圖調用' detectChanges'?在哪個組件生命週期中掛鉤?否則,我在我的片段中輸入了一個錯字。它是'ngAfterViewInit'代替... –

+0

似乎沒有必要使用'detectChanges',我喜歡使用observable作爲狀態服務的想法。但我選擇了普通的'app.service.ts',因爲它支持多個屬性。我爲這兩種方式寫了解決方案。 –

0

wp莫名其妙似乎運行在安格拉斯區以外的代碼。要強制代碼回Angulars區,使用zone.run()作爲代碼更新性質如下所示,視圖結合:

import {NgZone} from 'angular2/core'; 

export class HomeCmp { 
page; 

constructor(private zone:NgZone, private wp: WPModels, private appState: AppState) {} 

ngOnInit() { 
    var pageId = this.appState.get().config.home_id; 
    this.appState.set('loadState', true); // before http request 

    this.wp.fetch(WPEnpoint.Pages, pageId).subscribe(
    res => { 
     this.zone.run(() => { 
     this.page = res; 
     this.appState.set('loadState', false); // after http request 
     }); 
    }, 
    err => console.log(err) 
    ); 
} 
} 
0

通過使用可觀察的和共享功能另一種解決方案,在這裏我們用app.state.ts僅支持一個屬性改變了app.service.ts

LoaderCmp直接顯示來自appState服務的loadState屬性。

import {Component} from 'angular2/core'; 
import {AppStateService} from "../../app.state"; 

@Component({ 
    selector: 'loader', 
    template: ` 
    loading state : {{ active }} 
    ` 
}) 

export class LoaderCmp{ 
    active; 
    constructor(private service: AppStateService) {} 
    ngOnInit() { 
     this.service.state$.subscribe(newState => { 
     this.active = newState; 
    }); 
    } 
} 

app.state.ts持有我們的負載狀態。

import "rxjs/add/operator/share"; 
import {Observable} from "rxjs/Observable"; 
import {Observer} from "rxjs/Observer"; 
import {Injectable} from "angular2/core"; 

@Injectable() 
export class AppStateService { 
    state: boolean = false; 
    state$: Observable<boolean>; 
    private stateObserver : Observer<boolean>; 

    constructor(){ 
    this.state$ = new Observable(observer => this.stateObserver = observer).share(); 
    } 

    updateState(newState) { 
     this.state = newState; 
     this.stateObserver.next(newState); 
    } 
} 

app.ts不需要任何配置,只需添加LoaderCmp到應用程序的指令。從任何組件,例如

@Component({ 
    selector: 'app', 
    directives: [HeaderCmp, LoaderCmp], 
    template: ` 
    <div class="app-inner"> 
    <loader></loader> 
    <header></header> 
    <main> 
     <router-outlet></router-outlet> 
    </main> 
    </div> 
    ` 
}) 
export class App { 
    constructor() { } 
} 

更新應用程序狀態:home.ts

export class HomeCmp { 
page; 

constructor(private wp: WPModels, private service: AppStateService) {} 

ngOnInit() { 
    this.service.updateState(true); // before http request 

    this.wp.fetch(WPEnpoint.Pages, pageId).subscribe(
    res => { 
     this.page = res; 
     this.service.updateState(false); // after http request 
    }, 
    err => console.log(err) 
    ); 
} 
}