Getting chatty with Meteor.js

I promise you, that after using Meteor, going back to anything else will feel slow. It’s the unfortunate side effect of the “Meteor Workflow™”.

Meteor is a full-stack application platform which lets you write everything with JavaScript, because y’know…yay JavaScript.

For more of the actual features, please visit Meteor.com.

My aim, by the end of this article, is for you to be able to build, not a Hello World app, not a Todo app, but drumroll …a Realtime Chat App. Cool, huh!?

If I’ve already lost you to boredom, then have a play around with the finished demo here:

Chatt

Or mess around with the code here on GitHub

Aaaaand you’re back, awesome, lets get you building it.

First step, let’s get Meteor on your machine.

Installing Meteor:

Fire up Terminal, and type:
$ curl https://install.meteor.com | /bin/sh

Create a project:
$ meteor create myapp

Run it locally:
$ cd myapp
$ meteor
=> Meteor server running on: http://localhost:3000/

Boom, installed and running, quick pat on the back and lets move on.

You should now be looking at 3 files: myapp.js, myapp.html & myapp.css. Neato huh?

Templates

So, firstly the HTML. We break our UI up into logical components. With this chat app, we only really need two sections; an area to input your messages and an area to view everyone’s messages. We then create templates for those two areas; one called “input” and one called “messages”.

They look a little bit like this:

<template name="input">
    <div id="input" class="well">
        <strong>Your message</strong>
        <textarea rows="3" id="newMessage"></textarea>
        <input type="text" placeholder="Your name..." id="username" />
        <a id="send">Send</a>
    </div>
</template>

<template name="messages">
    <div id="messages">
        {{#each messages}}
            {{> message}}
        {{/each}}
    </div>
</template>

<template name="message">
    <blockquote>
        <p>{{message}}</p>
        <small>{{username}}</small>
    </blockquote>
</template>

So, I kinda lied to you (hope I haven’t lost your trust), but we actually end up with 3 templates. We could have easily just left the HTML for a single messages within the {{#each messages}}{{/each}}, but, for neatness and organisation, we’ve split it up.

So, from the above templates, the only thing that you might be wondering, is where does messages come from, what the heck are we looping around?

The JavaScripts

Let’s move onto the JavaScript.

A Meteor app is split into two parts; Server and Client. Any JavaScript files that you put into the _server folder isn’t piped down to the browser. For organisation, you can, if you want, put all your other JavaScript files into a _client folder. However feel free to leave them in the root folder of your application.

The first line in our JavaScript file is Messages = new Meteor.Collection("Messages");, this tells Meteor to grab the Messages MongoDB collection or create a fresh one.

MongoDB

Going back to the loop in our Messages template, we can now find out how we make that Messages array available, well here it is:

Template.messages.messages = function() {
    return Messages.find({

    }, {
      sort: {
        timestamp: -1
      },
      limit: 20
    });
};

All of our templates are namespaced under the Template variable. We then access our messages template and set a messages variable to an anonymous function which should return the relevant data; in this case, the messages from our Messages collection.

So, the first parameter of the find() is a conditions object, ie. something like:

{
    username: 'ben howdle'
}

This will bring back all records where the username is ben howdle. However, in our chat app, we want all records, so we don’t pass in a conditions object. The next parameter is a filtering object, ie. we want to sort by the latest message first, so we do:

{
    sort: {
        timestamp: -1
    }
}

The above will bring back the records in descending order by timestamp.

For conciseness, we also limit our recordset to 20.

Events

So, we’ve done all of this reading of data, how are we taking our lovely user’s message and saving it back to our MongoDB collection? Well, for each template we have, we can supply an Events hash. They look a little like this:

Template.input.events({
    'click #send': function() {
      var message = $('#newMessage').val();
      var username = $('#username').val();
      if (!message || !username) {
        alert('Fill out both fields yo!');
      }
      Meteor.saveMessage({
        message: message,
        username: username
      });
    }
});

You’ll notice we’re accessing the template via it’s name: input. We then pass in an object to an events() method.

A key of an Event’s object element is split into [eventType] [selector], the selector is scoped to the template itself. The value of the same key is either a named function or, like we’ve used, an anonymous function.

Saving data

After we’ve done some rudimentary DOM caching and validation, we then call a Meteor method called saveMessage and pass in an object with our user’s content.

Meteor.saveMessage looks a little like this:

Meteor.saveMessage = function(content) {
    var username = content.username;
    var message = content.message;
    if (!username || !message) {
      return;
    }
    Messages.insert({
      username: username,
      message: message,
      timestamp: Date.now()
    }, function(err, id) {
      if (err) {
        alert('Something definitely went wrong!');
      }
      if (id) {
        $('#newMessage').val('');
        $('#username').val('');
      }
    });
};

We then do some more validation (you can never be too cautious!) and then we use the Mongo method insert to save our data. The first parameter is a hash of key:values to save. We then have an optional second parameter, in which Mongo gives you two parameters to deal with (if you want to!), err and id. If the insert() was successful, then id is the id of the inserted record, if it wasn’t successful then err contains the error from Mongo. Depending on the results of your if statements, it’s upto you how you deal with that in the UI. I went for a classy alert() if it all went belly up.

So, that’s all well and good us saving the data to MongoDB but now I guess we have to write some lame ass boilerplate code to wire up our changes to the DOM reflecting the latest data, right? AW HELL NO.

Meteor templates are reactive, which means they automatically reflect the MongoDB collection. Magic. Save data and it’s updates in the DOM realtime to all clients that are viewing the app.

Just letting that information settle for a second…

Back? Cool.

Other notes

Other things we need to consider; security.

At the moment we actually allow all clients to insert into our MongoDB collection, like so:

Messages.allow({
    'insert': function(userId, doc) {
      return true;
    },
    'remove': function(userId, doc) {
      return false;
    }
});

What we have done, is restrict anyone (including us as developers writing the app code) from removing records. Everytime anyone tries to insert or remove from the Messages collection, Meteor will run through the above checks, passing in the currently logged user’s ID and the document in question. So, for example, we could do something like:

'remove': function(userId, doc) {
    if(userId == doc.userId){
        return true;
    }
}

This will check that the currently logged in user is trying to delete their own records and no one elses.

Neat huh?

A goodbye from me

So, here is a good point to leave it here. You should hopefully have enough information from this tutorial, coupled with the GitHub repo and demo to go off and build your own Meteor app! Go enjoy the realtime, reactive goodness…

If you do have any further questions, problems or just want to chat about Meteor, then drop me a tweet: @benhowdle

Back to the articles