Anthony McLin

Rapid App Development using Meteor

I was skeptical of a recent SmashingMagazine article about building a webapp in 45 minutes. So I decided to follow along and see if I could replicate their success. I have never used Meteor before, or any server-side Javascript processing (aka Node.JS) for that matter. Usually I rely on Apache and PHP for the server side, so this will all be a new learning experience for me.

I've had an idea for a social scotch-tasting app for a while now, ever since we started an informal scotch club at work. I figured this would be the perfect test for stubbing out my idea.

Step 1: Install Meteor

I'm on OSX, so this was dirt simple to follow along. Do a direct download and execution of the Meteor installer via Terminal:

curl https://install.meteor.com | /bin/sh

Took less than a minute, and prompted for my admin password to allow the install. Easy-peasy.

Step 2: Start a Project

In the folder where I keep all my Git-based repositories, I create a new Meteor App

meteor create scotchApp

Now I can run the project.

cd scotchApp
meteor

Ok, that was damn fast. The app is now running at http://localhost:3000/

Add to a git repository

Let's turn this app folder into a git repository so we can keep track of everything. Fire up a new terminal window (since the previous one has the active Meteor server running) and navigate to the scotchApp folder. From there, we initialize a git repo:

git init
git add *
git commit -m "base project setup"

Now that I have a basic project setup, git versioning enabled, and the Meteor server running, lets get to coding!

Step 3: Make it not look like shit

Yeah, yeah... you can program functionality and add visual styling later, but if I'm going to be staring at something for a long time, I'd like it to look good. I could grab the Foundation framework, there appears to be a third party package manager called Meteorite, and a Meteorite package of Foundation ready to go. I don't want to get too far down the road of extending the base system though, so for now I'll stick with Bootstrap:

meteor add bootstrap
git add -A
git commit -m "Added Twitter Bootstrap"

Wow, that was easy.

Hmmm... but in the commit, only one file was modified. And I'm not seeing any files from the Bootstrap library in my project? WTH? I alt-tab over to Chrome where I have the app running, and HOLY CRAP, it's been updated without me even refreshing!

The app is already styled with the base Bootstrap theme, and I didn't even reload the page! It looks like Meteor is now tracking that Bootstrap should be slipstreamed into my project at runtime, but I don't have to actually track all the Bootstrap files myself! This is fantastic for buidling multiple projects and sharing a common library.

Step 5: Setup a basic display

Now let's edit the scotchApp.html that was so conveniently provided for us and includes the Hello World features. I've never used Handlebar templates before, but it looks fairly straightforward, and similar to Smarty templates (talk about reinventing the wheel).

<head>
  <title>scotchApp</title>
</head>
<body>
  {{> content}}
</body>

<template name="content">
    {{#each scotches}}
        {{> scotch}}
    {{/each}}
</template>


<template name="scotch">

<div class="span3 well">

        <div class="distillery">{{distillery}}</div>

        <div class="name">{{name}}</div>

        <div class="age">{{age}}</div>    

</div>

</template>

So far so good.

Step 6: Populate some data

I've got an output, but now I need to shove some entries in there. I modify the scotchApp.js to create a simple database on the server, and render out a list on the client using the template:

//Define the Mongo database table
Scotches = new Meteor.Collection("scotches");

if (Meteor.isClient) {
  //Render out the list of scotches using the template
  Template.content.scotches = function () {
      return Scotches.find({});
  }; 
}

if (Meteor.isServer) {
  Meteor.startup(function () {
    //Generate some placeholders if the database is empty
      if (Scotches.find().count() === 0) {
          Scotches.insert({
                  distillery: "Oban",
                  age: 14
              });
          Scotches.insert({
                  distillery: "Oban",
                  age: 18
              });
          Scotches.insert({
                  distillery: "Lagavulin",
                  age: 16
              });
      }
  });
}

Lo and behold, as soon as I save, there's my list of scotches. I even screwed up a few times while stubbing out the code and so have repeated entries in the database. Let's see if I can clean up the database by adding a delete button.

First edit the HTML to add an icon

<template name="scotch">
<div class="span3 well scotch">
        <span class="remove icon-remove-sign"></span>
        <div class="distillery">{{distillery}}</div>
        <div class="name">{{name}}</div>
        <div class="age">{{age}}</div>    
</div>
</template>

And a little bit of CSS for a nice visual

.scotch {
    position: relative;
}

.remove {
    position: absolute;
    top: 10px;
    right: 10px;
}

And, let's delete a record on click by adding to the Client section of the scotchApp.js

if (Meteor.isClient) {
  //Render out the list of scotches using the template
  Template.content.scotches = function () {
      return Scotches.find({});
  };

  Template.content.events({
      'click .remove': function() {
          Scotches.remove(this._id);
      } 
  });
}

And save... now I see the little delete buttons in my browser. And surprisingly, they work! I can quickly delete all those extraneous entries.

Step 7: Add new entries

Well, since I have the ability to delete entries, what about adding new entries? I guess I'm going to need an input form, I figure I'll break it into a seperate template section for flexibility as I move forward. Here's the HTML:

<template name="content">
    {{#each scotches}}
        {{> scotch}}
    {{/each}}

    {{> newscotchform}}
</template>

<template name="scotch">
   <div class="span3 well scotch">
        <span class="remove icon-remove-sign"></span>
        <div class="distillery">{{distillery}}</div>
        <div class="name">{{name}}</div>
        <div class="age">{{age}}</div>
   </div>
</template>

<template name="newscotchform">
   <div class="span4 well scotch">
        <form id="addscotch">
            <input name="distillery" placeholder="distillery name" />
            <input name="name" placeholder="bottling/release name" />
            <input name="age" placeholder="age" type="number"/>
            <submit class="btn"><span class="icon-plus-sign"></span> Add Scotch</button>
        </form>
   </div>
</template>

Seems straightforward enough. Now, I figure I probably need another event in the Client section of scotchApp.js. I probably can limit it to the newscotchform template I just created.

if (Meteor.isClient) {
  //Render out the list of scotches using the template
  Template.content.scotches = function () {
      return Scotches.find({});
  };

  Template.content.events({
      'click .remove': function() {
          Scotches.remove(this._id);
      }
  });

  //Events for the form
  Template.newscotchform.events({
      'click submit': function() {
          //TODO add validation!

          var formcontents = {};
          //Serialize the input values from the form
          //This works because the input names match the
          //database field names.
          $.each($('#addscotch').serializeArray(), function() {
              formcontents[this.name] = this.value;
          });
 
          //Pass the serialized values into a new database record
          Scotches.insert(formcontents, function(err) {
              if(!err) {
                  console.log("Successfully added new scotch to database.");
                  $('#addscotch')[0].reset(); //Reset the form with blank inputs
              } else {
                  alert("Failed to add scotch to database.");
                  console.log(err);
              }
            });
          }
  });
}

Lets see how that works. Save (don't even need to refresh the browser) and the new form element is showing. Inputing a new scotch works, deleting a scotch works.

Granted, this is a very very basic proof of concept. But I was able to whip it up in less than an evening, which I think is pretty impressive considering I've never before used Meteor, NodeJS, Handlebars, or Bootstrap. In fact, the only pieces here I have worked with before are HTML,CSS, and JQuery. I'm excited to progress with this toolchain to rapidly build out a web app.

Important Caveats

Also, if you're following along, a few big big best-practices caveats I should point out.

  1. I'm directly modifying the database from the client portion of the app. This is very dangerous. What you should do is make limited server calls (basically an API) that the client can safely call without directly touching the database.
  2. Once you split the client and server portions of the app remove the Autopublish and Insecure packages before releasing to the public.
  3. Make sure you do client-side form validation (to make it easier for your users) AND server-side validation (to ensure data integrety).

Comments

You can follow the progress of my Scotch-tasting app at ScotchDr.am

- Anthony McLin

Great! Thank you for sharing this!
meteor scotchApp ? typo?
meteor create scotchApp

- Min Soo Kim (not verified)

Good catch, I've updated the article to correct that command.

- Anthony McLin

I love your tutorial I build it rapidly!

- txshon (not verified)

However, I may be biased because I am also a scotch lover.

Well if you're interested in trying out the app as it is progressing, I'm taking beta signups at http://scotchdr.am

- Anthony McLin

Add new comment