WoJ
WoJ

Reputation: 29967

How to build a dynamic CSS grid with Vue?

Background

I would like to build a Vue.js component which will be reused twice - the components being next to each other, governed by the CSS Grid (display: grid;). They will take an equal space on the width of the screen.

The components will themselves will be grids of rows, dynamically generated in Vue.

This is how this is supposed to look like:

enter image description here

Plain HTML/CSS solution (works fine)

The following code correctly generates what I am expecting: on the left three rows of time/content pairs, and on the right two such rows (Codepen version)

.grid {
  display: grid;
  grid-template-columns: 6ch auto;
}

#app {
  width: 400px;
  display: grid;
  grid-template-columns: 1fr 1fr;
}

.hour {
  text-align: right;
  padding: 3px 5px 3px 3px;
}

.name {
  text-align: left;
  padding: 3px 5px 3px 3px;
}
<div id="app">

  <div class="grid">
    <span class="hour">9:10</span> <span class="name">Acituf haih bim nizec ziohra uvi utucivsew koukeji fip kene ejviv wiel oj paphewan ilo newut.</span>
    <span class="hour">19:40</span> <span class="name">Macun dahhis cekdiwvug iti fieldu ab rewki pa ruoti abfonon cagoh codsi zadufi pu jurwut urmo owzew cawub.</span>
    <span class="hour">23:18</span> <span class="name">Zerba clegsns dgdrelm cevblrh.</span>
  </div>

  <div class="grid">
    <span class="hour">12:17</span> <span class="name">Hevkeek pe niwwat omkodo hapwas fepono azahawop ir bilbuel kame usipe cato icakori rosi sommawo.</span>
    <span class="hour">14:10</span> <span class="name">Hocmaizi weipe low rit todzup evtepcot zesaif unebojmug bageta ri fop sec ibti kukdo caukuocu fugocofo.</span>
  </div>

</div>

HTML/CSS/JS+Vue version (does not work as expected)

The same content is now generated through Vue upon the mount of the component. Please note that the result is different: the boxes are all mixed up.

I suppose that this is due to the fact that the content is appearing, then rendered and placed in the relevant places by CSS Grid, but then new content appears and this is not handled the way I expect by Grid (wildly speculating here)

(Codepen version)

new Vue({
  el: '#app',
  data: {
    els1: [],
    els2: []
  },
  mounted() {
    // content of the left box
    this.els1.push({
      first: '9:10',
      last: 'Acituf haih bim nizec ziohra uvi utucivsew koukeji fip kene ejviv wiel oj paphewan ilo newut.'
    })
    this.els1.push({
      first: '19:40',
      last: 'Macun dahhis cekdiwvug iti fieldu ab rewki pa ruoti abfonon cagoh codsi zadufi pu jurwut urmo owzew cawub.'
    })
    this.els1.push({
      first: '23:18',
      last: 'Zerba clegsns dgdrelm cevblrh.'
    })
    // content of the right box
    this.els2.push({
      first: '12:17',
      last: 'Hevkeek pe niwwat omkodo hapwas fepono azahawop ir bilbuel kame usipe cato icakori rosi sommawo.'
    })
    this.els2.push({
      first: '14:10',
      last: 'Hocmaizi weipe low rit todzup evtepcot zesaif unebojmug bageta ri fop sec ibti kukdo caukuocu fugocofo.'
    })
  }
})
.grid {
  display: grid;
  grid-template-columns: 6ch auto;
}

#app {
  width: 400px;
  display: grid;
  grid-template-columns: 1fr 1fr;
}

.hour {
  text-align: right;
  padding: 3px 5px 3px 3px;
}

.name {
  text-align: left;
  padding: 3px 5px 3px 3px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">

  <div class="grid" v-for="e1 in els1">
    <span class="hour">{{e1.first}}</span> <span class="name">{{e1.last}}</span>
  </div>

  <div class="grid" v-for="e2 in els2">
    <span class="hour">{{e2.first}}</span> <span class="name">{{e2.last}}</span>
  </div>

</div>

What is the correct way to instruct CSS Grid of such dynamic content?

Upvotes: 6

Views: 8604

Answers (1)

puelo
puelo

Reputation: 5977

You are repeating the grid instead of the content. You need to wrap the content into a <template> or <div>:

new Vue({
  el: '#app',
  data: {
    els1: [],
    els2: []
  },
  mounted() {
    // content of the left box
    this.els1.push({
      first: '9:10',
      last: 'Acituf haih bim nizec ziohra uvi utucivsew koukeji fip kene ejviv wiel oj paphewan ilo newut.'
    })
    this.els1.push({
      first: '19:40',
      last: 'Macun dahhis cekdiwvug iti fieldu ab rewki pa ruoti abfonon cagoh codsi zadufi pu jurwut urmo owzew cawub.'
    })
    this.els1.push({
      first: '23:18',
      last: 'Zerba clegsns dgdrelm cevblrh.'
    })
    // content of the right box
    this.els2.push({
      first: '12:17',
      last: 'Hevkeek pe niwwat omkodo hapwas fepono azahawop ir bilbuel kame usipe cato icakori rosi sommawo.'
    })
    this.els2.push({
      first: '14:10',
      last: 'Hocmaizi weipe low rit todzup evtepcot zesaif unebojmug bageta ri fop sec ibti kukdo caukuocu fugocofo.'
    })
  }
})
.grid {
  display: grid;
  grid-template-columns: 6ch auto;
}

#app {
  width: 400px;
  display: grid;
  grid-template-columns: 1fr 1fr;
}

.hour {
  text-align: right;
  padding: 3px 5px 3px 3px;
}

.name {
  text-align: left;
  padding: 3px 5px 3px 3px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>

<div id="app">
  
  <div class="grid">
    <template v-for="e1 in els1">
      <span class="hour">{{e1.first}}</span> <span class="name">{{e1.last}}</span>
    </template>
  </div>
  
  <div class="grid" >
    <template v-for="e2 in els2">
      <span class="hour">{{e2.first}}</span> <span class="name">{{e2.last}}</span>
    </template>
    
  </div>
 
</div>

Upvotes: 7

Related Questions