2016-03-27 14 views
2

私はアプリケーションの状態データを格納する指示文app.service.tsを持っています。この指示文を他のコンポーネントから使用しようとしていますので、アプリケーションの状態を取得して設定できます。私はこのディレクティブからプロパティをバインドしようとすると、それはときappState.get().loadState == trueangular2プロパティのエラーが変更されました

アプリここで私は、ロードテキストを表示しようとしているビューに私に

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]

をこのエラーが発生しますが

.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; 
    } 
} 

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

を有していると仮定app.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

ソリューションは、状態から負荷状態を表示するために特別なコンポーネントを作ることによって、非常に単純です

LoaderCmpディスプレイloadState直接appStateサービスから属性:サービスapp.service.ts、ここにコードがあります。

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; 
    } 
} 

だけのアプリのディレクティブにLoaderCmpを追加し、不要な構成をapp.ts。任意のコンポーネントから、例えば

@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を利用できると思います。

これをAppコンポーネントに挿入し、その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'の必要はないようですが、私はstateサービスにobservableを使う考えが好きです。複数のプロパティをサポートしているので、通常のapp.service.tsを選択します。私は両方の方法で解決策を書いた。 –

0

wp何とかAngularsゾーンの外でコードを実行しているようです。 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

観測可能と共有機能を使って、別の解決策は、ここでは1つの属性のみをサポートしてapp.state.tsapp.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); 
    } 
} 

だけのアプリのディレクティブにLoaderCmpを追加し、不要な構成をapp.ts。例えば、任意の成分、から

@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) 
    ); 
} 
} 
関連する問題