如何在Angular 2中观看复杂对象,就像我们在Angular 1中使用$ watch一样

时间:2016-06-17 19:09:52

标签: angularjs angular typescript1.8

我们能够在复杂对象上应用$watch,如何在Angular 2中执行类似操作。

Angular 1

$scope.data = {name : "somvalue"}
$scope.$watch('data.name', function(newValue, oldValue) {
    scope.counter = scope.counter + 1;
});

Angular 2

export class MyData{
   name: string;
} 

export class MyComponent implements OnInit {
   @Input() data: MyData;

   constructor(private ds: MyService){
      this.data = ds.data;
   }

   // $watch('data.name', function(newValue, oldValue) {
   //   scope.counter = scope.counter + 1;
   // });
}

现在如果data.name在服务中发生变化,如何监视组件本身的变化,请注意数据不是可观察的,它只是一个常规对象。

更新

Please see Plunk for an example

提前致谢!!

4 个答案:

答案 0 :(得分:2)

Angular检查属性,如果它们在模板中绑定,则甚至是内部对象。

对于复杂对象,首选选项是使用Observable主动通知Angular2有关更改。

您还可以通过实施DoCheck

来使用自定义更改检测

答案 1 :(得分:0)

实现组件生命周期钩子“ngOnChanges”:

@expectedException \InvalidArgumentException

<强>更新

我找到了一个可行的解决方法。实现DoCheck钩子而不是OnChanges。

import { OnChanges } from '@angular/core';

@Component({
    selector: 'child',
    template: `
        <h2>Child component</h2>
        {{ person }}
    `
})
class ChildComponent implements OnChanges {
    @Input() person: string;

    ngOnChanges(changes: {[ propName: string]: SimpleChange}) {
        console.log('Change detected:', changes[person].currentValue);
    }

}

请记住,doCheck运行多次,如果错过使用会导致性能问题。

答案 2 :(得分:0)

一般情况下,除非您使用不同的更改检测策略,否则您的组件将会听取所有对象的突变。

import { OnChanges } from '@angular/core';

@Component({
    selector: 'child',
    template: `
        <h2>Child component</h2>
        {{ person }}
    `,
   changeDetection:ChangeDetectionStrategy.OnPush/Default/CheckOnce/Always
})
class ChildComponent implements OnChanges {
    @Input() person: string;

    ngOnChanges(changes: {[ propName: string]: SimpleChange}) {
        console.log('Change detected:', changes[person].currentValue);
    }

这:

   changeDetection:ChangeDetectionStrategy.OnPush/Default/CheckOnce/Always

将定义当其中一个输入更新时组件的行为方式。

最重要的一个是OnPush和Default。

OnPush表示仅当整个对象替换为新对象时才更新组件,而Default表示如果任何嵌套值已更新(变异)则更新我。

而且,如果你不在组件中使用该对象,Angular将忽略并且不会更新视图(为什么会这样)。

然后你可以轻松地挂钩到ngOnChange生命周期钩子,而另一个答案建议得到更新。

答案 3 :(得分:0)

如果您想进行更多自定义并进行更多手动操作,它是DoCheck的替代解决方案。当然,根据Günter Zöchbauer answer,这是一种Observable解决方案。

我使用装饰器来告诉TS必须检测到该属性,因此让我们开始吧。

export class ChangeDetection {
  private emitedChangeDetector: ChangeDetection;

  private _value;
  get value() {
    return this._value;
  }

  /** @warning Don't use it */
  __action: (value: any) => void;

  /** @warning Don't use it */
  __change(value: any) {
    this._value = value;
    this.__action && this.__action(value);
    this.emitedChangeDetector?.__change &&
      this.emitedChangeDetector.__change(this.emitedChangeDetector.value);
  }

  onChange<T = any>(action: (value: T) => void) {
    this.__action = action;

    return this;
  }

  emit(extendedObserver: ChangeDetection) {
    this.emitedChangeDetector = extendedObserver;
  }
}

// For manage list of changes
export class ChangeDetectionList {
  changeDetectors: ChangeDetection[];

  constructor(...changeDetectors: ChangeDetection[]) {
    this.changeDetectors = changeDetectors;
  }

  onChange(callback: (data: any) => void): ChangeDetectionList {
    this.changeDetectors.forEach((a: ChangeDetection) => a.onChange(callback));

    return this;
  }

  emit(changeDetector: ChangeDetection): ChangeDetection {
    this.changeDetectors.forEach((a: ChangeDetection) =>
      a.emit(changeDetector)
    );

    return changeDetector;
  }
}

/**
 * @usageNotes{
 * ```typescript
 * @ChangeDetector()
 * data : string
 * ```
 * Gives you a ChangeDetection object with the name of data$ that fires "onChange()" function when "data" is changed
 */
export function ChangeDetector(suffix: string = "$") {
  return function (prototype: any, key: string | symbol) {
    const changeDetectorName: string = `${key.toString()}${suffix}`;
    if (!prototype[changeDetectorName]) {
      Object.defineProperty(prototype, changeDetectorName, {
        value: new ChangeDetection(),
      });

      const changeDetectorObject = prototype[changeDetectorName] as ChangeDetection;
      Object.defineProperty(prototype, key, {
        set(value: any) {
          Object.defineProperty(this, key, {
            get() {
              return changeDetectorObject.value;
            },
            set(v: any) {
              changeDetectorObject.__change(v);
            },
            enumerable: true,
          });
          this[key] = value;
        },
        enumerable: true,
        configurable: true,
      });
    }
  };
}

现在,它很容易使用。只需在要检测的每个属性上放置@ChangeDetector()装饰器,或定义一个changeDetector属性,以在属性更改时引发onChange事件。

下面是一个示例,向您展示如何使用它。

export class PersonModel {
  @ChangeDetector()
  changeDetector: number;
  changeDetector$: ChangeDetection; // It's just for intellisense, you can ignore it.

  firstName: string;
  lastName: string;
  age: number;

  constructor() {
    this.age = 34;
    this.changeDetector = 0;
  }
}

export class ProfileModel {
  @ChangeDetector('Listener')
  person: PersonModel;
  personListener: ChangeDetection; // It's just for intellisense, you can ignore it.

  constructor() {
    this.person = new PersonModel();
  }
}

export class ProfileService {
  profile: ProfileModel;

  constructor() {
    this.profile = new ProfileModel();
    // calls 'profileChanged()' when 'person' is changed
    this.profile.personListener.onChange((_) => this.profileChanged());
  }

  setProfile() {
    Object.assign(this.profile.person, { firstName: 'Pedram', lastName: 'Ahmadpour' });
    this.profile.person.changeDetector++;
  }

  private profileChanged() {
    console.log('profile is', JSON.stringify(this.profile));
  }
}

export class ProfileComponent {
  constructor(private profileService: ProfileService) {
    // Now, profileService.profile listen to its own child changes
    this.profileService.profile.person.changeDetector$.emit(profileService.profile.personListener);
    this.profileService.setProfile();
  }

  happyBirthday() {
    this.profileService.profile.person.age = 35;
    console.log('Happy birthday');
    this.profileService.profile.person.changeDetector++;
  }
}

或者在实际的Angular项目中:

  @Input()
  @ChangeDetector()
  data: ProfileModel;
  data$: ChangeDetection;

并进行测试:

const profileService = new ProfileService();
const profileComponent = new ProfileComponent(profileService);
profileComponent.happyBirthday();