【问题标题】:angular2 property has changed errorangular2 属性已更改错误
【发布时间】:2016-07-15 04:47:03
【问题描述】:

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

【问题讨论】:

  • 您可以为此使用共享对象。我认为 HTML 中的 {{loadState}} 总是会以这种方式抛出错误。而是尝试绑定一些在服务中使用的共享变量。例如。 _state={} 是一个共享对象。为其设置一些属性,然后在 html 中您可以通过 {{appState._state.someProperty}} 访问它
  • @micronyks 同样的错误

标签: angular


【解决方案1】:

解决方案很简单,通过制作一个特殊的组件来显示来自状态服务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)
   );
 }
}

【讨论】:

    【解决方案2】:

    https://github.com/angular/angular/issues/6005 这是开发模式按预期工作的一个功能。调用 enableProdMode( ) - 查看引导应用程序何时防止抛出异常。

    您需要再次触发更改检测。 Triggering Angular2 change detection manually

    【讨论】:

      【解决方案3】:

      我认为您可以利用 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 => {
            (...)
          });
        }
      }
      

      查看这些链接了解更多详情:

      【讨论】:

      • 我试过了,但它给了我完全相同的错误
      • 你的意思是你试图打电话给detectChanges?在哪个组件生命周期钩子中?否则我在我的 sn-p 中打错了。而是ngAfterViewInit...
      • 似乎不需要detectChanges,我喜欢使用observable作为状态服务的想法。但我选择了普通的app.service.ts,因为它支持多个属性。我为这两种方式编写了解决方案。
      【解决方案4】:

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

      【讨论】:

        【解决方案5】:

        另一种使用可观察和共享功能的解决方案,这里我们将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)
           );
         }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2014-12-17
          • 1970-01-01
          • 2016-06-13
          • 2022-01-18
          • 1970-01-01
          • 2016-09-13
          • 1970-01-01
          相关资源
          最近更新 更多