简体   繁体   English

如何像在 Angular 1 中一样使用 $watch 观察 Angular 2 中的复杂对象

[英]How to watch on complex object in Angular 2 like we did in Angular 1 using $watch

We were able to apply $watch on complex object, how to do the similar in Angular 2.我们能够在复杂对象上应用$watch ,如何在 Angular 2 中做类似的事情。

Angular 1角 1

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

Angular 2角 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;
   // });
}

Now If data.name changes in service, How to watch for the changes in Component itself, Please note data is not an observable it is just a regular object.现在如果服务中的data.name发生变化,如何观察组件本身的变化,请注意数据不是可观察的,它只是一个常规对象。

Update更新

Please see Plunk for an example请参阅 Plunk 示例

Thanks in advance!!提前致谢!!

Angular checks properties, even deep inside objects if they are bound to in the template. Angular 会检查属性,如果它们绑定到模板中,甚至会检查对象内部的深处。

For complex objects the preferred option is to use Observable to actively notify Angular2 about changes.对于复杂的对象,首选的选项是使用Observable主动通知 Angular2 有关更改的信息。

You can also use custom change detection by implementing DoCheck您还可以通过实施DoCheck来使用自定义更改检测

Implement the component lifecycle hook "ngOnChanges":实现组件生命周期钩子“ngOnChanges”:

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

}

UPDATE更新

I found a possible workaround for this.我找到了一个可能的解决方法。 Implement the DoCheck hook instead OnChanges.实现 DoCheck 钩子而不是 OnChanges。

ngDoCheck() {
   if(!this.inputSettings.equals(this.previousInputSettings)) {
       // inputSettings changed
       // some logic here to react to the change
       this.previousInputSettings = this.inputSettings;
   }
}

Remember that doCheck runs many times, and can cause performance issues if missused.请记住,doCheck 运行多次,如果误用可能会导致性能问题。

In general , your component will listen to all of it's object mutation, unless you use a different change detection strategy.通常,除非您使用不同的更改检测策略,否则您的组件将侦听它的所有对象突变。

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

This :这:

   changeDetection:ChangeDetectionStrategy.OnPush/Default/CheckOnce/Always

Will define how your component will behave when one of it's inputs gets updated.将定义您的组件在其输入之一更新时的行为方式。

Most important one is OnPush and Default .最重要的一个是 OnPush 和 Default 。

Where OnPush says update the component only if the whole object has got replace with a new one and Default says update me if any of the nested values has got updated( mutated). OnPush 表示仅当整个对象被新对象替换时才更新组件,而 Default 表示如果任何嵌套值已更新(变异),则更新我。

And, if you don't use that object inside your component, Angular will ignore and won't update the view ( why would it).而且,如果您不在组件中使用该对象,Angular 将忽略并且不会更新视图(为什么会这样)。

And then you can easily hook to ngOnChange life cycle hook and as the other answer suggest get the update.然后您可以轻松挂钩 ngOnChange 生命周期挂钩,并按照其他答案建议获取更新。

It's an alternative solution for DoCheck if you want to have more customization and do something more manual.如果您想进行更多自定义并执行更多手动操作,它是DoCheck的替代解决方案。 Of course, according to Günter Zöchbauer answer it's a kind of Observable solution.当然,根据Günter Zöchbauer 的回答,这是一种Observable解决方案。

I use a decorator to tell TS that property must be detected, so let's get start it.我使用装饰器告诉 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,
      });
    }
  };
}

Now, It's easy to use.现在,它很容易使用。 Just put @ChangeDetector() decorator on each property what you want to detect or define a changeDetector property to raise onChange event when a property changes.只需将@ChangeDetector()装饰器放在您想要检测的每个属性上,或者定义一个changeDetector属性以在属性更改时引发onChange事件。

Here is an example show you how to use it.这是一个示例,向您展示如何使用它。

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

Or in real Angular project:或者在真正的 Angular 项目中:

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

And for the test:对于测试:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM