desandro / masonry

:love_hotel: Cascading grid layout plugin
https://masonry.desandro.com
16.41k stars 2.11k forks source link

layout() does not work always on async/dynamic rendering #1089

Open mburger81 opened 5 years ago

mburger81 commented 5 years ago

Hello, we are creating a standard web component for your lib using new stenciljs compiler.

This means we use custom elements with shadow dom, css variables and dynamic width wrapping your lib.

But we have some problems on layout if the childs for items are changed

If we use the custom web component in an angular with ionic4 project (which is our main intend) we have something like this

       <lan-masonry>
                <lan-masonry-item
                        *ngFor="let widget of widgets; trackBy: widgetsTrackByFn"
                        size="12"
                        size-sm="6"
                        size-lg="1">
                        <widget />
                </lan-masonry-item>
            </lan-masonry>

As you can se we have a container called lan-masonry and a container for the items called lan-masonry-item which has a loop for n widgets which came from server async.

FYI: with the size attribute we "bootstrap" the components like a grid in bootstrap or ionic. :)

What we do in lan-masonry is listening for DOM changes with an observer this is our code

 private watchForHtmlChanges(): void {
    MutationObserver = this.win['MutationObserver'] || this.win['WebKitMutationObserver'];

    if (MutationObserver) {
      /** Watch for any changes to subtree */
      const observer = new MutationObserver((mutations: MutationRecord[]) => {
        console.log('mutations => ', mutations);

        mutations
          .forEach(
            (mutation: MutationRecord) => {
              this.masonry.addItems(mutation.addedNodes);
              this.masonry.remove(mutation.removedNodes);
            }
          );

        this.masonry.layout();

      });

      // define what element should be observed by the observer
      // and what types of mutations trigger the callback
      observer.observe(this.el, {
        subtree: true,
        childList: true
      });
    }
  }

as you can see we addItems and remove on every mutation, and at the end we ran masonry.layout() to style the dashboard again.

But this is not working as expected, on first initialization of our angular component the observer runs well but after layout() nothing is happening. If we add manually another element ot the widget lists anything is fine.

1) I'm not sure why this is happening. probably the new elements are not available at this moment? Which should not be. But if I run layout after 500ms it is working always fine. So what could be the problem? How does the system work? Any idea? I have tried also with appended, but on init *ngFor it is never working

our default options are { itemSelector: 'lan-masonry-item' }

2) on the other side, as you can see the observer has to listen also for subtree, not sure why this has to be? We are only interested on lan-masonry-item and not al his shadow sub elements.

Not sure where is the problem, having a look the the angular wrapper https://github.com/gethinoakes/ngx-masonry anyhting is fine. It is working great in angular. But also your web components are working great outside of angular, it means in plane JS.

Perhaps the DOM is not sync with the Observer?

mburger81 commented 5 years ago

I figured out that one issue using our web-component in an angular app is that the styles added for transitions for the "item"

    opacity: 1;
    transition-property: opacity, transform;
    transition-duration: 0.4s;
    transition-delay: 0ms;

are not removed from element.

mburger81 commented 5 years ago

It seems the problem could be also on stenciljs side, which does not compile right your lib. Still under investigation

desandro commented 5 years ago

I'm sorry to see you're having trouble with Masonry. I appreciate you discussing this issue in depth, but the best resource for me would be a reduced test case? See Submitting Issues in the contributing guidelines.

Venorcis commented 5 years ago

I figured out that one issue using our web-component in an angular app is that the styles added for transitions for the "item"

    opacity: 1;
    transition-property: opacity, transform;
    transition-duration: 0.4s;
    transition-delay: 0ms;

are not removed from element.

We have the same issue that under some circumstances, the transition properties are not removed from the first one or two items.

arildm commented 3 years ago

I figured out that one issue using our web-component in an angular app is that the styles added for transitions for the "item"

    opacity: 1;
    transition-property: opacity, transform;
    transition-duration: 0.4s;
    transition-delay: 0ms;

are not removed from element.

I had a similar problem with vue-masonry and somehow fixed it by setting transition-duration/transitionDuration to "0s".