Meteor, a Framework Where the Hardest Part is Coming Up with an App Name

If you’ve spent nights researching and pouring over new technologies almost as fast as they’re 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 that are 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

Of course that was before you met Meteor. It’s as if the powers that be heard the pleas of countless Dev’s, 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 an assortment of packages and tools that allow you to start creating scalable apps immediately. This includes MongoDB, Node.js, Spacebars and Blaze (a specially adapted of version of Handlebars and jQuery and virtual DOM). Meteor also integrated an easy way of building mobile apps, entirely in JS thanks to a custom Cordova integration. The magic doesn’t stop there. Meteor even 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:
1. Allow for real time text editing.
2. Show a live preview of our code.
3. 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 the purposes of 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 client folder accessible to the client and any code found in 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 off 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 main.js files that is irrelevant to our project.

From main.html we can change the name of our template from “hello” to “editor” and move it into its own 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, 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 in order 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 setup 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 is 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 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 in by the Meteor template helper (Spacebars). This syntax might even look familiar to many, because it based off of 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:

<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:

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 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: 50px;
}

After we style our editor we want to separate out 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:

<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 in order 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 to 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 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.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: 50px;
}
#viewer_iframe{
border:1px solid gray;
resize:both;
width:100%;
height:100%;
}

Our final app build should now look like this after styling:

Outro

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 reduces the amount of time and code it takes to develop exciting web applications. And 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.

Show your support

Clapping shows how much you appreciated Jscrambler’s story.