我有一个指令app.service.ts
,它存储了应用程序状态数据,我正在尝试使用其他组件中的这个指令,这样我就可以获取并设置应用程序的状态(这是有效的)。
但是当我尝试将此指令中的属性绑定到视图
时,它会给我这个错误 EXCEPTION: Expression 'loading: {{loadState}} in App@0:23' has changed after it was checked. Previous value: 'false'. Current value: 'true' in [loading: {{loadState}} in App@0:23]
这里我试图在appState.get().loadState == true
app.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 :(得分:2)
解决方案非常简单,通过创建一个特殊组件来显示状态服务app.service.ts
的加载状态,这里是代码:
LoaderCmp 直接在appState服务中显示loadState
属性。
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)
);
}
}
答案 1 :(得分:0)
我认为您可以利用ChangeDetectorRef类及其方法detectChanges。
为此将其注入App组件并在其ngAfterViewInit中调用该方法。
constructor(private cdr: ChangeDetectorRef) {}
ngAfterViewInit() {
this.cdr.detectChanges();
}
有关详细信息,请参阅此问题:
话虽如此,我会在您的状态服务中使用一个observable属性,以便在读取时通知组件。
@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 => {
(...)
});
}
}
有关详细信息,请参阅以下链接:
答案 2 :(得分:0)
https://github.com/angular/angular/issues/6005 这是开发模式按预期工作的功能。调用enableProdMode() - 查看引导应用程序何时阻止抛出异常。
您需要再次触发更改检测。 Triggering Angular2 change detection manually
答案 3 :(得分: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)
);
}
}
答案 4 :(得分:0)
使用observable和share函数的另一个解决方案,我们在这里更改了app.service.ts
app.state.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)
);
}
}