1

Using angular2-masonry, it will draw the bricks in the correct initial order (i.e. the order they appear in the array for the ngFor), but when a new array is obtained from a subscription and it has a new item (or even just a changed item), that new/changed item is always appended to the end and not placed in its correct position (the position it has in the array).

Array is obtained in a subscription like this:

    this.thingService.getThings().takeUntil(this.unsubscribe)
        .subscribe( (things:IThing[]) => {
            this.things = things;
            /*This should reload and resort the things, but it doesn't.*/
            if (this.masonry){
                this.debug("yes this is being called");
                this.masonry._msnry.reloadItems();
                this.masonry.layout();
            }
        });

This subscription works correctly, and if I output things they are as expected and in the correct order. Out of desperation I'm calling reloadItems and layout on masonry (obtained from @ViewChild(AngularMasonry) private masonry: AngularMasonry;). The calls succeed without error, but the items are still not drawn in the order they appear in the array.

Here's my template HTML:

    <masonry *ngIf="things && things.length"  [options]="{ }">
        <masonry-brick *ngFor="let thing of things" >
            Hi, my name is {{thing.name}}
        </masonry-brick>
    </masonry>

Again, to be clear - on initial load (driven by this same subscription) the items are in the correct order.

One additional note - if I change (or add) yet another item, the previous item that was erroneously appended to the end now is properly sorted in its correct location, and the new item is at the end (regardless of if it should be or not). So at any time, only one item is out of place.

WillyC
  • 3,917
  • 6
  • 35
  • 50

1 Answers1

0

I found that I can solve this problem by making this change to the masonry calls (specifically put them in a setTimeout call):

setTimeout( () => {
    if (this.masonry){
        this.masonry._msnry.reloadItems();
        this.masonry.layout();
    }
});

Note that both of those calls are required. layout on its own doesn't change anything and reloadItems will cause it to be sorted correctly eventually, but some event has to trigger a layout first.

I'm still interested to understand why. At the time I am calling reloadItems and layout, even without setTimeout, the array has been updated and the new contents should be immediately available for the reload.

...so I post this answer to help anyone else with the same problem, but it would still be great to understand what is actually happening here.

WillyC
  • 3,917
  • 6
  • 35
  • 50
  • 1
    View has not updated yet when you're running reloadItems. See http://stackoverflow.com/questions/43457149/angular-4-what-is-the-right-way-to-wait-for-operation – yurzui Apr 25 '17 at 21:13