18 min read

(For more resources related to this topic, see here.)

Modern web applications

Our world is changing.

With continual advancements in display, computing, and storage capacities, what wasn’t possible just a few years ago is now not only possible, but critical to the success of a good application. The Web in particular has undergone significant change.

The origin of the web app (client/server)

From the beginning, web servers and clients have mimicked the dumb terminal approach to computing, where a server with significantly more processing power than a client will perform operations on data (writing records to a database, math calculations, text searches, and so on), transform the data into a readable format (turn a database record into HTML, and so on), and then serve the result to the client, where it’s displayed for the user.

In other words, the server does all the work, and the client acts as more of a display, or dumb terminal. The design pattern for this is called…wait for it…the client/server design pattern:

This design pattern, borrowed from the dumb terminals and mainframes of the 60s and 70s, was the beginning of the Web as we know it, and has continued to be the design pattern we think of, when we think of the Internet.

The rise of the machines (MVC)

Before the Web (and ever since), desktops were able to run a program such as a spreadsheet or a word processor without needing to talk to a server. This type of application could do everything it needed to, right there on the big and beefy desktop machine.

During the early 90s, desktop computers got faster and better. Even more and more beefy. At the same time, the Web was coming alive. People started having the idea that a hybrid between the beefy desktop application (a fat app) and the connected client/server application (a thin app) would produce the best of both worlds. This kind of hybrid app — quite the opposite of a dumb terminal — was called a smart app.

There were many business-oriented smart apps created, but the easiest examples are found in computer games. Massively Multiplayer Online games (MMOs), first-person shooters, and real-time strategies are smart apps where information (the data model) is passed between machines through a server. The client in this case does a lot more than just display the information. It performs most of the processing (or controls) and transforms the data into something to be displayed (the view).

This design pattern is simple, but very effective. It’s called the Model View Controller (MVC) pattern.

The model is all the data. In the context of a smart app, the model is provided by a server. The client makes requests for the model from the server. Once the client gets the model, it performs actions/logic on this data, and then prepares it to be displayed on the screen. This part of the application (talk to the server, modify the data model, and prep data for display) is called the controller. The controller sends commands to the view, which displays the information, and reports back to the controller when something happens on the screen (a button click, for example). The controller receives that feedback, performs logic, and updates the model. Lather, rinse, repeat.

Because web browsers were built to be “dumb clients” the idea of using a browser as a smart app was out of the question. Instead, smart apps were built on frameworks such as Microsoft .NET, Java, or Macromedia (now Adobe) Flash. As long as you had the framework installed, you could visit a web page to download/run a smart app.

Sometimes you could run the app inside the browser, sometimes you could download it first, but either way, you were running a new type of web app, where the application could talk to the server and share the processing workload.

The browser grows up (MVVM)

Beginning in the early 2000s, a new twist on the MVC pattern started to emerge. Developers started to realize that, for connected/enterprise “smart apps”, there was actually a nested MVC pattern.

The server (controller) was performing business logic on the database information (model) through the use of business objects, and then passing that information on to a client application (a “view”).

The client was receiving this information from the server, and treating it as its own personal “model.” The client would then act as a proper controller, perform logic, and send the information to the view to be displayed on the screen.

So, the “view” for the server MVC was the “model” for the second MVC.

Then came the thought, “why stop at two?” There was no reason an application couldn’t have multiple nested MVCs, with each view becoming the model for the next MVC. In fact, on the client side, there’s actually a good reason to do so.

