Qtable, fail to make Masonry layout if I put iframe.....



  • I have followed the Masonry example in Flexbox pattern (https://quasar.dev/layout/grid/flexbox-patterns) . However, when I notice is, if I put iframe in the qCard, it will screw up the masonry layout.

    Original code:

    <template v-slot:item="props">
            <div class="q-pa-xs col-xs-12 col-sm-6 col-md-4">
              <q-card>
                <q-card-section class="text-center">
                  Address for 
                  <br>
                  <strong>{{ props.row.address }}</strong>
                </q-card-section>
                <q-separator />
                <q-card-section class="flex flex-center" >
                  <div>{{ props.row.description }} g</div>
                </q-card-section>
              </q-card>
            </div>
          </template>
    

    Screen Shot 2020-09-18 at 12.14.26 PM.png

    Now,… i add iframe or qvideo (which is also iframe) to the qcard:

    <template v-slot:item="props">
            <div class="q-pa-xs col-xs-12 col-sm-6 col-md-4">
              <q-card>
                <q-card-section class="text-center">
                  Address for
                  <br>
                  <strong>{{ props.row.address }}</strong>
                </q-card-section>
                <q-separator />
                <q-card-section class="flex flex-center" >
                  <div>{{ props.row.description }} g</div>
                  <q-video
                    src="https://www.youtube.com/embed/k3_tw44QsZQ?rel=0"
                  />
                </q-card-section>
              </q-card>
            </div>
          </template>
    

    Screen Shot 2020-09-18 at 12.17.26 PM.png

    now, it will just run on in the first row. I tried different things, like iframe with style, etc but nothing works.

    I am not sure why iFrame doesn’t work…



  • @murvinlai I am not sure it has anything to do with the iframes themselves. The biggest downside about the masonry layout is that you need to know the height of the container. It looks like you may not have updated the height to account for the taller cards. Can you try to increase the height first and see if it still breaks?



  • I have added:

    <template v-slot:item=“props”>
    <div class=“q-pa-xs col-xs-12 col-sm-6 col-md-4” style=“height:200px” >
    <q-card>

    and it works. wrapping around.
    However, if I use min-height it won’t work, I can set a fix height by px or % but it will cut off the long card. so, is there a way to dynamically determine & set the height?



  • @murvinlai Perhaps I wasn’t clear enough in my answer: the height must be set on the q-table itself, not the individual items. So, you’ll need to know beforehand the approximate heights of each card and calculate from there. It isn’t ideal, but that’s the only css-only approach. It’s possible to use JS to actually calculate and resize the q-table though, but it’s not a great UX.



  • Thanks… if I don’t use Qtable to do Masonry layout, is there a better way to do it without QTable? any insight? 🙂



  • @murvinlai What exactly do you want to accomplish? How many cards will you have? Will they change often? Will there always be the same amount of cards shown?



  • What I trying to get is to display a bunch of cards. I think I will start with a fix number of cards, e.g. 12 or 15 cards per page, with pagination. So, last page will most likely won’t be full. The cards are fixed after loading.

    However, if it is easy to just keep loading cards while scrolling down, it may work too.



  • @murvinlai If the average card size is the same, you can also just place them in a row of columns. Basic HTML structure is like this (not tested)

    <div class="row q-col-gutter-md">
    
      <div class="col-4">
        <div class="column q-gutter-y-md">
    
          <!-- Items in first column go here -->
          <div class="col-auto">
            <q-card>...</q-card>
          </div>
          <div class="col-auto">
            <q-card>...</q-card>
          </div>
          <!-- End of items in first column -->
    
        </div>
      </div>
    
    
      <div class="col-4">
        <div class="column q-gutter-y-md">
    
          <!-- Items in second column go here -->
          <div class="col-auto">
            <q-card>...</q-card>
          </div>
          <div class="col-auto">
            <q-card>...</q-card>
          </div>
          <!-- End of items in second column -->
    
        </div>
      </div>
    
    
    
      <div class="col-4">
        <div class="column q-gutter-y-md">
    
          <!-- Items in third column go here -->
          <div class="col-auto">
            <q-card>...</q-card>
          </div>
          <div class="col-auto">
            <q-card>...</q-card>
          </div>
          <!-- End of items in third column -->
    
        </div>
      </div>
    
    </div>
    

    To use dynamic data, you can use computed properties to separate the cards into columns:

    computed: {
      col1() {
        // slice the first four items, where this.items is your full list
        return this.items.slice(0, 4)
      },
      col2() {
        return this.items.slice(4, 8)
      },
      col3() {
        return this.items.slice(8, 12)
      },
    }
    

    And then:

    <div class="row q-col-gutter-md">
    
      <div class="col-4">
        <div class="column q-gutter-y-md">
          <div 
            v-for="items in col1"
            :key="item.uniq_id"
            class="col-auto"
            >
            <q-card>...</q-card>
          </div>
        </div>
      </div>
    
      <div class="col-4">
        <div class="column q-gutter-y-md">
          <div 
            v-for="items in col2"
            :key="item.uniq_id"
            class="col-auto"
            >
            <q-card>...</q-card>
          </div>
        </div>
      </div>
    
      <div class="col-4">
        <div class="column q-gutter-y-md">
          <div 
            v-for="items in col3"
            :key="item.uniq_id"
            class="col-auto"
            >
            <q-card>...</q-card>
          </div>
        </div>
      </div>
    
    </div>
    

    This should work okay if you don’t use infinite scrolling. If you do, you’ll need a bit more logic in the computed properties where you slice the main items array, i.e. for col1 items, it would be something like:

      col1() {
        // slice the first four items
        return [
          ...this.items.slice(0, 4),
          ...this.items.slice(12, 16),
        ]
      },
    

    (of course you can abstract that a bit and not have to hardcode it.) However, since this method won’t auto-balance different height cards, the infinite scroll will get pretty uneven columns at some point where one column is much taller than another one.

    You can apply more logic on top of this if you do know the height of each card, but that can get pretty tricky to implement. That being said, also take a look at https://infinite-scroll.com/#masonry-isotope-packery . I haven’t ever integrated that into Vue, so not sure how nicely it will play together.

    Edit: a better computed function is actually like this (it just dawned on me 😁 )

    computed: {
      cols() {
        const cols = [ [], [], [] ]
        for(let i = 0; i < this.items.length; i++) {
          cols[i%3].push(this.items[i])
        }
        return cols
      },
    }
    

    and then using v-for="items in cols[0], v-for="items in cols[1], and v-for="items in cols[2]. That should play nicely with the last page where you won’t have all 12 or whatever amount of items I assumed in the original post.



  • Thank you so much @beets I will try it out and also look into the infinite-scroll website. 🙂



  • i played around with the code… if I set the qtable height larger, e.g. 300vh, then I don’t have to define the height of qcard. so each qcard can have different height. i believe the qtable height ( or the container height) has to be tall enough to hold all qcard if stack vertically in one column.

    see here: https://codepen.io/pen/?template=NWNEgVW



  • @murvinlai Yes, the height must be set on the qtable if you’re going that route. The height doesn’t need to be equal to all of them stacked in one column, but the height of the tallest of all the columns. With dynamic content this gets tricky though. Also keep in mind when you resize the viewport, text will reflow and possibly change the height of each card.



  • This is very true. 🙂 thanks for the advice.


Log in to reply