To-Do Chrome Extension Part 2 – Browser Action Popup

This is part two of the series on making a to-do extension for Google Chrome.

In this post I will cover how to implement a good database layer for our extension, as well as how to use the jsTemplate library to display our data nicely. Fairly simple right? Let’s get to work.

We will begin with writing the necessary HTML markup for displaying the tasks. To make it easier to display data from JavaScript in HTML we will be using a template system called jsTemplate, created by Google. It will allow us to separate the presentation from logic, which is a good idea.

Update: Today Knockout is a better library for working with presentation in JS. Chrome has also tighten the security policy preventing inline JS.

Let’s add the jsTemplate library to our popup.html file. There are three files needed to run the library, but to save some file requests I joined them to one file. You can find this file in the ZIP-archive at the bottom of this post. Extract the file in the /js/libs/ folder and add the following line to the head section:

<script src="js/libs/jstemplate.js" type="text/javascript"></script>

We need to define the properties of a task – a task will have a descriptive title, a boolean for whether the task is completed, and lastly a date of creation. Translating this to HTML markup means we need a checkbox, text element and a remove button for each task item. The date of creation will not be displayed in my version, but it is always good idea to include it incase you need it later. The template for a task item will look like the snippet below.

<div id="results">
    <div class="item" jsselect="$this" jsvalues="id:id">
        <input type="checkbox" class="completed" checked="checked" jsvalues="checked:completed" /> 
        <span class="title" contenteditable="true" jscontent="title"></span>    
        <button class="remove" title="Remove Task">Remove</button>
    </div>
</div>

As you have probably already seen—there are a number of weird, non-standard attributes in the code. These are used by jsTemplate to select properties from the data we pass in. The jsselect attribute means we will iterate over the variable specified in it—in this case the $this variable. The jsvalues attribute can change properties of the element, and jscontent sets the text of the element. Now there is one more attribute that is not very commonly used in HTML, which is the contenteditable attribute. It makes it possible to edit the content even though it is not an input or textarea element, which is perfect for this specific use-case (we can style it just like we want to).

We will also need a text box for entering a new task, which will look like this:

<input type="text" id="add" placeholder="Write task and press enter..." />

Just a normal input element, but with the new HTML 5 attribute called placeholder. It shows the text in gray and when clicking the text box it disappears. Very cool feature, that previously required lots of scripts and styling.

Data storage in extensions

If you load the extension in Chrome it won’t really do much at all – even though we have made a template and included the necessary scripts. What we need to do is bind the data storage to the template system so that it has some data to play with. To store data in Chrome we can use two different systems, Local Storage or Web Database. The most common and easiest one is Local Storage and is also the one we will use here. It is simply an object that we can set properties on and they are automatically saved for us, even after browser restarts.

Remember that localStorage can only store strings, which means you have to store your objects in a JSON-string with JSON.stringify(object)

Before we get ahead of ourselves and hook up the template system, we will create a class for handling data storage. This will make it a lot easier to get, add and remove tasks from the storage.

var TaskRepository = function() {
    this.init();
    return this;
};

TaskRepository.prototype = {
    tasks: [],
    index: 0,

    init: function() {
        this.load();
    },

    create: function(task) {
        if (task == null) throw new TypeError('Missing task parameter');

        task.id = this.index++;

        this.tasks.push(task);
        this.save();
    },

    // 'get' and 'remove' functions removed for brevity,
    // see ZIP-archive for full code sample.

    load: function() {
        this.tasks = (localStorage.tasks ? JSON.parse(localStorage.tasks) : []);
        this.index = localStorage.index ? localStorage.index : 0;
    },

    save: function() {
        localStorage.tasks = this.tasks ? JSON.stringify(this.tasks) : null;
        localStorage.index = this.index;
    }
};

That is quite a bit of code but quite simple at a closer look. Basically what it does is load data from localStorage in init() and puts it in a local variable, tasks, and each method uses that variable to query data. After editing data you will have to save the data back to localStorage with the save method. An index number variable is also increased every time you create a task – it is used as ID which makes it easier to get and remove a specific task.

Hook up the template system and display some data

We are now ready to display some data in our browser action popup. To do that we create a View class which will include all presentation logics like load, edit and remove. We initialize this class in the onload event (and of course, in the popup.html file).

var View = function() { return this; }
View.prototype = {
    init: function() {
        // Manually create a task
        db.create({
            title: 'My first task is to make a task',
            completed: false,
            created: new Date()
        });
        
        this.renderView();
    },

    renderView: function() {
        // Tells jsTemplate to load our tasks and display them with our template
        jstProcess(new JsEvalContext(db.tasks), document.getElementById('results'));
    }
};

var db = new TaskRepository();
var view = new View();

window.onload = function() { view.init(); };

Round up

task If you reload the extension in Chrome you should see one task appearing in the list, and you should also notice that the layout is worse than the web was ‘99 (see image). There is no way to add a task either, nor remove or edit yet. In the next post we will fix all these points.

Download the Source

Download To-Do Extension Source (Part 2)

published in Chrome Extensions