Chris on Code
This tutorial is out of date and no longer maintained.
Meteor.js is an open-source platform built on Node and MongoDB. It’s not just a framework, it’s more than that.
Is it comparable to Angular? Angular handles just the frontend parts of the application. Meteor is more of a platform than Angular. It is both server and client-side code and handles a lot of the parts of applications that we need to create manually in Angular.
We’ll be diving into a simple application in this tutorial to show off the benefits and power of Meteor.
We’ll be creating a very simple polling application where a user can:
Let’s create our first Meteor app!
Follow the installation steps depending on your environment: OSX/Linux or Windows.
Test to make sure everything is installed:
- meteor --version
Meteor comes with a few demo applications that you can try out. If you want to check out a few demo apps now, you can go into your command line and grab the demo application using:
Todo sample app:
- meteor create --example todos
Example mobile application:
- meteor create --example localmarket
You can find the full list of examples in the Meteor GitHub.
Once you create a demo app, just cd
into that project and run the application with:
meteor
This will grab the necessary packages, bundle all the CSS and JS, start your application using a Node server, and make it viewable in your browser.
Definitely click around the files in these demo applications and you’ll see how Meteor apps tick. Let’s move onto creating our own application now.
With our newly installed Meteor CLI tools, we can easily create a new application with:
- meteor create polling
polling
will be the name of our new application and it will be created in a new polling/ folder. This command will create 3 new files and a folder:
| .meteor/ // holds information about our project
| polling.css // the css for our project
| polling.html // the overall template
| polling.js // javascript for the client and server
This is a very barebones setup and will not be how we structure our application, but it is useful to see how a very simple application can be built.
You can run this app by cd
ing into your new project and starting your meteor app with:
- meteor
Then you can view it in the browser at http://localhost:3000
.
If you take a quick look into polling.js
you’ll see some JavaScript bindings to the Template
(which is referencing our polling.html
file). What you’ll also find is two interesting lines of code that are how Meteor separates code that will be run on the server vs the client.
if (Meteor.isClient) {
// code here will only be run on the client
}
if (Meteor.isServer) {
// code here will only be run on the server
}
While we could build our whole application like this, with client and server code in the same file, I prefer to have our application structured so that we don’t have to worry about where our client and server code is.
Keep that meteor
command running for the duration of this tutorial. As we make changes to our application, Meteor will automatically restart the server, rebundle our files, and livereload our browser (while keeping all our data). Talk about convenient!
Luckily, Meteor has a few folders that are designed specifically for organizing our application. These folders are:
client/
: The client folder is for files that will only be served to the client. Any CSS and JS files in this folder will be automatically bundled and sent to the browser.server/
: The folder for all your server-side code. Store sensitive logic or data that a client shouldn’t see.public/
: By default, Meteor will find the CSS and JS files in your project and bundle them together. Anything in this public folder will be not be bundled by Meteor and will be served directly to the client as-is. If you have an image at public/images/example.jpg
, then you can reference that file from HTML using <img src="images/example.jpg">
.private/
: These files are only accessed by the server through the Assets API. We won’t be dealing with this folder for this tutorial.What’s cool is that with this structure, we won’t need to define Meteor.isClient
or Meteor.isServer
anymore. If a file is in that folder, then Meteor knows which side of our application it belongs to.
With these reserved folders in mind, let’s look at how our application structure will look like:
| .meteor
| client/ // all the code for our client and browser
|----- components/ // we'll be creating components for our application parts
|--- poll-form.css
|--- poll-form.html
|--- poll-form.js
|--- poll.css
|--- poll.html
|--- poll.js
|----- app.body.html // layout for our entire app
|----- app.head.html // document head for entire app
|----- app.js // the overall js for our layout
|----- app.css // the overall css for our layout
| collections/ // here we'll store our mongo models
|----- polls.js // defining our mongo collection
| server/ // code for our server
|----- bootstrap.js // adding sample data on app startup
client/
will be the folder where we spend most of our time. We’ll go through all of the components, views, CSS/JS in the next section.
client/components/
will hold the different parts of our application. In this case, we just need a form to create polls and a component to show the individual polls. All JS and CSS files in the client
folder will be bundled together by Meteor into our application so we’re naming these files is done however we want. They’ll all go to the same place anyway.
server/
will only contain one thing for now. We’ll create a bootstrap.js
file to seed our database with some sample data.
collections/
is where we define Mongo collections. In this case, we’ll only need one called polls.js
.
routes.js
will be in the root of our folder since the routes will be used in both the client and the server.
Take a look at the docs for more on Meteor file structure.
We’re going to start with the server and the collections part of our application first.
MongoDB is very simple to work with and we’ve gone over MongoDB in our article: What is MongoDB?. For our purpose for this application, we will be defining a single collection. Let’s do that now.
Create the collections/polls.js
file and fill it with:
Polls = new Mongo.Collection('polls');
The reason we define this in a collections/
folder and not in the client/
or the server/
folder is that this collection will be needed on both the server (creating sample polls) and the client (creating new polls).
We are re-using code on the server and client and this is what makes Meteor such a breeze to work with. With the Mongo collection out of the way, let’s create some sample data.
Let’s create some sample data for our application before we start to create the client-side part of our polling application. In the server/
folder, create a new file called bootstrap.js
.
Here is the full commented code for bootstrap.js
:
// run this when the meteor app is started
Meteor.startup(function() {
// if there are no polls available create sample data
if (Polls.find().count() === 0) {
// create sample polls
var samplePolls = [
{
question: 'Is Meteor awesome?',
choices: [
{ text: 'Of course!', votes: 0 },
{ text: 'Eh', votes: 0 },
{ text: 'No. I like plain JS', votes: 0 }
]
},
{
question: 'Is CSS3 Flexbox the greatest thing since array_slice(bread)?',
choices: [
{ text: '100% yes', votes: 0 },
{ text: '200% yes', votes: 0 },
{ text: '300% yes', votes: 0 }
]
}
];
// loop over each sample poll and insert into database
_.each(samplePolls, function(poll) {
Polls.insert(poll);
});
}
});
We are using the Meteor.startup
command to run this code when our server starts up. We’re going to check if there are any polls already created in our database and create sample polls if there is nothing in the database.
Note: If you ever want to clear what’s in your database, just run meteor reset
and your application will become a clean slate.
Since Meteor implements an instance of Mongo on the client, we are able to run MongoDB commands right in our browser’s console. Meteor already restarted our application so that means it should’ve already run that bootstrap.js
file and created our sample polls.
Let’s go into our browser and see our polls. Go into your browser’s console and run the Mongo command to find all the Polls
:
Polls.find().fetch()
You’ll see the two polls we created!
We now have the foundation we need to start building our polling application’s frontend. We now have:
Let’s move onto the thing that our users will actually see, the UI!
app.body.html
app.head.html
app.js
Meteor will find all references of <head>
and <body>
and combine whatever it finds to one <head>
and <body>
. So if you have multiple files that have a <body>
or <head>
, they will all be compiled into your final document.
Check the docs for more on Meteor templating.
Here is what our app.body.html
and app.head.html
files will consist of:
<body>
<!-- PULL IN THE POLL CREATION FORM -->
<div class="container">
<div class="row">
<div class="col-md-6 col-md-offset-3">
{{ >pollForm }}
</div>
</div>
</div>
<!-- LOOP OVER POLLS AND SHOW EACH -->
<div class="polls">
{{ #each polls }}
{{ >poll }}
{{ /each }}
</div>
</body>
{ >pollForm }}
will find a file with <template name="pollForm>
within it. We will create this file next. The {{ >templateName }}
syntax will be how we pull in different views from our application.
{{ #each polls }}
will loop over the polls
object and then show the template named poll
. We’ll look at how we pull in this polls
object from our database soon.
Here is the very simple <head>
of our app. Meteor will inject all the requirements into this section.
<head>
<meta charset="utf-8">
<title>My Polling App!</title>
<!-- CSS IS AUTOMATICALLY BUILT AND LOADED -->
</head>
Now that we have the main template down, let’s create the two templates that we referenced: pollForm
and poll
.
This will store the form for creating polls:
<template name="pollForm">
<form>
<div class="form-group">
<label>Question</label>
<input type="text" name="question" class="form-control" placeholder="Your Question">
</div>
<div class="form-group">
<label>Choice #1</label>
<input type="text" name="choice1" class="form-control" placeholder="Choice #1">
</div>
<div class="form-group">
<label>Choice #2</label>
<input type="text" name="choice2" class="form-control" placeholder="Choice #2">
</div>
<div class="form-group">
<label>Choice #3</label>
<input type="text" name="choice3" class="form-control" placeholder="Choice #3">
</div>
<button type="submit" class="btn btn-lg btn-primary btn-block">Create Poll</button>
</form>
</template>
We’ll just limit users to creating 3 options, for now, to keep things simple. In the future, you could add a plus button that adds an input for choices.
Here’s some quick CSS to style the question group a little differently than the option inputs. We’ll place this in poll-form.css
.
.question-group {
margin-bottom:20px;
background:#EEE;
padding:20px;
}
.question-group label {
font-size:18px;
}
Let’s go back to our browser and see what our cool application looks like now.
Pretty lackluster so far. This is because none of our Bootstrap classes will work because we never got Bootstrap CSS and added it to our project. We’ll handle processing this form and then create our poll
template. After that, we’ll move onto getting Bootstrap.
This will be the poll
template for showing off single polls.
<template name="poll">
<div class="poll well well-lg" data-id="{{ _id }}">
<h3>{{ question }}</h3>
{{ #each indexedArray choices }}
<a href="#" class="vote btn btn-primary btn-block" data-id="{{ _index }}">
<span class="votes pull-right">{{ votes }}</span>
<span class="text">{{ text }}</span>
</a>
{{ /each }}
</div>
</template>
We will be displaying the question
, choices
, votes
, and text
associated with the poll.
How do we get these polls to show? In our original app.body.html
file, we referenced the polls using {{ #each polls }}
. We are also adding in data-id
with the _index
so that we’ll be able to know which question to apply our vote to.
Let’s give our body access to this object from our database now.
We can easily assign variables to our body template by going into our app.js
file and creating:
Template.body.helpers({
polls: function() {
return Polls.find();
}
});
Just like we used the Polls
collection earlier, we can use it now to grab all our polls. This will give access to the polls
object within our body
template.
Notice we also added an indexedArray
to the choices
each. This is because we don’t inherently have the index when we loop over items in Meteor right now. That will probably change in the future, but it isn’t implemented currently because of the reactive nature of the templating engine. Apparently, $index
in these real-time applications is a harder thing to pull off.
We need to create this indexedArray
helper so let’s go back into app.js
and add the following:
// adds index to each item
UI.registerHelper('indexedArray', function(context, options) {
if (context) {
return context.map(function(item, index) {
item._index = index;
return item;
});
}
});
Thanks to Jenia Nemzer for the above helper. We now have access to the data-id={{ _index }}
. We’ll use this when we implement our voting features.
Now we can see the polls that we created in our bootstrap.js
file showing in our application.
Let’s wire up our form now to handle the form submission.
Meteor has a cool way of attaching events and variables to its templates. Currently, we have a pollForm
template. Let’s go into the poll-form.js
file and add event handlers for our form submission.
Template.pollForm.events({
// handle the form submission
'submit form': function(event) {
// stop the form from submitting
event.preventDefault();
// get the data we need from the form
var newPoll = {
question: event.target.question.value,
choices: [
{ text: event.target.choice1.value, votes: 0 },
{ text: event.target.choice2.value, votes: 0 },
{ text: event.target.choice3.value, votes: 0 }
]
};
// create the new poll
Polls.insert(newPoll);
}
});
Template.pollForm.events
is how we attach events to this specific template. We are defining an event to handle the submit form
event.
We can pull data from the form inputs using event.target.{input_name}.value
.
After we have gotten all the data we need, we are going to insert the new poll into our database using Polls.insert()
just like we did in our bootstrap.js
file on the server-side of things. Go ahead and use your form to submit data and you’ll find that the new poll automatically gets added to the list of polls.
We’ve added polls
to our overall template in app.js
, handled processing the poll form in poll-form.js
; now we will handle the last action, which is voting in the corresponding JS file, poll.js
.
Inside of client/components/poll.js
, let’s attach events to our template:
// attach events to our poll template
Template.poll.events({
// event to handle clicking a choice
'click .vote': function(event) {
// prevent the default behavior
event.preventDefault();
// get the parent (poll) id
var pollID = $(event.currentTarget).parent('.poll').data('id');
var voteID = $(event.currentTarget).data('id');
// create the incrementing object so we can add to the corresponding vote
var voteString = 'choices.' + voteID + '.votes';
var action = {};
action[voteString] = 1;
// increment the number of votes for this choice
Polls.update(
{ _id: pollID },
{ $inc: action }
);
}
});
A fun thing we can do to traverse the DOM is to use jQuery to call the current event and then find exactly what we need. To update the vote number for what was just clicked, we will need to get the parent ID of the poll and then the index of what was just clicked.
We will be using MongoDB’s $inc
operator to add 1 to the vote. Go ahead and click through your application and vote, and you’ll see the votes increment.
You may also be able to see other users voting in real-time!
This is some very simple templating here with the built-in Meteor features. For future larger projects, we’ll probably want to look at a routing solution like iron:router
, currently the most popular Meteor package in Atmosphere, Meteor’s packages site.
Let’s install Bootstrap to get some quick styling for our application. The thing to know about Meteor packages is that you only need to install them to get them to work.
Traditionally, to get Bootstrap, you would:
<link>
tag in your project’s <head>
In Meteor, all you have to do is install Bootstrap and it’s automatically applied to your project. Meteor’s build system will include the CSS on its own.
Let’s install Bootstrap found in Atmosphere.
- meteor add twbs:bootstrap
Now, if we go look at our application, we’ll see that our styling is there!
Note: You could also do things the old-fashioned way of grabbing the Bootstrap file and adding it to your public/
folder and then linking to it in the document <head>
if you want to go that route.
In addition to having Bootstrap, let’s add some of our own styles real quick to app.css
to get our grid of polls looking a bit better:
Let’s make our polls use flexbox to make it easier to create a grid of polls.
Inside of app.css
, let’s add some quick styling:
body {
padding-top:50px;
}
.polls {
display:flex;
flex-flow:row wrap;
justify-content:center;
}
.poll {
width:25%;
margin:20px;
}
We now have a decent-looking application with the basic functionality of a polling application!
Since our application is still in development mode, all collections are automatically published from our server and subscribed to on our client. We probably won’t want this behavior in our production applications since it doesn’t make sense for users of our application to subscribe to EVERY part of our application, even the parts they aren’t using at any one time.
It makes more sense to only have a user subscribed to the data that they are seeing at any one time. If you go look into .meteor/packages
to see the packages in your Meteor application, you’ll see one called autopublish
. This will need to be removed and then you’ll have to publish and subscribe to data manually in your application.
To remove this package, use:
- meteor remove autopublish
Then you can use Meteor.publish()
and Meteor.subscribe()
.
Now we want the whole world to see our super cool new application. We can use Meteor’s servers to show off our application.
With one simple command, our entire app will be available online:
- meteor deploy example.meteor.com
You can change out the subdomain for anything you like as long as it isn’t already taken. We’ll explore deploying to your own servers in future articles.
Meteor provides so many great tools to easily and quickly prototype your applications. Real-time, JavaScript client and server-side, the packages system, build system, minimongo, and so much more.
Hopefully, this has given you a good taste of how quickly you can build an application in Meteor.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!