angular2属性已更改错误

时间:2016-03-27 22:53:48

标签: angular

我有一个指令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)
   );
 }
}

5 个答案:

答案 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)
   );
 }
}
相关问题