Flexible tabs in Vue.js [Part 2]

In the first part of this tutorial I showed you how easy it is to create a tabbed menu that changes dynamically using Vue.js.

We created the tab data, we implemented the tab swapping functionality and it’s now time to see how to add new tabs, edit the name of the tabs we have added and removing tabs.

Add New Tab
Let’s start by adding a new tab.

As you can see, nothing really happens when we click on the button that’s supposed to add a new tab. Let’s change that.

We’ll begin by adding a new method in the methods object. We’ll call it addNewTab.


addNewTab: function() {
    let newId = this.tabs[this.tabs.length - 1].id + 1;
    this.tabs.push({
        id: newId,
        title: `Tab ${newId}`,
        content: {
            header: 'New Tab Header',
            content: 'New tab contents'
        },
        editMode: false
    });
    this.activateTab(this.tabs[this.tabs.length - 1]);
},

The addNewTab method is responsible for finding the id of the new tab. It does that by incrementing the id of the last tab in the array. Next the only thing we have to add is push a new tab object in the tabs array. We don’t need to perform any other action to update the view. The Vue.js engine will pick up the change and will automatically add a new tab for us. The call to activateTab will also cause navigation to the new tab. Remember that an important principle of UI/UX design is that your application should feel natural to the user and the actions the user has to take must be similar to actions in other applications. So when users use a browser, they know that when they’re opening a new empty tab they will directed to it. That’s what we have to do to in our application too in order to take advantage of the user’s memory.

We need one more step to make this work. We need to call the method when the user clicks on the add button


<button class="icon-btn" v-on:click="addNewTab()">
    <i class="fa fa-plus" aria-hidden="true"></i>
</button>

Edit / Rename Tab

Now when organizing your data in tabs you may need to rename them to something more meaningful. Especially if we’re talking about a new tab. There are a few ways in which we can trigger the renaming process. We could put one more icon next to the x icon. Like a pencil icon or something like that. A font awesome icon will serve this purpose.

 <li v-for="tab of tabs" class="nav-item">
    <a v-bind:class="{'nav-link d-flex align-items-center tab': true, 'active': (tab.id == activeTab.id) }" href="#" v-on:click="activateTab(tab)">
        <span class="mr-5" v-show="!tab.editMode">{{tab.title}}</span>
        <input class="form-control" type="text" v-show="tab.editMode" placeholder="Tab Name" v-model="tab.title">
        <button class="icon-btn">
            <i class="fa fa-pencil" aria-hidden="true"></i>
        </button>
        <button class="icon-btn">
            <i class="fa fa-times" aria-hidden="true"></i>
        </button>
    </a>
</li>

I’ve also added some margin to separate the tab’s title from the buttons and changed the CSS that styled the x button to also target the pencil:

.fa {
    opacity: 0.54;
    transition: color $color-transition-duration;
    
    &:hover {
        color: $x-hover-color;
    }
}  

(The target class used to be .fa-times)

We will also need a text box that will only be visible when our tab is in edit mode. So let’s create a text input right under the tab’s title that will only be visible when the ‘editMode’ property of the tab object exists and is set to true.

<input class="form-control" type="text" v-show="tab.editMode" placeholder="Tab Name" v-model="tab.title">

Now we have to add one more property to our tab objects, the editMode property that will be set to false by default:

this.tabs.push({
    id: newId,
    title: `Tab ${newId}`,
    content: {
        header: 'New Tab Header',
        content: 'New tab contents'
    },
    editMode: false
});

After adding the editMode property we will need a way to set it to true. So we can add a method called ‘editTabName’ to our view model. That method will accept a tab object as an argument and it will set its editMode property to true. That way, the text box will appear every time we click on the pencil button

editTabName: function(tab){
    tab.editMode = true;
}

And of course we need to call the method from our view on the ‘click’ event:

<button class="icon-btn" v-on:click="editTabName(tab)">
    <i class="fa fa-pencil" aria-hidden="true"></i>
</button>

The ‘editTabName’ method turns the edit mode on:

editTabName: function(tab){
    tab.editMode = true;
}

And we also need way to turn it off. That’s why we need an extra button that will replace the pencil when editMode is active:

<button class="icon-btn" v-show="!tab.editMode" v-on:click="editTabName(tab)">
    <i class="fa fa-pencil" aria-hidden="true"></i>
</button>
<button class="icon-btn" v-show="tab.editMode" v-on:click="acceptEdit(tab)">
    <i class="fa fa-check" aria-hidden="true"></i>
</button>

The ‘acceptEdit’ method will just turn the edit mode off. Everything else is taken care of by Vue. We do not need to get the text from the textbox since we’ve used model binding:

acceptEdit: function(tab) {
tab.editMode = false;
}

Deleting a tab

The deletion process is pretty simple. We ‘re just filtering out the deleted tab:

deleteTab: function(tab) {
    this.tabs = this.tabs.filter(t => t.id != tab.id);
}

Of course you have to make sure that you call the method when the delete button is clicked passing it the tab to delete.

Aaaand we have a fully functional tabbed menu with Vue.js. The code is open to modification that can make it adapt to your needs and it’s a nice place to start.

The complete JSFiddle: