Drunken Coder

Bootstrap tagging

Posted 11/11/2013

I had a need for a mongodb-driven tagging widget. The idea is that you get nice tags that you can add & remove, and it knows about tags that are already present.

First, get some cheap beer, and a bottle of Amaretto. Alternate swigs, and read on.

I was haing a lot of trouble with various parts of this. I think I just needed a bit o’ gangster-rap & Amaretto. Maybe with my help your roll will be smooth-sails.

Ok, so first I have a cute lil express.js app. I will detail the entire process.

Run npm init to create a new node package.json. Just answer the questions.

Run npm install --save express && npm install --save mongoose to add express & mongoose to your project.

You can head over to mongolab to get a free mongodb, if you don’t want to host it yourself.

make your server.js look like this:

var express = require('express'),
    app = express(),
    mongoose = require('mongoose'),
    MyModel = require('./models/MyModel');


app.configure(function() {
    app.use("/", express.static('public'));

    app.use(express.logger('\x1b[33m:method\x1b[0m \x1b[32m:url\x1b[0m :response-time'));
        dumpExceptions: true,
        showStack: true

// get a list of available tags
app.get('/d/tags', function(req, res){
    // you can put strings in here to preseed tags. slick!
    MyModel.find().distinct('tags', function(er, d){
    var out = ["hipster trash", "euro trash", "foolish whim", "good idea"];
        if (er) return res.send(500, er);

var port = require('./config.json').port || 8000;
console.log('come see us at' + port);


all the app.configure stuff is me getting fancy. Totally optional if you are running a static webserver somewhere else.

If you don’t need to preseed your auto-complete list, make your callback look like this fancy (simple) bee-yute:

// get a list of available tags
app.get('/d/tags', function(req, res){
    MyModel.find().distinct('tags', function(er, d){
        if (er) return res.send(500, er);

There is also a config file called config.json:

    "database": "mongodb://<dbuser>:<dbpassword>@<dbhost>:<dbport>/<dbname>"

Make sure to add that to .gitignore. Alternately, you can just fill in the mongo URI, above.

MyModel is an example model. Make sure it has a “tags” string-array field. Let’s make a lil cutie in models/MyModel.js that looks kinda like this:

var mongoose = require('mongoose');

var MyModel = new mongoose.Schema({
    "name": String,
    "description": String,
    "cool": Boolean,
    "tags": [String],

    "hipster.cool": Boolean,
    "hipster.ironic": Boolean,
    "hipster.poser": Boolean

module.exports = mongoose.model('MyModel', MyModel);

As a sidenote, I realized how to make models in mongoose have a nice Object structure, without pseudo-sub documents (extra ids & overhead.) All you gotta do is name fields things like thing.field. super-cool!

Make a lil dev script to insert a bunch of records, mine is silly, feel free to tailor to your own sweet-sweet model.

I am using my awesome randopeep lib. Install with npm install --save-dev randopeep

let’s call this file genenerate-hipsters.js and run it with node genenerate-hipsters.js

var mongoose = require('mongoose'),
    randopeep = require('randopeep');
    MyModel = require('./models/MyModel');


for (i=0; i<100; i++){
    var rec = {
        "name": randopeep.corporate.name('cyber'),
        "description": randopeep.corporate.catchPhrase(),
        "cool": Math.random() >= 0.5,
        "tags": [],
            "cool": Math.random() >= 0.5,
            "ironic": Math.random() >= 0.5,
            "poser": Math.random() >= 0.5

    rec.tags = rec.tags.concat(randopeep.invention(25));
    rec.tags = rec.tags.concat(randopeep.job(25));

    var s = new MyModel(rec);

This will generate 100 nice random MyModels, with 50 tags each.

OK! now, for the client-side code, put this in public/index.html like a champ:

<link rel="stylesheet" href="http://netdna.bootstrapcdn.com/bootstrap/3.0.2/css/bootstrap.min.css">
<link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/tagmanager/3.0.0/tagmanager.css">
<link rel="stylesheet" href="https://raw.github.com/jharding/typeahead.js-bootstrap.css/master/typeahead.js-bootstrap.css">

<script src="http://codeorigin.jquery.com/jquery-1.10.2.min.js"></script>
<script src="http://netdna.bootstrapcdn.com/bootstrap/3.0.2/js/bootstrap.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/typeahead.js/0.9.3/typeahead.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/tagmanager/3.0.0/tagmanager.js"></script>

<div class="container">
    <form class="form-horizontal" role="form">
      <div class="form-group">
        <label for="tags">Tags:</label>
        <input type="text" class="form-control" id="tags">

var tagger = $('#tags').tagsManager();
    prefetch: '/d/tags'
}).on('typeahead:selected', function (e, d) {
    tagger.tagsManager("pushTag", d.value);

Run npm start and you will have a running webserver & JSON interface to your db’s tags.

Now, yer all set. It still needs a little style love, but basically does what is needed. Pretty extreme level of magic for such a small investment, eh?


Staging Server with Dokku

Here is how I setup my staging server to show clients progress.

David KonsumerWritten by David Konsumer who lives and works in Portland and makes rad stuff. You should follow him on Twitter & Github