Today I had to render 200+ items in a bootstrap table which didn’t appear to be user-friendly. So I planned to show first 20 of them on load, and if the user wanted, show full items.
To do that, my Vue app code looks like this,
var app = new Vue({
el: "#app",
data: {
countries: [],
isLoadingCountries: false,
showLessCountries: true,
},
methods: {
getCountries: function () {
app.isLoadingCountries = true;
app.$http
.get("some-url")
.then(function (response) {
if (response.data) {
if (response.data.isSuccess) {
app.countries = response.data.countries;
}
}
})
.then(function () {
app.isLoadingCountries = false;
});
},
},
created: function () {
this.getCountries();
},
});
Basically, it contains an array, one flag to indicate the data fetch operation is ongoing or not, one flag to indicate whether the full list is showing or fewer items, and a function to fill the data in the array. I hope the naming convention in the code is pretty self-explanatory.
And my markup,
<table class="table" v-if="!isLoadingCountries">
<thead>
<tr>
<th scope="col">Country</th>
</tr>
</thead>
<tbody v-if="showLessCountries">
<tr v-for="country in countries.slice(0, 20)">
<td><h5>{{country.name}}</h5></td>
</tr>
</tbody>
<tbody v-else>
<tr v-for="country in countries">
<td><h5>{{country.name}}</h5></td>
</tr>
</tbody>
</table>
<button
v-if="!isLoadingCountries"
@@click="showLessCountries = !showLessCountries"
>
{{showLessCountries===true? "Show All Countries" : "Show Less"}}
</button>
Here, Using Array.slice(), we get a new array with the limited number of items from the original array. And we show the new sliced array items or the original array full items based on the flag. And a button to toggle the flag. If you wonder why I used **@@**click in Vue, is because my markup was written in Razor (.cshtml).
Update 1 - Sep 6, 2018
As Andrew Butler pointed out in the comments, we can avoid the markup duplication by using a computed property. And the code becomes,
var app = new Vue({
el: "#app",
data: {
countries: [],
isLoadingCountries: false,
showLessCountries: true,
},
computed: {
countriesToDisplay: function () {
if (this.showLessCountries) {
return this.countries.slice(0, 10);
} else {
return this.countries;
}
},
},
methods: {
getCountries: function () {
app.isLoadingCountries = true;
app.$http
.get("some-url")
.then(function (response) {
if (response.data) {
if (response.data.isSuccess) {
app.countries = response.data.countries;
}
}
})
.then(function () {
app.isLoadingCountries = false;
});
},
},
created: function () {
this.getCountries();
},
});
And the markup,
<table class="table" v-if="!isLoadingCountries">
<thead>
<tr>
<th scope="col">Country</th>
</tr>
</thead>
<tbody>
<tr v-for="country in countriesToDisplay">
<td>
<h5>{{country.name}}</h5>
</td>
</tr>
</tbody>
</table>
<button
v-if="!isLoadingCountries"
@@click="showLessCountries = !showLessCountries"
>
{{showLessCountries===true? "Show All Countries" : "Show Less"}}
</button>
If you know a better way to do the same, let me know.