Figure6-1

Introduction

Meteor is an open source Javascript framework for creating real-time apps. Real-time, means that the application responds to events immediately, without reloading. AJAX is often used for this purpose, but we don’t need to use AJAX with Meteor, since many real-time features are built directly into it. MongoDB is also built directly into Meteor. MongoDB is a database system that can manage the data/information used in our applications. Even if you don’t know MongoDB, you may still be able to use Meteor. The only pre-requisites required to learn Meteor are basic skills in HTML, CSS, and Javascript. However, a working knowledge of MongoDB is essential to taking advantage of all the features that Meteor has to offer. Although databases may seem a bit boring on the surface, you can actually do some amazing things with MongoDB.

There are many unique and powerful features within Meteor that are very easy to use. Creating a user login system or deploying our app to the web are made possible by entering a single command in the command line. It’s clear that Meteor focuses on making it easy for the developer to add many different types of functionality to their applications. Meteor apps are also cross-platform, which means that Meteor applications are compatible with multiple operating systems for both PC and mobile devices.

Installing Meteor

Installing Meteor is especially easy for Linux and Mac users, since it only involves entering the following command into the terminal:

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

If you’re using Windows, you may download the installer from the following link:
https://install.meteor.com/windows

Creating Our First App

Now that we’ve installed Meteor, let’s jump right into creating our first application. Open up your terminal and enter the following command to create a new Meteor app named HelloMeteor:

meteor create HelloMeteor

After the app is created, enter the following commands to change into our application directory and fire up a development server so that we may run our app:

cd HelloMeteor
meteor

By default, our Meteor server will use port 3000. Go into a web browser and visit the following location:

localhost:3000

If the above URL did not work, try replacing localhost with 127.0.0.1

Your browser window should look like the screenshot in Figure 3-1:

Figure3-1

Figure3-1

Clicking the button increases the value displayed in the string of text below the button. Let’s take a look at the source code of HelloMeteor.html, which may be found in the directory of your HelloMeteor application. The code for HelloMeteor is also shown in Code Listing 3-1.

Code Listing 3-1

Within the HTML (which interestingly doesn’t use html tags), you may have noticed an element named template that you’ve never seen before. Meteor uses a template engine to render HTML content. Templates are always placed outside of the body tags, since they are separate from the rest of the HTML. The name of the only template in HelloMeteor.html is hello, which you see referenced in the body of our HTML. When the output is rendered, the {{< hello}} is replaced by the HTML of our template named hello. Likewise, if we had a template named menu, we could display it in the body of our html with {{< menu}}. As a quick experiment, duplicate the {{< hello}} by copying that line of code and pasting it into a new line immediately after it. When you refresh your browser, the output should look like Figure 3-2:

Figure3-2

Figure3-2

