Tagging is an important part of grouping information on the web. On Medium you tag your posts with categories. On Facebook you tag your photos. In Gmail you can tag your messages with labels. On GitHub you can add tags to your repositories. In this tutorial we’re going to go through how I used prebuilt Laravel and Vue.js packages to build a tagging system for candidates on Employbl.com.

If you haven’t already, feel free to take this functionality for a test drive by applying to join the Employbl network.

The end result looks something like this:

The update profile page for candidates on Employbl.

I wanted to add tags to the candidate profile form so that employers can search and filter for relevant talent in their area. For the tags, I wanted a pre-populated list of available tags so that candidates with similar tags use the same tag. I didn’t want candidates adding new tags like “Node” and “Nodejs” and “Node.js” that all mean the same thing.

Let’s start with building the frontend, what the user actually sees.

🔖 The frontend

Vue-multiselect is built by Damian Dulisz, a member of the Vue.js core team. It has nearly 4,000 🌟stars on GitHub. The support of a core team member, friendly documentation website and GitHub stars all led me to choose this library over the awesome looking vue-tags-input.

Once I’d chosen a library it was important to have the component be part of a form that I could submit to the backend. Normally I’d use Blade to build a form that would look something like this:

app/resources/views/mytemplate.blade.php <form method="POST" action="{{ route(task.create) }}">

@csrf <input type="text" class="form-control" name="title">

<input type="submit" value="Submit">

</form>

This is nice, but it doesn’t work when your form needs Javascript functionality. For example see if this code makes any sense:

app/resources/views/mytemplate.blade.php <form method="POST" action="{{ route(task.create) }}">

@csrf <input type="text" class="form-control" name="title">

<multiselect v-model="value" :options="options" :searchable="false" :close-on-select="false" :show-labels="false" placeholder="Add tags for this task"></multiselect> <input type="submit" value="Submit">

</form>

This is ugly and would break. It’s best practice to extract the whole form out of Blade into a Vue component. I found the below blog post by Mark Scherrenberg very helpful.

In that post he extracts form logic into a handy Vue.js mixin. I didn’t go that far but if I was making lots of forms or had other developers to work with it’s certainly something to think about.

Instead my blade component looks like this:

@extends('layouts.app')



@section('content')

<br>

<br>

<br>

<div class="row">

<div class="col-sm-6 offset-sm-2">

<h3>✊ Complete your profile</h3>



<span class="text-muted">This will only be shown to legit companies. We don't mess with headhunters, agency recruiters or third parties.</span>

<hr>

</div>

</div>

<candidate-profile-form candidate="{{ $candidate }}" tags="{{ $allTags }}" candidate-tags="{{ $candidateTags }}"></candidate-profile-form>

In the above code we’re rendering a blade template that renders a Vue component. We pass in data from the server using Blade syntax and Vue props. The data will be available to the component as a string. To make sure it’s usable we’ll call JSON.parse on the props value.

The <candidate-profile-form> component looks something like this:

Within the data component function we parse the JS objects that we passed in via blade. The list of available tags candidates can select from, the error messages and the candidate’s current info will come from the Laravel backend.

Axios and CSRF protection is available to our Vue component through the default bootstrap.js file that comes with Laravel 5.7.

🚀 The backend

For adding tags to the backend we’re going to use a package by the Spatie web dev agency. Lead maintainers being: Freek Van der Herten, Alex Vanderbist and drbyte. Thank you! 🙏

There are two controller methods. Edit if for displaying the blade form and update is for the PUT request when candidate clicks “Save”. The second file is a seeder that creates all of the available tags that candidates can select from.

I’m storing candidates in the users table.

There’s some funky stuff going on here with eager loading and parsing out the tags when rendering them back to the form. It’s so we only display the tag name instead of the tag object. The aim of doing this on the backend is to keep our Vue component as simple as possible.