2

I have a form for which I've got a js object as model. I want to detect a change in the object, to inform the user of the need to save.

To achieve this, I currently using KeyValueDiffers. It does also detect every change, as wanted. Although I have an unwanted effect.

The form is an own component and I'll set the object to modify over an @Input() which calls a init() method that also initializes the KeyValueDiffers with the new object.

After initialize the KeyValueDiffers I immediately get a change notification, although there was no change to the object that I used to initialize the differ yet. Why could this be? I'm clueless...

export class MetadataDetailComponent implements DoCheck {

    constructor(private differs: KeyValueDiffers) { }

    unsavedChanges: boolean = false;
    differ: any;
    metadata: Metadata;
    orgMetadata: Metadata;

    @Input()
    set setMetadata(metadata: Metadata) {
        this.init(metadata);
    }

    private init(metadata: Metadata) {
        this.differ = undefined;
        if (this.metadata && this.unsavedChanges) {
            console.log('unsaved changes!!')

        }
        //Do different init stuff
        this.orgMetadata = metadata;
        this.metadata = new Metadata(metadata.name, metadata.description, metadata.id, metadata.state, metadata.type, metadata.script);
        this.differ = this.differs.find(this.orgMetadata).create(null);
        this.unsavedChanges = false;
    }

    ngDoCheck() {
        if (this.differ) {
            var changes = this.differ.diff(this.metadata);
            if (changes) {
                this.unsavedChanges = true;
                console.log('metadata has changed!');
            }
        }
    }
}

EDIT

This is the change event I'm getting enter image description here

It seems like this is the change from null to the object. But why am I getting this change when I'm already initializing the KeyValueDiffers with the object?

Herr Derb
  • 4,977
  • 5
  • 34
  • 62
  • What's the content of the change. The first change might be from `null` to the object instance you pass. – Günter Zöchbauer Oct 04 '16 at 11:12
  • @GünterZöchbauer I actually looks like this. But as I understand I initialize the `KeyValueDiffers` already with the object, so this change shouldn't happen? – Herr Derb Oct 05 '16 at 07:45
  • Are you sure `this.orgMetadata` has a value already? In your code use use `this.metadata` instead in the rest of the code. – Günter Zöchbauer Oct 05 '16 at 07:50
  • @GünterZöchbauer Sorry, I posted incomplete code. I just edited that. That's how it looks. So `this.metadata` and `this.orgMetadata` should be the same in the beginning – Herr Derb Oct 05 '16 at 07:55
  • You pass it using an `@Input()`. Are you sure the first time a value is passed it is different from `null`? – Günter Zöchbauer Oct 05 '16 at 07:59
  • To avoid a ambiguous state while recieving a new metadata object over `@Input()` , I put `differ` to `undefined` in the beginning of the `init()` method and initialize it again after `orgMetadata` and `metadata` is set again. As I understand, this should avoid that problem – Herr Derb Oct 05 '16 at 08:05
  • Seems I misinterpreted your code. I think I understand now. But I have no idea how to fix. – Günter Zöchbauer Oct 05 '16 at 08:11
  • 1
    @GünterZöchbauer Thanks for your time anyway. I'll try to make a plunker when I find the time. – Herr Derb Oct 05 '16 at 08:41

1 Answers1

1

I don't know if this solves your specific problem. But we had issues with KeyValueDiffer having "null" as the previous value on the first diff after things get going after ngOnInit. I've combed the internet and found no real answer on this, so we "solved" it by initializing the differ like so

this.differ = this.differs.find(this.orgMetadata).create().diff(this.orgMetadata);

or perhaps for your specific case

this.differ = this.differs.find(this.orgMetadata).create().diff(this.metadata);

Note that create doesn't take an argument anymore in Angular 6+

  • This really confuses me as it seems like without call .diff this whole thing is useless and the argument in .find does nothing. – Craig Apr 10 '19 at 17:26
  • I completely agree. If you want to hunt down a google employee and ask why on earth differs be the way they be then I'll come with you. – Malachi Tolman Apr 17 '19 at 19:07
  • I looked into the code on github and it looks like the find argument is just used as a quick test to see if that argument is of a type the differ wants. so you can give it {} as a one off in the find() and it will work the same. find should really be named findFactorySupporting – Craig Apr 18 '19 at 07:57
  • Awesome workaround!!! Thank you! Calling.diff during assignment to "initialize" works for IterableDiffers too :-) – sarora Mar 24 '21 at 20:28