Splitting Columns in Vue.js

Just a snippet which might be useful.

Layouts almost always call for multi-column layouts, for which we reach for the grid — currently the Zurb Foundation x-y (flexbox) grid which is great.

I am also now using Vue.js to provide a reactive ux — it is a truly brilliant piece of software.

This means I am getting data as json objects which often need dividing across the columns — for example a list of items which need to be displayed across three columns for medium and up sized screens.

To achieve this I am using as many columns as the design calls for — often three or four — and wanted a way to automatically divide the objects in my json across the columns.

v-for does a great job of iterating the items — but we need to feed it with data.

So I wrote a little function to split the list into as many columns as required, it takes two arguments — the first is the column and second is the number of columns — i.e. (1,3) is column one of three, (3,4) is column three of four etc, the method doesn’t care how many columns, just make sure you are consistent when using it on a block of data — (1,3), (2,3), (3,3) will work; (1,3), (2, 4) won’t — it will run but you will get repetitions across your items.

The data looks like this: (vue data — any array, normally pulled in from an api as json).

export default {
data() {
return {
items: [
{ name: 'Foo', id: 1 },
{ name: 'Bar', id: 2 },
],
};
}

The method looks like this: (vue method)

methods: {
filteredItems(column, columns) {
const self = this; // Enables us to pass this to the method
const total = this.items.length; // How many items
const gap = Math.ceil(total / columns); // How many per col
let top = (gap * column); // Top of the col
const bottom = ((top - gap) + 1); // Bottom of the col
top -= 1; // Adjust top back down one
return self.items.filter(item =>
self.items.indexOf(item) >= bottom
&& self.items.indexOf(item) <= top,
); // Return the items for the given col
},
},

The v-for looks like this:

<!-- First column (within a div with css classes for columns) -->
<li v-for="item in filteredItems(1, 3)" :key="item.id">
{{ item.name }}
</li>
<!-- Second column -->
<li v-for="item in filteredItems(2, 3)" :key="item.id">
{{ item.name }}
</li>

The method should work on any array. It will divide the data into (more or less) equal columns. I say more or less because you can get 4,4,2 for example when dividing ten across three columns. This was fine for me so I didn’t go the extra mile of adding an argument to specify how to weight the columns when the items don’t divide equally, though this would be perfectly doable.

There may well be better ways to achieve this result (I didn’t want to use CSS columns as the xy grid gives me better control) but this worked for me, happy to hear alternatives and suggested improvements!

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.