Community Post

How to work with Bootstrap Tags Input in Laravel

Pham Tien Thanh

Bootstrap Tags Input is a JQuery plugin providing a Twitter Bootstrap user interface for managing tags.

Features

Bootstrap tags Input plugin provides the following features :

  • Objects as tags
  • True multi value
  • Typeahead
  • Designed for Bootstrap 2.3.2 and 3
  • Objects as tags

Example can be found at its own github sites : https://bootstrap-tagsinput.github.io/bootstrap-tagsinput/examples/

Typeahead support

Typeahead is not included in Bootstrap 3, so you'll have to include your own typeahead library. I'd recommed twitter typeahead.js and it can be downloaded from the following site :

Twitter Typeahead JS : https://twitter.github.io/typeahead.js/

Mix them up using Laravel as server side

In this case, let build function in a HRM (Human Resource Management) system which allow us to add employee's skill as input tags. What we are going to build look as just simple as the following screen:

Table definitions

First of all, let define the tables that we are going to use in this sample. We need a master table which hold the information about the skill that employee might have such as PHP, Java, and how each skill will be display. So the table might look like this :


class CreateMstSkillsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('mstSkills', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->string('display')->nullable();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
         Schema::drop('mstSkills');
    }
}

Route definition

To display suggestion when we start to type, We need a search API which will lookin into above master tables and filter the data and return the result based on the keyword provied. It is not looking so hard to do such athing

Route::group(['prefix'=>'api','middleware' => 'auth'], function(){
    Route::get('find', function(Illuminate\Http\Request $request){
        $keyword = $request->input('keyword');
        Log::info($keyword);
        $skills = DB::table('mstSkills')->where('name','like','%'.$keyword.'%')
                  ->select('mstSkills.id','mstSkills.name','mstSkills.display')
                  ->get();
        return json_encode($skills);
    })->name('api.skills');
});

That's all we need! Result is returned in json format.

Bootstrap Tag Inputs and Twitter Typeahead js

Include the plugins

This two library can be download from their website and we are going to included them into our blade

<!-- Bootstrap tags input -->
<script src="{{asset('/plugins/bootstrap-tagsinput/dist/bootstrap-tagsinput.min.js')}}"></script>
<!-- Type aheaed -->
<script src="{{ asset('/js/typeahead/dist/typeahead.bundle.min.js') }}" type="text/javascript"></script>
<script src="{{ asset('/js/typeahead/dist/bloodhound.min.js') }}" type="text/javascript"></script>

HTML part

A text input field is needed so that we can list up the employee' skills. So let take a look

<div class="tab-pane" id="TabCareerInfo" name="Career History">
    <div class="row">
        <div class="col-md-12">
            <label for="txtSkills">{{trans('pg_candidates.table.skills')}}</label>
            <input type="text" class="form-control" id="txtSkills" name = "Skills" data-role="tagsinput">                                   
        </div>
    </div>
</div>

Note that by defining the [data-role] of text input field is "tagsinput" we tell the tags input plugin to attach itself to this text field. At the moment without any other code, we can start to write tags into the text field and see the result :

Javascript part

I lose about 2 hours to find out that the initial JS of those 2 plugin should not be put in $(document).ready(). So I suggest that you should not lose another 02 stupid like me. What we need to do next is initialize Bloodhound so that it will use our defined route and return suggestion data based on what we type.

<script type="text/javascript">
// Get the reference to the input field
var elt = $('#txtSkills'); 

var skills = new Bloodhound({
      datumTokenizer: Bloodhound.tokenizers.obj.whitespace('id'),
      queryTokenizer: Bloodhound.tokenizers.whitespace,
      remote: {
            url: '{!!url("/")!!}' + '/api/find?keyword=%QUERY%',
            wildcard: '%QUERY%',                
      }
});
skills.initialize();

It is simple and the explaination is also written at Twitter TypeAHead.js web site which is quite enought. Let say we are going to fetch data directly from server using the given URL and search keyword is passed in QUERY.

The most important part is the part where we config TagsInput to work with TypeAhead :

$('#txtSkills').tagsinput({
      itemValue : 'id',
      itemText  : 'name',
      maxChars: 10,
      trimValue: true,
      allowDuplicates : false,   
      freeInput: false,
      focusClass: 'form-control',
      tagClass: function(item) {
          if(item.display)
             return 'label label-' + item.display;
          else
              return 'label label-default';

      },
      onTagExists: function(item, $tag) {
          $tag.hide().fadeIn();
      },
      typeaheadjs: [{
                hint: false,
                        highlight: true
                    },
                    {
                       name: 'skills',
                    itemValue: 'id',
                    displayKey: 'name',
                    source: skills.ttAdapter(),
                    templates: {
                        empty: [
                            '<ul class="list-group"><li class="list-group-item">Nothing found.</li></ul>'
                        ],
                        header: [
                            '<ul class="list-group">'
                        ],
                        suggestion: function (data) {
                            return '<li class="list-group-item">' + data.name + '</li>'
                        }
                    }
        }]         
});           

What we did tell the plugin are

  • itemValue : The name of the field whose value will be used as hidden value corresponding to each tag. In this case we use our id as itemValue.
  • itemText : The name of the field whose value will be used to display as name of tag. IN this case we used "name" (PHP, Java) as itemText.
  • tagClass : As we want to each tag has its own display, we use tagClass function. This function is called whenever a new tag is added and should return the CSS which is used to display the added tag. In our example, we use field master table's 'display' field to store the label style color of each tag.
  • As we don't want one tag is input twice, we set allowDuplicates to false and whenever a duplicate tag is added, onTagExists function is called. We do nothing but just blink the existed tags to get user's attention.
  • typeaheadjs part tell the configuration of typeahead so that the tag selected on suggestion will be added to input tags' text field.
    • Doing typeaheadjs in tagsinput's configuration is verymuch the same like we configure typeahead in standalone case. The structure of the configuration will be : typeaheadjs : [{options}, {configuration}]
    • As you see, we disabled the hint, and allow highlight the word we type as typeaheadjs's options
    • We then tell typeahead to use our defined Bloodhoud - skills as input of the suggestion display by indicate that [source: skills.ttAdapter()]. The name field's text will be used for display in suggestion and id field's value will be used as itemValue. We also customize the template to display the suggestion incase there is no valid match according to what we typed and to display the list of suggest.

So that is the complete example of showing how our plugin cooperate to gave us the result we want.

Hope this help! Neobk

Pham Tien Thanh

2 posts

Strategic Planning Manager @ CO-WELL Asia