You probably had that requirement to build tables where the user can Edit/Remove/Add items to it dynamically. With
jQuery that required handling a lot of events usually leading to bugs and wasted time. Turns out that with
Vue.Js, if modeled correctly, that's extremely easy. Let's take a look.
Adding Bootstrap 4
First, to make it look cool and save us some time prepping html, css and layout stuff, let's use
Bootstrap's
starter template to save us some dev time building the base html page.
Save that file and add Vue's dev version from the cdn on the bottom of the file with:
<!-- Vue development version, includes helpful console warnings -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
With Bootstrap and Vue loaded, let's do some code.
Building the Vue Instance
My
agenda class below will be responsible for rendering the dynamic table. Its syntax looks like this:
const agenda = new Vue({
el: '#agenda',
data: {
topics: [
new Topic({ title: 'Item 1' }),
new Topic({ title: 'Item 2' }),
new Topic({ title: 'Item 3' }),
]
},
computed: {
},
methods: {
add() {
this.topics.push(new Topic({ editing: true }))
},
save() {
alert("todo :: submit to server");
},
cancelEdits() {
this.topics.forEach(el => el.editing = false );
}
}
});
That represents our
Vue instance, the start point of our Vue app. See how simple it is? That's the beauty of Vue. Now let's revise our
Topic class. Our agenda app consists of a table of a list of topics in which each row is an instance of the
Topic class described below:
const Topic = function(model){
var self = this;
var m = model || {};
self.oTitle = m.title || "";
self.title = m.title;
self.required = m.required || false;
self.orequired = self.required;
self.editing = m.editing;
self.update = function(i){
if ((self.title || "").length < 3){
alert('At least 3 chars are required to save');
return;
}
agenda.topics[i].title = self.title;
self.editing = false;
}
self.edit = function(){
agenda.cancelEdits();
self.editing = true;
}
self.cancel = function(i){
if (!self.oTitle){
agenda.topics.splice(i,1);
return;
}
self.title = self.oTitle;
self.required = self.orequired;
self.editing = false;
}
self.remove = function(i){
if (confirm('Are you sure you want to remove this topic?')){
agenda.topics.splice(i,1);
}
}
return self;
}
Reviewing the HTML
The part of the HTML that deserves some comment is how we dynamically list the records using
Vue's v-for directive binding. The code below shows how this is accomplished elegantly using Vue:
<tr v-for="(t, i) in topics">
So, for each element in agenda.topics, Vue will render a tr for us with this piece of code:
<tr v-for="(t, i) in topics">
<th scope="row">{{ i + 1 }}</th>
<td>
<span v-if="t.editing">
<input v-model="t.title" @@keyup.enter="t.update(i)"/>
</span>
<span v-else>
{{ t.title }}
</span>
</td>
<td class="text-center">
<span v-if="t.editing">
<input type="radio" value="true" v-model="t.required"> Yes
<input type="radio" value="false" v-model="t.required"> No
</span>
<span v-else>
{{ t.required == "true" ? 'Yes' : 'No' }}
</span>
</td>
<td class="text-center">
<span v-if="t.editing">
<button class="btn btn-outline-info btn-sm" v-on:click="t.update(i)">Update</button>
<button class="btn btn-outline-danger btn-sm" v-on:click="t.cancel(i)">Cancel</button>
</span>
<span v-else>
<button class="btn btn-outline-info btn-sm" v-on:click="t.edit(i)">Edit</button>
<button class="btn btn-outline-danger btn-sm" v-on:click="t.remove(i)">Remove</button>
</span>
</td>
</tr>
The source code for this page is available on
my GitHub repo
Conclusion
The objective of this post is to demo how simple things become when using the right tools. Vue in my experience has matured and deserves a lot of praise in how it handles reactivity, simplifies, organizes and accelerates development.
See Also