Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

vue-masonry-css makes first components loaded unreactive #9

Open
ghost opened this issue Mar 20, 2018 · 24 comments
Open

vue-masonry-css makes first components loaded unreactive #9

ghost opened this issue Mar 20, 2018 · 24 comments

Comments

@ghost
Copy link

ghost commented Mar 20, 2018

I'm facing a weird issue, the first components in the grid arent reactive anymore: see live

here is a previous production deployment , where everything works fine

馃

@paulcollett
Copy link
Owner

Could there be a conflict with the lazy-load/fade-in image plugin your using? Maybe theres a way to tell that plugin of the updated positions. Is it just the images or are you seeing other events breaking?

Nice showcase by the way!

@ghost
Copy link
Author

ghost commented Mar 20, 2018

@paulcollett: thanks for the quick response

I'm glad you like it. I'm launching it tomorrow on Product Hunt :)
I had to remove the grid feature in production unfortunately :(

Every events (@click...) and component methods (mounted()) are disabled for the first components.
Here is the component where everything happen. Let me know if you find anything suspicious, or if you need more code to investigate.

Page/index.vue

@mbarwick83
Copy link

I'm getting the exact same thing!

@mbarwick83
Copy link

@tchret what did you end up using?

@ghost
Copy link
Author

ghost commented Apr 10, 2018

@mbarwick83 did my own grid 馃ぁ

@paulcollett
Copy link
Owner

This seems reoccuring and something I'll look into.

@tchret Any tips on what you came up?

@justinkames
Copy link

Same issue here. When using other vue components inside masonry they become unresponsive.

@mateuswetah
Copy link

Hi there, just wanted to say the component is awesome and it's being incredibly useful and efficient with me. Unfortunately I also have this issue, trying to evoke a custom component @input event inside masonry. I'm also not sure of the best solution for so... Would there be any way to map any sort of event from masonry to it's child rendered components? Maybe using slots instead of creating the elements directly on template? I'm not sure. In any case, thanks for the simple solution!

@helariL
Copy link

helariL commented Sep 6, 2018

I'm quite late to the party and initial link is down already, but while using infinite-loading + progressive-img everything works like a char. Maybe something to consider?

@ghost
Copy link

ghost commented Sep 19, 2018

Hi,

I had such similar issue.
I have an infinite wall, I add items in masonry depending on the scroll => no problem
Sometimes I want to change all my data inside masonry => the data i changed, but the HTML of previously rendered components stays here

My workaround is to destroy masonry component before recreating it:

<masonry v-if="!killMasonry" :cols="5" :gutter="10">
        <div v-for="item of itemList.slice(0, maxNumber)">
          ...
        </div>
      </masonry>
...
data: () => ({
      maxNumber: 15,
      killMasonry: false,
    }),
    props: {
      itemList: Array
    },
    watch: {
      'itemList': {
        handler() {
          this.killMasonry = true;
          setTimeout(() => {
            this.killMasonry = false;
          }, 10);
        }
      }
    },
...

In my opinion, that's an heavy bug :(

@wouterfovel
Copy link

Have the same issue. Data changes in store do nor render to the screen. Kind of a deal braker for us ...
Other than this, the plugin is very cool ... vote +1 for a fix ...

@helariL
Copy link

helariL commented Sep 21, 2018

@wouterfovel and @thibaultbrocheton just use :key inside transition to force rerender of the component

<transition>  
    <masonry 
        :key="columnsCount" 
        :cols="{ default: columnsCount, 1000: 2, 500: 1}" 
    ></masonry>  
</transition>  

Referenced from -> https://vuejs.org/v2/api/#key

@wouterfovel
Copy link

@helariL

<masonry :cols="{default: 3, 1000: 2, 700: 1}">	
    <v-flex v-for="dimension in dimensions" :key="dimension.id">
    ... --- Code allowing a dimension.name to change ---

After we change a dimension's name, the db is updated and the vuex state mutates to these changes (state.dimensions mutates).
The adapted (state.)dimension.name is not altered on screen when <masonry ...> is used. If we remove <masonry ...>, it works fine ...
We are not adding new columns in our case ...

@mydnic
Copy link

mydnic commented Oct 27, 2018

Same error here :(
Actually all my components inside masonry are unresponsive. They trigger some stuff most of the time but the data doesn't update somehow... really weird

@BoKKeR
Copy link

BoKKeR commented Nov 4, 2018

Same issue, nothing in this thread has worked :(. I have to run a refresh after I load all the items and it's a deal breaker.

@sbine
Copy link

sbine commented Nov 7, 2018

We ran into the same issue and ended up using a Flexbox alternative, but first narrowed it down to a couple of problems:

  1. Any time the number of columns changes, contained elements lose reactivity
  2. When the component mounts, the reCalculate function is called twice: once with the default number of columns (2) and once with the number of columns you specified via props (in our case, 3). Since the number of columns has changed (2 -> 3), all contained elements lose reactivity.

Here's a simple Codepen demo illustrating the issues
Elements in the 3-column layout are never reactive. Elements in the Responsive layout are only reactive in their default 2-column layout (try resizing the window).

The 2nd issue is also related to this check which returns early when previousWindowWidth is the same as current window width.
Since reCalculate is called twice with different column values, _reCalculateColumnCount never gets called with the correct number of columns (3).
First recalculate:

  • displayColumns is 2
  • previousWindowWidth is undefined
  • _reCalculateColumnCount is called

Second recalculate:

  • displayColumns is 3
  • previousWindowWidth is the same as current window width
  • _reCalculateColumnCount is skipped

We were able to get around this by explicitly calling _reCalculateColumnCount and setting previousWindowWidth in the created hook instead of/in addition to mounted. This lets elements keep their reactivity as long as the number of columns is the same as when the component mounted.

As for the 1st issue, we think it has something to do with how Vue tracks key (maybe it's losing internal references to elements) but we switched gears before debugging any further.
Hope that helps.

@mbarwick83
Copy link

Wrap all child components in a div. ie:

<masonry>
    <div>
        <some-component>
    </div>
</masonry>

@mydnic
Copy link

mydnic commented Nov 8, 2018

Wrap all child components in a div. ie:

<masonry>
    <div>
        <some-component>
    </div>
</masonry>

That doesn't help. In @sbine codepen the components are already wrapped in div

@roberttolton
Copy link

Am also facing this problem, which is a deal-breaker :(
Inspecting with Vue DevTools shows an inconsistency with the DOM once the column counts are changed. For instance, something simple like a loading data property while false in Vue DevTools is effectively true on screen.

@tcober
Copy link

tcober commented Dec 11, 2018

I also am having this issue which is a real bummer because I really like this solution otherwise.

@mydnic
Copy link

mydnic commented Dec 12, 2018

@paulcollett you think you can find a solution?

@tcober
Copy link

tcober commented Dec 12, 2018

Hello everyone. So I think I found a solution to this problem for myself anyways. I had a pretty typical use case that is something like this:

<masonry :cols="{default: 2, 700: 1}" :gutter="30">
    <span v-for="number in [1,2,3,4,5,6,7]" :key="number">
        <basic-card />
    </span>
</masonry>

in my card component on re-size to 700px I would lose all my reactivity like everyone here. I read through @paulcollett 's script and saw that he was only grabbing the initial slot element for rerendering and had a sneaking suspicion that his script or Vue's render() function or something only cared about the initial child elements functionality, in this case that span tag. So I did an experiment like this:

<masonry :cols="{default: 2, 700: 1}" :gutter="30">
    <basic-card/>
    <basic-card/>
    <basic-card/>
    <basic-card/>
</masonry>

And sure enough on re-size my components kept their reactivity. SO, having done some work with Angular I wondered if there was some way to create that loop without nesting in another element like with ng-template and sure enough there is. You can nest template elements. So I tried something like this:

<masonry :cols="{default: 2, 700: 1}" :gutter="30">
    <template v-for="number in [1,2,3,4,5,6,7]">
        <basic-card :key="number"/>
    </template>
</masonry>

and sure enough, everything seems to be working! Hope this helps.

@roberttolton
Copy link

@tcober Seems like a useful workaround, however I'm also using a draggable component to my implementation, which means I can't get the required root-level structure :(

@ChibiChanNya
Copy link

ChibiChanNya commented Jan 8, 2019

I'm having issues when I wrap the elements in tags to give them entrance animations. The initially rendered ones won't animate and this causes the lazy-loaded images to not render unless I modify the array.

imagen

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests