Technology and Internet

Starting out with Meteor.js

For some time now, I’ve this urge to try to build something using meteor.js just for test purposes and to check what i can do with this framework that everybody is talking and writing about. So a few days ago i decided to give it a go, i installed meteor and I have put everything into place to start developing (and learning, while doing it of course).

The problem was that i was short in ideas for a small project, the ones that came to my mind were a bit too complex for my first app and specially for the time i wished to spend on it. After a while, the solution came in the form of an eureka moment and decided to build the most original piece of software ever, a To-Do List.

Ok, that wasn’t my brightest moment but at least i had something. To increase the difficulty of the task a little bit, i tried to build a simple clone of flask.io (without the good look), a tool that as Lifehacker says, lets you “create shareable To-Do lists on the fly, no account required”. Plus i added for each task a “progress feed” that lets you know what’s going on and what recently changed in it (good for shared tasks).

So in the next “few” lines i will describe the most important steps i took to build this thing (summed) and since this was a discovery project probably you will find some stuff that could be done in a better way. The git repository with the code can be found here.

To try it out, an instance of this application is running at rapido.ovalerio.net.

As first step we need to install the following meteor’s “smart packages” (all of them will be used throughout the project):

  • coffescript
  • backbone
  • accounts-ui
  • accounts-password

And remove some of the ones that came already installed:

  • insecure
  • autopublish

To achieve this we can use the following command:

meteor add/remove [package name]

The “coffee script” package is used here because i chose to build this project using coffee script instead of the regular javascript, the backbone package is needed because of the router and both account packages are just shortcuts that will handle the authentication.

Since we are not gonna let the user manage our database using the browser console, the insecure package must be removed. In the same way we don’t want to make everything in the database accessible to the client so we must remove “autopublish” too.

For the front-end i opted to try the Ink framework, this way i didn’t had to bother with all the CSS stuff. This leave us with just 2 files to edit one with the structure (HTML) and one with the logic (js/coffee). I could have divided the logic over different files and folders, separating the client stuff from the server but since this is a small app i think the methods Meteor.isClient and Meteor.isServer are good enough.

So, below the code that is common to both the client and the server:

Tasks = new Meteor.Collection("tasks")
Lists = new Meteor.Collection("lists")

Meteor.methods
  addTask: (newTask) ->
    newTask.comments[0].date = new Date()
    return Tasks.insert(newTask)
  addComment: (comment) ->
    user = Meteor.user()
    if user
      hash = md5 user.emails[0].address.trim().toLowerCase()
      new_com = {name:user.username, content:comment.content, pic_hash: hash, date:new Date()}
    else
      new_com = {name:"Anonymous", content:comment.content, pic_hash: "", date:new Date()}
    return Tasks.update({_id:comment.task}, {$push:{comments:new_com}}) 

...

In the first 2 lines, i declare the collections that will be in use in our Mongo Database, next I declare some methods that run on the server side but can be called from the client side and will used to modify the database content.

Next we have the logic that only the server can execute:

if Meteor.isServer
  Meteor.startup ->
    #604 800 000 - 7 days in miliseconds
    Meteor.setInterval ->
      now = new Date()
      limit = now.getTime() - 8035200000 # 3 months
      Tasks.remove({date:{$lt:limit}})
      Lists.remove({date:{$lt:limit}})
    , 604800000

  Accounts.config
    sendVerificationEmail: true,
    forbidClientAccountCreation: false

  Meteor.publish "tasks", () ->
    return Tasks.find {}

  Meteor.publish "lists", () ->
    return Lists.find {}

In the above block of code, we “declare” the content we want to publish to the client, we setup some items of the authentication smart packages, define one function that will run every 7 days to clean content that is 3 months old and that’s it for the server. Now lets look at the client (only logic, for a complete look, check the git repository).

if Meteor.isClient 

  Accounts.ui.config
    passwordSignupFields: 'USERNAME_AND_EMAIL'

  BackboneRouter = Backbone.Router.extend
    routes: {
      ":list_id": "main"
    },
    main: (list_id) ->
    ,
    setList: (list_id) -> 
      this.navigate list_id, true

  router = new BackboneRouter

In the previous lines we configure the user interface of the accounts-ui package to require an username and an email and setup the backbone router. The router plays an important role here since this is a single page application and each URL will be mapped to different list.

  Meteor.startup ->
    Backbone.history.start
      pushState: true
    Session.set "list_id", null
    if Backbone.history.fragment != ""
      Session.set "list_id", Backbone.history.fragment
    else
      Meteor.call "newList", "Set this ToDo List name.", (err, result) ->
        if err
          alert err
        Session.set "list_id", result
        router.setList result, true

    # Subscribed data from the server
    Meteor.subscribe "tasks"
    Meteor.subscribe "lists"

At startup, the client subscribes to the published data and the URL is parsed to check if a list must be fetched or if a new one must be created. In the second case the URL must be updated after the creation. Now below you will find the logic that covers the 3 main actions in this application, the rest is mainly the repetition of the same pattern.

  Template.list_tasks.all = ->
    return Tasks.find { list_id: Session.get("list_id")}, {sort:{ status:-1 ,date:-1}}

  Template.task_form.events
    'submit': (e, tmpl) ->
      e.preventDefault()
      user = Meteor.user()
      name = if user then user.username else "Anonymous"
      new_event = name + " created this tasks!"
      date = new Date()
      newTask =
        name: tmpl.find("#newtask").value,
        comments: [{name: "", content:new_event}],
        status: true,
        list_id: Session.get("list_id"),
        date: date.getTime()
      tmpl.find("#newtask").value = ""
      Meteor.call "addTask", newTask, (err, result) ->
        if err
          alert("Unable to save the task")

  Template.remove.events
    'click': ->
      Meteor.call "removeTask", this._id, (err, result) ->

The first item just fetches all tasks for a given list and passes the cursor to his template where the data will be rendered. In the second a submission event is associated with the “task_form” template and will handle the creation of new tasks. Finally in the last item a click is associated with a icon to remove a task (remove template).

And this is it, the above examples cover the most important concepts that i used to build the app, perhaps i didn’t use some functions or meteor concepts correctly but I’m just starting and i was able to achieve the intended functionality. It took a little more time to build than they advertised but overall it was quite fast.

If you have any doubts, want to point out some bug or suggest a better way to achieve the same result, feel free to post a comment bellow and I’ll reply as soon as possible.

Standard

One thought on “Starting out with Meteor.js

Comments are closed.