0

My angular is pretty basic. I have a view where I display an object (which is in a service):

<div *ngFor="let group of service.groups">
    {{group.id}} - {{group.name}}
</div>

The simple version of my service looks like this:

public pushNewGroup(group) {

    this.groups.push(group);
    console.log('Pushing new group!');

    // this.zone.run(() => {
    //     this.groups.push(group);
    // });

}

public addGroup(group: Group) {
    let component = this;
    this.api.postWrapper('groups', group).then(function (data: Group) {
        component.pushNewGroup(data);
    });
}

If I create a button to call the service, everything works and the view updates:

<input type="button" (click)="pushGroup()" value="Add group/>

With service call in the view component:

pushGroup() {
    let group = { ... }
    this.service.pushNewGroup(group);
}

But if I call the promise through a dialog window then it doesn't update the view:

<li (click)="addGroup()" ><a>Add Group</a></li>

Component (simplified):

addGroup() {
    let dialogRef = this.dialog.open(AddGroupComponent, {
        data: {
            ...
        },
    });
}

export class AddGroupComponent {

    constructor(private service: service) {}

    addGroup(group: Group) {
        this.service.addGroup(group);
    }
}

I've tried both ngZone and ChangeDetectionStrategy.Default. Both solutions did not work for me. I can see that the object changes both times and the console log also works. But the view does not update.


My @Componment doesn't have anything interesting. For the whole class it's:

@Component({
    templateUrl: './mainComponent.html',
    styleUrls: ['./mainComponent.scss'],
    providers: []
})

And for my add group dialog it is:

@Component({
    selector: 'addGroup',
    templateUrl: './AddGroupComponent.html',
    styleUrls: ['./AddGroupComponent.scss'],
    providers: [ApiService, PermissionsService, Service],
    changeDetection: ChangeDetectionStrategy.Default
})

EDIT: It's very hard to extract the whole thing into plunker, but I tried recreating it and turns out the promise thing works. I think the fault might be with MatDialog. It doesn't update the main view. I tried recreating the whole thing in plunker, but I can't get angular material working in plunker. And it seems like all other examples on the net are broken as well:

https://embed.plnkr.co/LTAEwV6bNvoGQAFoky60/

PadaKatel
  • 177
  • 2
  • 15
  • 1
    Can you show your `@Component` decorator? – Ashish Ranjan Jun 06 '18 at 15:25
  • Please create a JS Fiddle (http://jsfiddle.net/) so people can quickly and easily test your code. Also, in Angular 1.x you had to tell the view to update (`$scope.$apply()`) if something happened outside of the normal Angular pipeline, though I don't know if this is the same with 2.x. – harvzor Jun 06 '18 at 15:27
  • I tried creating a plunker here: https://embed.plnkr.co/LTAEwV6bNvoGQAFoky60/ Turns out, I think the problem is with the angular material dialog window (the data doesn't update after I close it). But I can't seem to get MatDialog working with plunker at this moment. – PadaKatel Jun 07 '18 at 13:45

2 Answers2

1

Why don't you use arrow function and forget this hack let component = this? Maybe this can be the problem. Use the ES6 features, follow the angular styleguide.

public addGroup(group: Group) {
  this.api.postWrapper('groups', group).then( (data: Group) => {
     this.pushNewGroup(data);
  });
}
Christian Benseler
  • 7,907
  • 8
  • 40
  • 71
  • Unfortunately, this does not change anything. I suspected that it might somehow be a different object (when I create the component), but that's why I extracted the actual pushing to a separate method. – PadaKatel Jun 06 '18 at 16:23
0

Promise will postpone the change detection to the next detection cycle.

Try this:

Add private _cdr: ChangeDetectorRef in the constructor.(obviously import ChangeDetectorRef)

Add this._cdr.detectChanges() inside promise (at the end).

Sanju
  • 1,478
  • 2
  • 20
  • 41
  • This gives me: ERROR Error: Uncaught (in promise): Error: ViewDestroyedError: Attempt to use a destroyed view: detectChanges I tried some solutions for it from here, but none of them worked unfortunately: https://stackoverflow.com/questions/37849453/attempt-to-use-a-destroyed-view-detectchanges – PadaKatel Jun 06 '18 at 16:16
  • It seems your actual component doesn't know there's a change in the service because you have triggered a change from a dialog component. You need to notify the parent component that a change has occurred. That can be done by emitting an event to the parent component. Can you post the simplified code in plunker or jsFiddle because it's difficult to get the context? – Sanju Jun 07 '18 at 07:54
  • I tried to create a plunker here: https://embed.plnkr.co/pvBT5jGddQP8IrYHGi79/ I used some existing code and tried to format to work like mine (it is really hard to extract it). Unfortunately the promise doesn't work right now, but if we could get that working maybe we could test it. – PadaKatel Jun 07 '18 at 09:38
  • Change your promise section to this to make it work on plunker: `this.http.get("https://randomapi.com/api/6de6abfedb24f889e0b5f675edc50deb?fmt=raw&sole") .subscribe(response => { response.json(); component.pushGroup(); });` – Sanju Jun 07 '18 at 10:53
  • Thanks. I got it working. And the whole promise thing works. I think the problem might be with my angular material dialog window. After I close it, it get's the data from the promise but the view doesn't update. I tried to create a plunker with MatDialog, but it seems all exmaples for it are broken with plunker (at the moment at least): https://embed.plnkr.co/LTAEwV6bNvoGQAFoky60/ – PadaKatel Jun 07 '18 at 13:44
  • If the problem persists, try emitting an event from the dialog component to the parent indicating the status of the operation. Then you can call _cdr.detectChanges() at that event. – Sanju Jun 08 '18 at 09:15