Another ‘Hello World’ to MeteorJS
In this tutorial, I’ll try to walk you through building a simple realtime wall (like a guestbook) where anyone can post messages. And let’s try to do this without using any magic.
Install Meteor and Atmosphere #
- Installing Meteor
- Installing Atmosphere
Create new project #
Just go to the directory you want to create the app and create a new meteor project using following command.
meteor create the-wall
Feel free to run the meteor app although we haven’t even started coding it yet. It’s okay to keep it running even while you’re coding.
cd the-wall
meteor
Remove the magic #
We’re not going to use any magical features of Meteor. So, let’s remove them.
meteor remove insecure
meteor remove autopublish
Create a collection #
In meteor, we’ll be keeping most of our realtime data in collections. Create a new file posts/collections/posts.js
. This will make this collection available in both the server and the client. And we want to let anyone post messages and make sure no one can delete them.
posts/collections/posts.js
Posts = new Meteor.Collection('posts');
Posts.allow({
insert: function () { return true },
remove: function () { return false }
});
Publish the collection #
Data collections must be first published before they can be accessed by clients. For our example, let’s publish posts which posted in last 24 hours.
posts/server/publications/latestPosts.js
Meteor.publish('latestPosts', function () {
var yesterday = new Date().getTime() - 1000*60*60*24;
return Posts.find({created: {$gt: yesterday}});
});
Publishing happens server side therefor we can place the code inside a directory called server
. For more information about meteor directories, please read [Organizing Meteor files](#).
Let’s add some example posts so we can check if everything works. Go to localhost:3000
using a browser (I guess its either Firefox or Chrome). Bring up the developer console and enter the following.
var twoDaysAgo = new Date().getTime() - 1000*60*60*24*2;
Posts.insert({text: "Two Days Ago", created: twoDaysAgo});
var twoHoursAgo = new Date().getTime() - 1000*60*60*2;
Posts.insert({text: "Two Hours Ago", created: twoHoursAgo});
var now = new Date().getTime();
Posts.insert({text: "Now", created: now});
Subscribe and fetch posts #
If you try fetching them from the database, it won’t work because first we must subscribe to a publication. If you try to fetch anyways, it’ll return an empty array.
Posts.find().fetch()
=> []
First you must subscribe to the publication and wait until it’s ready.
var sub = Meteor.subscribe('latestPosts');
After some time, the subscription will be ready. You can check it using the ready method of the object returned by Meteor.subscribe().
sub.ready()
=> true
Finally, you can access posts from the client.
Posts.find().fetch().toString()
=> "[object Object],[object Object]"
You’ll notice that although there are 3 docs in the collection, only 2 gets to the client through this publication/subscription. This happens because we have limited docs sent to the client when publishing them.
Method to add posts #
Earlier we added posts from the client side and we set the created time manually. To make things simpler, we can create a Meteor method which does this for us.
posts/server/methods/saveMethod.js
Meteor.methods({
addPost: function (text) {
check(text, String);
Posts.insert({
text: text,
created: new Date().getTime()
});
}
});
And we can make sure this works by calling the method form the client. Try running this command on browser console.
Meteor.call('addPost', 'Added using method');
Finally the UI #
Let’s just wrap things up with a simple UI.
Main Wrapper #
client/index.html
<body>
<div id="wrapper">
{{> newPost}}
{{> postsList}}
</div>
</body>
client/index.css
#wrapper {
max-width: 900px;
margin: 5em auto;
padding: 0 5em;
font-family: sans-serif;
}
Form to add new post #
posts/client/newPost.html
<template name="newPost">
<div id="newPost">
<textarea autofocus id="newPostText"></textarea>
<button id="newPostButton">Save Post</button>
</div>
</template>
posts/client/newPost.css
#newPost textarea {
display: block;
width: 100%;
height: 2.5em;
padding: 0.5em 0.75em;
outline: none;
font-size: 1.5em;
box-sizing: border-box;
}
#newPost button {
display: block;
width: 100%;
background: rgba(0,0,0,0.05);
border: 1px solid rgba(0,0,0,0.1);
outline: none;
}
posts/client/newPost.js
Template.newPost.events({
'click #newPostButton': function (e) {
var textarea = $('#newPostText');
var text = textarea.val();
Meteor.call('addPost', text);
textarea.val('');
}
});
List Posts #
posts/client/postsList.html
<template name="postsList">
<ul id="postsList">
{{#each posts}}
<li>{{text}}</li>
{{/each}}
</ul>
</template>
posts/client/postsList.css
#postsList {
padding: 0;
}
#postsList li {
display: block;
font-size: 1em;
background: rgba(0,0,0,0.05);
padding: 1.5em 2em;
margin: 1em 0;
border: 1px solid rgba(0,0,0,0.1);
}
posts/client/postsList.js
Meteor.subscribe('latestPosts');
Template.postsList.posts = function () {
return Posts.find();
}