Separating actual display logic (such as “this submit button goes here” and “the text area changed value”) from the client-side object logic (such as “user can submit this record” and “the phone # has changed”) allows a large majority of the code to be reused. The object logic can be ported to another application, and all you have to do is change out the display logic to extend the same model and controller code to a different application or device.

From 2004-2005, this idea was refined and modified for smart apps (called the presentation model) by Martin Fowler and Microsoft (called the Model View View-Model). While not strictly the same thing as a nested MVC, the MVVM design pattern applied the concept of a nested MVC to the frontend application.

As browser technologies (HTML and JavaScript) matured, it became possible to create smart apps that use the MVVM design pattern directly inside an HTML web page. This pattern makes it possible to run a full-sized application directly from a browser. No more downloading multiple frameworks or separate apps. You can now get the same functionality from visiting a URL as you previously could from buying a packaged product.

A giant Meteor appears!

Meteor takes the MVVM pattern to the next level. By applying templating through handlebars.js (or other template libraries) and using instant updates, it truly enables a web application to act and perform like a complete, robust smart application.

Let’s walk through some concepts of how Meteor does this, and then we’ll begin to apply this to our Lending Library application.

Cached and synchronized data (the model)

Meteor supports a cached-and-synchronized data model that is the same on the client and the server.

When the client notices a change to the data model, it first caches the change locally, and then tries to sync with the server. At the same time, it is listening to changes coming from the server. This allows the client to have a local copy of the data model, so it can send the results of any changes to the screen quickly, without having to wait for the server to respond.

In addition, you’ll notice that this is the beginning of the MVVM design pattern, within a nested MVC. In other words, the server publishes data changes, and treats those data changes as the “view” in its own MVC pattern. The client subscribes to those changes, and treats the changes as the “model” in its MVVM pattern.

A code example of this is very simple inside of Meteor (although you can make it more complex and therefore more controlled if you’d like):

var lists = new Meteor.Collection("lists");

What this one line does is declare that there is a lists data model. Both the client and server will have a version of it, but they treat their versions differently. The client will subscribe to changes announced by the server, and update its model accordingly. The server will publish changes, and listen to change requests from the client, and update its model (its master copy) based on those change requests.

Wow. One line of code that does all that! Of course there is more to it, but that’s beyond the scope of this article, so we’ll move on.

To better understand Meteor data synchronization, see the Publish and subscribe section of the Meteor documentation at http://docs.meteor.com/#publishandsubscribe.

Templated HTML (the view)

The Meteor client renders HTML through the use of templates.

Templates in HTML are also called view data bindings. Without getting too deep, a view data binding is a shared piece of data that will be displayed differently if the data changes.

The HTML code has a placeholder. In that placeholder different HTML code will be placed, depending on the value of a variable. If the value of that variable changes, the code in the placeholder will change with it, creating a different view.

Let’s look at a very simple data binding – one that you don’t technically need Meteor for – to illustrate the point.

In LendLib.html, you will see an HTML (Handlebar) template expression:

<div id="categories-container"> {{> categories}} </div>

That expression is a placeholder for an HTML template, found just below it:

<template name="categories"> <h2 class="title">my stuff</h2>...

So, {{> categories}} is basically saying “put whatever is in the template categories right here.” And the HTML template with the matching name is providing that.

If you want to see how data changes will change the display, change the h2 tag to an h4 tag, and save the change:

<template name="categories"> <h4 class="title">my stuff</h4>...

You’ll see the effect in your browser (“my stuff” become itsy bitsy). That’s a template – or view data binding – at work! Change the h4 back to an h2 and save the change.

Unless you like the change. No judgment here…okay, maybe a little bit of judgment. It’s ugly, and tiny, and hard to read. Seriously, you should change it back before someone sees it and makes fun of you!!

Alright, now that we know what a view data binding is, let’s see how Meteor uses them.

Inside the categories template in LendLib.html, you’ll find even more Handlebars templates:

<template name="categories"> <h4 class="title">my stuff</h4> <div id="categories" class="btn-group"> {{#each lists}} <div class="category btn btn-inverse"> {{Category}} </div> {{/each}} </div> </template>

The first Handlebars expression is part of a pair, and is a for-each statement. {{#each lists}} tells the interpreter to perform the action below it (in this case, make a new div) for each item in the lists collection. lists is the piece of data. {{#each lists}} is the placeholder.

Now, inside the #each lists expression, there is one more Handlebars expression.

{{Category}}

Because this is found inside the #each expression, Category is an implied property of lists. That is to say that {{Category}} is the same as saying this.Category, where this is the current item in the for each loop. So the placeholder is saying “Add the value of this.Category here.”

Now, if we look in LendLib.js, we will see the values behind the templates.

Template.categories.lists = function () { return lists.find(...

Here, Meteor is declaring a template variable named lists, found inside a template called categories. That variable happens to be a function. That function is returning all the data in the lists collection, which we defined previously. Remember this line?

var lists = new Meteor.Collection("lists");

That lists collection is returned by the declared Template.categories.lists, so that when there’s a change to the lists collection, the variable gets updated, and the template’s placeholder is changed as well.

Let’s see this in action. On your web page pointing to http://localhost:3000, open the browser console and enter the following line:

> lists.insert({Category:"Games"});

This will update the lists data collection (the model). The template will see this change, and update the HTML code/placeholder. The for each loop will run one additional time, for the new entry in lists, and you’ll see the following screen:

In regards to the MVVM pattern, the HTML template code is part of the client’s view. Any changes to the data are reflected in the browser automatically.

Meteor’s client code (the View-Model)

As discussed in the preceding section, LendLib.js contains the template variables, linking the client’s model to the HTML page, which is the client’s view. Any logic that happens inside of LendLib.js as a reaction to changes from either the view or the model is part of the View-Model.

The View-Model is responsible for tracking changes to the model and presenting those changes in such a way that the view will pick up the changes. It’s also responsible for listening to changes coming from the view.

By changes, we don’t mean a button click or text being entered. Instead, we mean a change to a template value. A declared template is the View-Model, or the model for the view.

That means that the client controller has its model (the data from the server) and it knows what to do with that model, and the view has its model (a template) and it knows how to display that model.

Let’s create some templates

We’ll now see a real-life example of the MVVM design pattern, and work on our Lending Library at the same time. Adding categories through the console has been a fun exercise, but it’s not a long -t term solution. Let’s make it so we can do that on the page instead.

Open LendLib.html and add a new button just before the {{#each lists}} expression.

<div id="categories" class="btn-group"> <div class="category btn btn-inverse" id="btnNewCat">+</div> {{#each lists}}

This will add a plus button to the page.

Now, we’ll want to change out that button for a text field if we click on it. So let’s build that functionality using the MVVM pattern, and make it based on the value of a variable in the template.

Add the following lines of code:

<div id="categories" class="btn-group"> {{#if new_cat}} {{else}} <div class="category btn btn-inverse" id="btnNewCat">+</div> {{/if}} {{#each lists}}

The first line {{#if new_cat}} checks to see if new_cat is true or false. If it’s false, the {{else}} section triggers, and it means we haven’t yet indicated we want to add a new category, so we should be displaying the button with the plus sign.

In this case, since we haven’t defined it yet, new_cat will be false, and so the display won’t change. Now let’s add the HTML code to display, if we want to add a new category:

<div id="categories" class="btn-group"> {{#if new_cat}} <div class="category"> <input type="text" id="add-category" value="" /> </div> {{else}} <div class="category btn btn-inverse" id="btnNewCat">+</div> {{/if}} {{#each lists}}

Here we’ve added an input field, which will show up when new_cat is true. The input field won’t show up unless it is, so for now it’s hidden. So how do we make new_cat equal true?

Save your changes if you haven’t already, and open LendingLib.js. First, we’ll declare a Session variable, just below our lists template declaration.

Template.categories.lists = function () { return lists.find({}, {sort: {Category: 1}}); }; // We are declaring the 'adding_category' flag Session.set('adding_category', false);

Now, we declare the new template variable new_cat, which will be a function returning the value of adding_category:

// We are declaring the 'adding_category' flag Session.set('adding_category', false); // This returns true if adding_category has been assigned a value //of true Template.categories.new_cat = function () { return Session.equals('adding_category',true); };

Save these changes, and you’ll see that nothing has changed. Ta-daaa!

In reality, this is exactly as it should be, because we haven’t done anything to change the value of adding_category yet. Let’s do that now.

First, we’ll declare our click event, which will change the value in our Session variable.

Template.categories.new_cat = function () { return Session.equals('adding_category',true); }; Template.categories.events({ 'click #btnNewCat': function (e, t) { Session.set('adding_category', true); Meteor.flush(); focusText(t.find("#add-category")); } });

Let’s take a look at the following line:

Template.categories.events({

This line is declaring that there will be events found in the category template.

Now let’s take a look at the next line:

'click #btnNewCat': function (e, t) {

This line tells us that we’re looking for a click event on the HTML element with an id=”btnNewCat” (which we already created on LendingLib.html).

Session.set('adding_category', true); Meteor.flush(); focusText(t.find("#add-category"));

We set the Session variable adding_category = true, we flush the DOM (clear up anything wonky), and then we set the focus onto the input box with the expression id=”add-category”.

One last thing to do, and that is to quickly add the helper function focusText(). Just before the closing tag for the if (Meteor.isClient) function, add the following code:

/////Generic Helper Functions///// //this function puts our cursor where it needs to be. function focusText(i) { i.focus(); i.select(); }; } //------closing bracket for if(Meteor.isClient){}

Now when you save the changes, and click on the plus [ ] button, you’ll see the following input box:

Fancy!

It’s still not useful, but we want to pause for a second and reflect on what just happened. We created a conditional template in the HTML page that will either show an input box or a plus button, depending on the value of a variable.

That variable belongs to the View-Model. That is to say that if we change the value of the variable (like we do with the click event), then the view automatically updates. We’ve just completed an MVVM pattern inside a Meteor application!

To really bring this home, let’s add a change to the lists collection (also part of the View-Model, remember?) and figure out a way to hide the input field when we’re done.

First, we need to add a listener for the keyup event. Or to put it another way, we want to listen when the user types something in the box and hits Enter. When that happens, we want to have a category added, based on what the user typed. First, let’s declare the event handler. Just after the click event for #btnNewCat, let’s add another event handler:

focusText(t.find("#add-category")); }, 'keyup #add-category': function (e,t){ if (e.which === 13) { var catVal = String(e.target.value || ""); if (catVal) { lists.insert({Category:catVal}); Session.set('adding_category', false); } } } });

We add a “,” at the end of the click function, and then added the keyup event handler.

if (e.which === 13)

This line checks to see if we hit the Enter/return key.

var catVal = String(e.target.value || ""); if (catVal)

This checks to see if the input field has any value in it.

lists.insert({Category:catVal});

If it does, we want to add an entry to the lists collection.

Session.set('adding_category', false);

Then we want to hide the input box, which we can do by simply modifying the value of adding_category.

One more thing to add, and we’re all done. If we click away from the input box, we want to hide it, and bring back the plus button. We already know how to do that inside the MVVM pattern by now, so let’s add a quick function that changes the value of adding_category. Add one more comma after the keyup event handler, and insert the following event handler:

Session.set('adding_category', false); } } }, 'focusout #add-category': function(e,t){ Session.set('adding_category',false); } });

Save your changes, and let’s see this in action! In your web browser, on http://localhost:3000 , click on the plus sign — add the word Clothes and hit Enter.

Your screen should now resemble the following:

Feel free to add more categories if you want. Also, experiment with clicking on the plus button, typing something in, and then clicking away from the input field.

Summary

In this article you’ve learned about the history of web applications, and seen how we’ve moved from a traditional client/server model to a full-fledged MVVM design pattern. You’ve seen how Meteor uses templates and synchronized data to make things very easy to manage, providing a clean separation between our view, our view logic, and our data. Lastly, you’ve added more to the Lending Library, making a button to add categories, and you’ve done it all using changes to the View-Model, rather than directly editing the HTML.

 

Resources for Article :


Further resources on this subject:


LEAVE A REPLY

Please enter your comment!
Please enter your name here