Go ahead and remove the duplicate {{> hello}} now that you understand how the templates work. You may also notice within our template, we reference something named counter (which is surrounded by two sets of curly braces). Although {{> signifies a template, two curly braces without a greater than symbol signify a helper to call from our Javascript. The Javascript code of our Meteor application (HelloMeteor.js) is shown in Code Listing 3-2.

Code Listing 3-2

In HelloMeteor.js, we have a session variable named counter that begins at zero. The {{counter}} within our HTML isn’t referencing our session variable directly, it’s actually calling the helper function named counter. The counter helper function is defined in Template.hello.helpers (which returns the value of our session variable). In Template.hello.events, we have an event listener for buttons that increments the value of our session variable by one. Last, you’ll notice that almost all of our code is within a conditional block that runs only if Meteor.isClient returns true. All of the code that runs on the client goes here, while all of the code that runs on the server goes in the block under the conditional test/if statement for Meteor.isServer. This sample application created by Meteor doesn’t have any server-side code, but if it did, this is where it would go.

InstantCart – An Example of a Meteor Application

The default Meteor app shows off some of the basic ideas behind Meteor. However, to really gain a strong grasp of Meteor it is very useful to create our own app from scratch. We’re going to create an application that I have named InstantCart, which is a shopping cart application. Traditionally, when you’re doing shopping online, you’ll add a product to your cart, and then the page reloads. On the other hand, our Meteor shopping cart/store application will do everything in real-time. The application won’t need to reload to update the shopping cart. It will also display the current shopping cart at the bottom of the app.

Let’s get going on creating our app. Go into a command line, cd (change directory) into whichever folder you want the app to be contained, and then enter the following command:

meteor create InstantCart

Then:

cd InstantCart
meteor

If you visit localhost:3000 in your web browser, you’ll find that we have the same “Hello World” application once again. When creating applications, I generally like to follow an agile approach. Therefore, I prefer to first create a simple prototype that has the basic idea of what I want, and then gradually add more functionality/features to the application. As I add each change, I test the app and make sure the new features are working the way that they should.

Our particular app will use a hardware store as an example, but you could really use virtually any type of store with the “Instant Cart” style of shopping cart. The initial prototype will display three picture buttons (a wrench, a hammer, and a screw driver). It will also display the items in our cart at the bottom. Whenever you click on one of the buttons, it will update the cart in real-time (no reloading). The logic is similar to the logic of the default Meteor app, in which we use counter variables to keep track of how many times the images have been clicked. The initial version of our app will look like Figure 4-1 when it initially loads. Figure 4-2 shows the state of the application after I clicked on the wrench button twice and the hammer button four times. By the way, I’m aware that our application is rather ugly looking, but we will add styling to it later (let’s get it working properly first).

Figure4-1

Figure4-1

Figure4-2

Figure4-2

Let’s update the default files created by Meteor (InstantCart.html, InstantCart.js, and InstantCart.css) so that our application looks and behaves like the application shown in Figures 4-1 and 4-2. First, you’re going to need to create a folder named public within your application directory. The public folder will be used for storing the assets of our application (in this case, the images for the wrench, hammer, and screwdriver). Meteor knows that it should look in this directory for things such as picture files.

Next, let’s add the necessary HTML, Javascript, and CSS code. Add the code to your application now, and we’ll review the code in each file later. The code for the InstantCart.html is shown in Code Listing 4-1, the code for InstantCart.js is shown in Code Listing 4-2, and the code for InstantCart.css is shown in Code Listing 4-3.

Code Listing 4-1 (InstantCart.html)

Code Listing 4-2 (InstantCart.js)

Code Listing 4-3 (InstantCart.css)

Let’s first go through the code of InstantCart.html, as shown in Code Listing 4-1. In the head, we simply set our title to InstantCart. Then we have the body of our document:

If we weren’t using Meteor, our body would need to have much more code in it to render the same result. By using Meteor templates, it is not necessary to clutter our body. Instead, we separate all of the necessary components of our application into different templates. The body of our document first renders the header template, then the store template, and finally the cart template. Now let’s take a look at each template, starting with the header template:

This template is used as the header for our application. It shouldn’t need any further explanation – it just contains a header with the name of our store. The next template is the store template:

The store template consists of three buttons, one for each item in our store (a wrench, a hammer, and a screwdriver). Instead of using buttons that contain text, we’re actually using buttons that contain images. Finally, we have the cart template:

Our shopping cart template displays each item in our cart. The quantity shown for each item is equivalent to the number of times that particular button has been clicked. {{wrenchCounter}}, {{hammerCounter}}, and {{screwdriverCounter}} are populated with the values of our session variables for each of the items in our store. It’s the same idea as how {{counter}} is used within the first application we created (HelloMeteor). Now let’s take a look at our Javascript. Our application doesn’t have any server-side code, so we’ll just be looking at the client-side code (which is located in the curly braces of the conditional statement that checks whether Meteor.isClient is true). First, we initialize the counter variables for our wrenches, hammers, and screwdrivers to zero:

We then create the helper functions for our cart template:

The helper functions for the cart template are essentially identical to the helper function of our hello template in HelloMeteor.js (from the default application created by Meteor). The only real difference is that we’re specifying multiple helper functions for our cart template (instead of only one) by separating each helper function by a comma. Each of our helper functions simply returns the value of the associated session variable. As mentioned earlier, we have a session variable that keeps track of the number of wrenches in the cart, another variable that keeps track of the number of hammers, and one more variable that keeps count of the screwdrivers in our cart.

The last step is to create event handlers for events that occur within our store template:

Each of the event handler’s work are nearly identical to each other. We have one for each of the items that we sell (wrench, hammer, and screwdriver). Therefore, we’ll just look at the event handler for the wrench button, since the hammer and screwdriver event handlers work in exactly the same way.

The ‘click #wrenchButton’ : function() {} basically says “when the button with the id wrenchButton is clicked, perform the following function.” Within the body of that function (which is everything between the curly braces), all we do is increment the counter variable for our wrench (wrenchCounter) by setting it to it’s current value plus one. This is also similar to the event handler of the hello template within the default Meteor application, so it shouldn’t be too difficult to understand.

That covers all of our Javascript, so now we will examine the CSS:

First, we set the border of our buttons to a 2 pixel solid yellow border. The background of our buttons is set to white to match the background of our images (for the wrench, hammer, and screwdriver). The cursor property simply specifies the type of cursor that should be used when hovering over the element. In this case, we specify a regular mouse pointer.

InstantCart 2.0 – A MongoDB Solution

There are some major issues with our previous shopping cart application, including (but not limited to) security holes and the fact that you can’t remove an item from your cart. The security issues are far outside the scope of this article, and we aren’t going to worry about making our application secure. Therefore, you shouldn’t use this code as-is for a real e-commerce shopping cart. However, we should at least allow the user to remove an item from their cart – customers are likely to change their mind after they add something to their cart. We will also eventually improve the general look and feel of the application.

We’ll be using a MongoDB collection instead of session variables for the new version of our app. In MongoDB, a collection is the equivalent of a database table in SQL – it’s a collection of rows/columns. If you’ve never dealt with SQL databases, think of a MongoDB collection as sort of like a spreadsheet OpenOffice Calc or Microsoft Excel. MongoDB is different from SQL since it’s much less structured, and it has a unique syntax for communicating with the database. Every Meteor application automatically comes equipped with a MongoDB database, so there isn’t anything that we need to do to setup the database. Since the database already exists, we can create a collection anywhere in our Javascript code. Once we create the collection, we can add rows of data to it. Rows are generally referred to as documents within MongoDB – although I prefer to use the term rows since it makes more sense. We’re going to create a collection named Items for our application which will hold all of the items in our shopping cart:

I come from a traditional SQL background and find MongoDB to be far more confusing – both due to the jargon that it uses and the fact that it’s less structured. However, it does have some powerful features and can do some interesting things. If you want to develop apps with Meteor, MongoDB certainly isn’t something that you should ignore. With that being said, there is quite a lot to becoming proficient with MongoDB, and we can only briefly cover it in this article. Therefore, although I will give brief explanations for how the MongoDB code works, some of it may still be a bit unclear. As I mentioned before, MongoDB is a bit confusing in nature (contrary to what other developers may believe) and you won’t be able to completely understand it all at once. The new version of our application will at least serve as a demonstration of what you can do with MongoDB, and you will pick up a few concepts of MongoDB along the way.

Without further ado, let’s update the code for our application (and then we can take a look at the code in depth). InstantCart.html is shown in Code Listing 5-1, InstantCart.js is shown in Code Listing 5-2, and InstantCart.css is shown in Code Listing 5-3.

Code Listing 5-1 (InstantCart.html)

Code Listing 5-2 (InstantCart.js)

Code Listing 5-3 (InstantCart.css)

I also took a few screenshots of the application to show off the new version of our application. When the application initially loads, it should look like Figure 5-1. After you add three wrenches and two screwdrivers, it should look like Figure 5-2. If you hit the Remove button for our wrench three times and once for our screwdriver, it should then look like Figure 5-3. As you can see, we can now remove items from our cart!

Figure5-1

Figure5-1

Figure5-2

Figure5-2

Figure5-3

Figure5-3

Let’s first take a look at our updated InstantCart.html file. The majority of the file is actually exactly the same as the first version of InstantCart. The only thing that has changed is our cart template:

We use an HTML table for the new version of our cart, which also comes equipped with a price column and a button for removing items from the cart. Each item in our shopping cart is stored in a MongoDB collection, which we iterate through using #each. #each is a for each loop, which allows us to iterate through an array or collection. The rows of our table are dynamically created by populating {{subtract}}, {{name}}, {{quantity}}, and {{price}} with values from our collection of shopping cart items.

We need to take a look at InstantCart.js to really understand why InstantCart.html works the way it does. The first line of code of InstantCart.js creates our collection of shopping cart items if it hasn’t already been created:

Before we go into the client-side code, let’s also take a quick look at the server-side code of our application since it’s short and sweet:

Items.remove({}); removes all of the items within our Items collection. This way, every time we refresh our application, the shopping cart collection is reset to having no items. You can comment out or remove this line of code if you would like to retain the data in your Items collection. However, for testing/development purposes, I often like to start from scratch each time I load the application. Let’s start going through the client-side code of our Javascript.

First, we have the helper functions of our cart template. There is only one helper function defined, which is named items:

The only thing that this helper function does is return an array of all objects in the MongoDB collection named Items. The only parameter of the find method that we use (there are other optional parameters which we aren’t using) is the query parameter. The query parameter is used to filter our collection, although passing just {} means “everything in the collection”. Remember, the Items collection is used to hold all of the items in the customer’s shopping cart, so the items helper function returns all of the items in the cart. This helper function is actually being called in this line of code of InstantCart.html:

We have already covered this code, but just to clarify, {{#each items}} creates a loop that iterates through each element of the array returned by the helper function named items.

Next, we define the event handler functions for our store template. The event handlers for the wrench, hammer, and screwdriver are almost exactly the same, so we’re just going to cover the event handler for the wrench. The same logic that applies to the wrench also applies to the hammer and screwdriver.

Just like the InstantCart.js in the first version of our application, we specify that we want to create an event handler for a click on the wrench button with the code ‘click #wrenchButton’: function (). The code that follows in the curly braces is executed each time the wrench button (named wrenchButton) is clicked. First, we store the array of rows returned by the find method of our collection (which is really only one row in this case, but the find method returns an array, even if it’s only one element) into a variable appropriately named wrenches:

As mentioned earlier, the first parameter to the find method specifies a query/filter for what we return from our collection. In this case, we’re only returning rows (also known as documents within MongoDB) of our collection which have the name field set to “Wrench”. As mentioned before, this is really only one row. We’re about to see why there’s only one row named Wrench, as opposed to potentially several rows:

We only insert a wrench row into the Items collection if the number of rows named wrench in our collection is less than one. In other words, if any rows (documents) already exist within our collection with the name field set to “Wrench”, we don’t insert a new row. Instead, if a wrench row already exists, we simply update the quantity of wrenches by using the update method of the Items collection. In our insert method, we specify the values for each field in the following format:

fieldName: fieldValue

We separate each field by commas; our list of fields includes:

  1. subtract – This field holds the HTML code for our subtract button, so that we can remove items from our cart.
  2. name – This field represents the name of the item.
  3. quantity – This represents the quantity of this particular item in our cart, which is initially set to 1.
  4. price – This represents the price of our item, which remains constant (just like the name of our item). If we created an admin dashboard for our store, then we would want to be able to manipulate the price of our item. However, the customer certainly shouldn’t be able to choose their price!

As mentioned before, if you already have a wrench in your cart, we don’t insert a new row representing wrenches into our collection. We simply update the quantity value of the row that already exists via the update method:

The first parameter to the update method of a collection specifies the id of the row that you would like to update. The second parameter specifies what to actually do to the row specified by the first parameter. In this case, we increment the quantity field by one. This way, each time we click the wrench button, the quantity shown in our cart increases by one.

That’s everything that happens in the event handler for our wrench button. The event handlers for our hammer and screwdriver work in exactly the same way, and it isn’t necessary to discuss it here.

The only thing remaining for us to cover in InstantCart.js is the event handlers for the cart template. The event handlers in our cart all relate to the subtract button, which allow us to remove items from our cart. Just like the event handlers for adding items to our cart, the subtract button event handlers work exactly the same for all types items (wrenches, hammers, and screwdrivers). Therefore, we’re only going to cover the event handler for the subtract button of our wrench to avoid redundancy:

You may have noticed that the event handlers for our subtract buttons are very similar to the event handlers for adding items to the cart. We first store the single-element array of wrenches into a variable named wrenches via the find method. There’s one major difference between the add and subtract event handlers. The add event handler has to determine whether we should insert or update a row, whereas the subtract event handler determines whether we should remove or update a row. If the quantity of wrenches in our cart is greater than 1, then we simply subtract 1 from the quantity of wrenches. On the other hand, if we only had 1 wrench left in our cart when the remove button is clicked, then we completely remove the row for our wrenches from the Items collection:

The line of code above removes all rows from our Items collection that have their name field set to ‘Wrench’. This way, instead of being left with a row in which the number of wrenches is zero, the row in our cart for wrenches is completely removed. We could have left out this else clause, in which case we could even have negative wrenches displayed in the cart. This wouldn’t reflect reality (since it isn’t physically possible to buy a negative number of wrenches), so we prevent that from happening.

The event handlers for the subtract buttons of the screwdriver and hammer work exactly the same way, it is not necessary to explain them. The CSS code in the new version of our application is still exactly the same as the previous version, so there are no changes to discuss within the CSS.

Adding Styling/Formatting

At this point, we’ve completed adding all of the necessary features to our application. Of course, if this was a real shopping cart, we would also have a checkout process – although we aren’t going to be writing any code for processing credit card transactions. The InstantCart app is really only an educational example of how a real-time shopping cart could work by using Meteor. There are plenty of security issues that need to be taken into consideration (such as PCI compliance) before using any app for processing payment transactions. Therefore, I have kept our app limited to the cart itself.

Even though this is an article on Meteor, the app would feel a bit incomplete without adding at least a little bit of CSS styling. We won’t be going overboard with the CSS, but I did craft together something in order to improve the appearance just a little bit. It’s still not very professional looking, but it looks much better than it did before. We will be keeping our Javascript code exactly the same, so I’ll only be showing the HTML and CSS code. The HTML code is shown in Code Listing 6-1.

Code Listing 6-1 (InstantCart.html)

The HTML code has barely changed, we simply added line breaks (<br /> tags) and divisions (<div> tags) for formatting purposes. On the other hand, we’ve made plenty of changes to the CSS, which is shown in Code Listing 6-2.

Code Listing 6-2 (InstantCart.css)

The polished version of our app should look like Figure 6-1 after we have added some items to our cart.

Figure6-1

Figure6-1

I won’t be covering the CSS in detail, since this article has already grown much bigger than I had originally planned. There are plenty of resources on CSS if you lack any experience with it and are curious about how it works.

Deploying Our App

Deploying our app to the web is a piece of cake in Meteor. You may run it off of your own server, although for the purposes of this tutorial, we will be using Meteor’s server. In a terminal, go to the directory of your app (InstantCart) and execute the following command:

meteor deploy AppName.meteor.com

Replace AppName with whatever you would like to call it, and your app will be hosted there. Visit it in a browser and even share it with your friends. Now that it’s on the internet, it may be viewed by anybody anywhere (with an internet connection, of course). In my case, I chose to deploy my app to instantcart.meteor.com which you may visit in a browser.

Important! Shortly before this article was published, I received an email from meteor.com which stated that they will no longer be providing free hosting as of March 25, 2016. Therefore, you will no longer be able to host your app for free on Meteor’s server. Instead, they now provide a service named Galaxy which offers pay-as-you-go hosting. For more information, please visit https://www.meteor.com/galaxy

Now that we have built a custom app from start to finish in Meteor, think of ideas of simple apps that you could create with it. You could also add new features to the InstantCart app, such as a subtotal field (which would add up the quantities multiplied by the prices for each item). The best way to learn programming is to jump right in and do it. Keep learning and never stop having fun!








Leave a Reply

Your email address will not be published. Required fields are marked *