Meteor, a Framework Where the Hardest Part is Coming Up with an App Name
April 26th, 2016 | By Jscrambler | 11 min read
Dive into our article to explore and understand how simple Meteor is.
If you’ve spent nights researching and pouring over new technologies almost as fast as they are created, then you know what it is to be a JavaScript developer in 2016.
You fill your mind with the most cutting-edge build processes and utilities expected of a relevant Web Dev. Yet, it seems like the JS ecosystem continues to change, and you never actually catch up.
Modern JS Developer
That was before you met Meteor. It’s as if the powers that be heard the pleas of countless Devs to incorporate a best-in-class bundle of libraries and tools for building JS applications into one framework.
Meteor is that complete framework.
It consists of packages and tools that allow you to start creating scalable apps immediately. This includes MongoDB, Node.js, Spacebars, and Blaze (a specially adapted version of Handlebars, jQuery, and virtual DOM).
Meteor also integrated an easy way of building mobile apps in JS through a custom Cordova integration. The magic doesn’t stop there. Meteor has a dedicated package manager and community similar to NPM called Atmosphere.
Getting set up with Meteor
Meteor is a full-stack framework, meaning it was created for building apps that can easily scale into larger platforms with the included components. To understand Meteors simplicity and ease of use we’ll be building a collaborative text-editor app. Let’s call it Sharedit for obvious reasons and because I’ve heard worse web app names.
First, let’s describe the main functionality and features of our app:
Allow for real-time text editing.
Show a live preview of our code.
Don’t look terrible
Meteor has the technical stack to make our app a reality in a short amount of code and time.
As of today, Meteor is at version 1.3. The latest version boasts improved ES2015 along with integrations with React and Angular if that happens to be your preference.
For this project, we’ll install Meteor through our terminal on version 1.2.1. Not to worry, as we’re not using any bleeding edge features just yet.
To begin our app we’ll start by installing the MeteorJS stack onto our local machine.
curl https://install.meteor.com/ | sh
Once you’ve installed Meteor, create a new project at our specific version:
meteor create Sharedit--release 1.2.1
In our project’s version of 1.2.1, we start with 3 files, which is all we need for our app.
Sharedit.html
Sharedit.css
Sharedit.css
However, when you create a new project in 1.3, Meteor creates a file structure along with boilerplate files including css and our app’s HTML. A freshly created project root folder should look similar to the structure below:
client/main.js # a JavaScript entry point loaded on the client
client/main.html # an HTML file that defines view templates
client/main.css # a CSS file to define your app's styles
server/main.js # a JavaScript entry point loaded on the server
package.json # a control file for installing NPM packages
.meteor # internal Meteor files
.gitignore # a control file for git
Meteor depends on this specific file structure for recognizing whether code runs on the server or client. This distinctly modular approach makes any code found in the client folder accessible to the client and any code found in the server folder on the server.
Any code found in the root will be accessible to both the client and the server. If we needed to, we could manually create the above folder structure to allow our 1.2.1 code to run in its respective environment exactly as it would in 1.3.
Installing dependencies
We’ll also need to install Sharejs with Codemirror, which will enable the collaborative functionality found in our app:
meteor add mizzao: sharejs - codemirror
Meteor starts your project with code for a counter app tutorial. In this next step, we can remove a portion of the boilerplate code from the main.html and mostly all from the main.js files that are irrelevant to our project.
From main.html we can change the name of our template from “hello” to “editor” and move it into its template. We can delete everything else so that we only see the following:
<head>
<title>Sharedit</title>
</head>
<body>
<h1>Welcome to Meteor!</h1>
</body>
<template name="editor">
{{> editor}}
</template>
Then, in our main.js we can remove essentially all boilerplate code below our imports. Go to your terminal and run Meteor by typing
meteor
Meteor will fire up a server and our template should render a blank page with only a “Welcome to Meteor!” Now we can begin adding functionality to our app.
Draw the rest of the owl
Meteor’s use of MongoDB is unique in the way it manipulates data.
The client initiates changes, those changes are sent to the server model, clients are informed that the model has changed, and the templates in the browser update the page on the client. This is all thanks to live data and client-side MongoDB.
To see this in action we’ll head back into the client/main.js file and add a startup function that will create a new MongoDB collection to store the data necessary for keeping our collaborative editor in sync.
this.Documents = new Mongo.Collection("documents");
We’re adding “this” to give our text editing packages access to our Document collection (MongoDB’s version of a table). Without this, the ShareJS package will not load.
Next, we can create our client and server blocks by adding the following:
if (Meteor.isClient) {}
and
if (Meteor.isServer) {
Meteor.startup(function() {
// this code will run at startup.
});
Our server block will check to see if our collection exists when we add documents.findOne().
Next up we’ll create an If/Else statement to detect any documents. By adding a ! logical NOT operator to our function we can check our document for data (or lack thereof in this case). If no document data exists then the if statement will run by adding a title to the app.
if (Meteor.isServer) {
Meteor.startup(function() {
// startup code that creates a document in case there isn't one yet.
if (!Documents.findOne()) { // no documents yet!
Documents.insert({
title: "my new document"
});
}
});
Because this code is on our client, it will run at startup. If our code is correct we should be able to see the object with our title when we run our app.
Next, we’ll need to set up our template helpers which will pass in our docid information and display to our HTML page. We call a template within our main.html by adding the following to our template property.
{
{ > sharejsCM docid = docid id = "editor"
}
}
This will display a code mirror editing window but we also need to provide a document ID, which will originate from the template helper we’ll add in the next step.
We won’t run it just yet though as we need to set up our template helper to pass information into our document. In our main.js file, we need to add our template helper within our client block by adding these few lines.
if (Meteor.isClient) {
Template.editor.helpers({
docid: function() {
var doc = Documents.findOne();
if (doc) {
return doc._id;
} else {
return undefined;
}
}
});
}
Our app will now find the first document in the Documents collection and send back its ID to the template.
The template will only be able to access other templates along with the docid parsed by the Meteor template helper (Spacebars). This syntax might even look familiar to many because it is based on the handlebars templating library.
Lastly, we have to create a variable to hold our Document and include an If/Else statement to provide our docid on success.
Your Sharedit.html should look like this:
<head>
<title>Sharedit</title>
</head>
<body>
<h1>Welcome to Meteor!</h1> {{> editor}}
</body>
<template name="editor">
{{>sharejsCM docid=docid id="editor"}}
</template>
Your sharedit.js should now look like this:
if (Meteor.isClient) {
Template.editor.helpers({
docid: function() {
var doc = Documents.findOne();
if (doc) {
return doc._id;
} else {
return undefined;
}
}
});
}
It’s time to run and test our app with the meteor command in your terminal. Only this time open up localhost:3000 on another browser and bask in your progress.
You should now see both browser windows update in real-time with any changes you make to the text. So far we’ve successfully implemented collaborative real-time editing functionality into our Meteor app.
Next, we add some reactivity to our app. For the uninitiated, Reactive data is simply data that automatically tells us when it gets updated. This is exactly what we want, to render changes in our view automatically according to the changes that we make to our data.
When I type something into the editor, the template should re-render based on the new data in our collection.
Meteor has this reactivity built into it at a low level. Because real-time data is a cornerstone of our app Meteor allows us to take full advantage.
Style and Flare
We’ll need to polish our app some so that it doesn’t look so basic. To make our lives easier we’ll add a bootstrap meteor package to easily style our entire project.
meteor add twbs: bootstrap
Then in our sharedit.html, we can add in the following to add a navbar along with a stylish heading.
<body>
<!-- / nav container -->
<nav class="navbar navbar-default navbar-fixed-top">
<div class="container">
<a class="navbar-brand" href="#”>Sharedit</a>
</div>
</nav>
<body>
We can encapsulate our editor inside a Bootstrap container div to ensure that it displays on our page. Then we make sure it remains visible by applying top-margin CSS properties to it within our CSS file.
.top - margin {
margin - top: 50 px;
}
After we style our editor we want to separate the page into our left half text editor and our right half iframe render. Start by adding some more Bootstrap classes to style the left column:
<div class="container top-margin">
<div class="row">
<div class="col-md-6">
{{> editor}}// this class will house our editor
</div>
The div container will wrap our editor template within half of the page and if you created the top-margin class should be invisible as soon as you save your file.
Next, we’ll create the right column HTML preview to display our rendered code as we type similar to JSFiddle or Codepen. The left half of our page will be our code and the right half will be a live preview of the code on the left, rendered within an iframe.
The code below includes the template for our HTML view template {{>viewer}} on the right side of the page:
<div class="container top-margin">
<div class="row">
<div class="col-md-6">
{{> editor}}
</div>
<div class="col-md-6">
{{> viewer}}
</div>
</div>
</div>
Our HTML so far should look like this:
<head>
<title>Sharedit</title>
</head>
<body>
<nav class="navbar navbar-default navbar-fixed-top">
<div class="container">
<a class="navbar-brand" href="#”>Sharedit</a>
</div> <!-- / nav container -->
</nav>
<div class=" container top-margin ">
<div class="row”>
<!-- / right and left containers -->
<div class="col-md-6">
{{> editor}}
</div>
<div class="col-md-6">
{{> viewer}}
</div>
</div>
</div>
</body>
<!-- / Template for our editor -->
<template name="editor">
{{>sharejsCM docid=docid id="editor"}}
</template>
<!-- / Template for our iframe viewer -->
<template name="viewer">
<iframe id="viewer_iframe">
</iframe>
</template>
Then within the template that renders our editor, we’ll need to reconfigure our codemirror to bind our editor to the viewer by adding onRender=config which passes a function that displays the current state of the editor.
<template name="editor">
{{>sharejsCM docid=docid onRender=config id="editor"}}
</template>
Next, we move into our Sharedit.js to create the config function that will bind our editor and viewer. The configuration function will also set properties, like our event listener, that will mirror the code in our editor as soon as we type it into our {{>viewer}} iframe.
We define our template helper inside of our config function within Sharedit.js.
Sharedit.js:
if (Meteor.isClient) {
Template.editor.helpers({
docid: function() {
var doc = Documents.findOne();
if (doc) {
return doc._id;
} else {
return undefined;
}
},
config: function() {
return function(editor) {
editor.setOption("lineNumbers", true);
editor.on("change", function(cm_editor, info) {
$("#viewer_iframe").contents().find(
"html").html(cm_editor.getValue());
});
}
},
});
}
Config is a helper function for our template. Inside our function, we’re passing in the Codemirror editor along with information about the changes with our event listener editor.on("change", function(cm_editor, info) Whenever we see a change in the editor render it to our {{>viewer}} within our Sharedit.html.
This is where we can also add some flare to our text editor by enabling numbered lines in our code editor with the editor.setOption("lineNumbers", true); Lastly we’ll dynamically render our code in the iframe we created in our Sharedit.html file. We can use a jQuery selector to select the iframe we named “viewer_iframe” and display the contents from our editor.
We can chain our jQuery functions like so:
$("#viewer_iframe").contents().find("html").html(cm_editor.getValue());
Our completed Sharedit.js code should look like this:
this.Documents = new Mongo.Collection("documents");
if (Meteor.isClient) {
// find the first document in the Documents collection and send back its id
Template.editor.helpers({
docid: function() {
var doc = Documents.findOne();
if (doc) {
return doc._id;
} else {
return undefined;
}
},
//template helper necessary for passing in our editor data.
config: function() {
return function(editor) {
editor.setOption("lineNumbers", true);
editor.on("change", function(cm_editor, info) {
$("#viewer_iframe").contents().find(
"html").html(cm_editor.getValue());
});
}
},
});
}
if (Meteor.isServer) {
Meteor.startup(function() {
// code that runs at startup and creates a document in case it doesn’t exist.
if (!Documents.findOne()) { // no documents yet!
Documents.insert({
title: "my new document"
});
}
});
}
Our completed Sharedit.html should be the following:
<head>
<title>Sharedit</title>
</head>
<body>
<nav class="navbar navbar-default navbar-fixed-top">
<div class="container">
<a class="navbar-brand" href="#">Sharedit</a>
</div>
<!-- / nav container -->
</nav>
<div class="container top-margin">
<div class="row">
<div class="col-md-6">
{{> editor}}
</div>
<div class="col-md-6">
{{> viewer}}
</div>
</div>
</div>
</body>
<template name="editor">
{{>sharejsCM docid=docid onRender=config id="editor"}}
</template>
<template name="viewer">
<iframe id="viewer_iframe">
</iframe>
</template>
In our CSS file we can add some final styling to the borders and enlarge the preview area with a full-width column:
.top - margin {
margin - top: 50 px;
}
#viewer_iframe {
border: 1 px solid gray;
resize: both;
width: 100 % ;
height: 100 % ;
}
Conclusion
We’ve built a real-time collaborative text editor thanks to the power of Meteor.
If it wasn’t for the combination of several extensible web components that make up Meteor, our job would not have been as easy. It may be difficult to appreciate what this framework has allowed us to accomplish in so few lines of code.
Everything we needed to build this app was found within the Meteor ecosystem. Meteor is more than a fancy build tool made of popular packages, it’s simplicity and productivity greatly reduce the amount of time and code it takes to develop exciting web applications.
Even better, Jscrambler is compliant with it (as it is for all main JS frameworks) so we can easily protect our Sharedit app before we deploy it. See it for yourself - start your free Jscrambler trial today.
Jscrambler
The leader in client-side Web security. With Jscrambler, JavaScript applications become self-defensive and capable of detecting and blocking client-side attacks like Magecart.
View All ArticlesMust read next
Unraveling HTTP Parameter Pollution
In this blog post, let's learn about HTTP Parameter Pollution, or HPP, which affects multiple modern applications.
October 28, 2022 | By Jscrambler | 6 min read
Jscrambler transformations: what you can expect from Rename Local
If you don’t know our renaming source code transformations, they basically replace identifier names with meaningless ones. Check them here!
September 30, 2014 | By Filipe Silva | 4 min read