Reactive programming and data in Meteor.js

Meteor uses NoSQL document-oriented storage and it comes by default with Mongo DB. The name comes form the word "humongous", meaning extremely large. The database is part of a NoSQL database family, meaning, it does not store the data as traditional relational databases. Mongo DB persists the date in a JSON-like document format, making the integration with JavaScript-based frameworks a lot easier. In this recipe, we are going to see how to use the database from Meteor, and how data access is orchestrated.

Getting ready

There is an icon.png image in the example files; besides that, only Meteor is needed to be installed on your machine and to have an open command line.

How to do it...

  1. First we can start by creating the application named movies from the command line:
    >meteor create movies
    

    To simplify the generated structure, we will create two folders: one called server and another called client. The movies.css, movies.js, and movies.html files can be placed in the client directory, as we will putting client-related code there.

  2. In the server directory, we create a file called bootstrap.js that will initialize the database with the few objects that we will define:
    Meteor.startup(function () {
      if (Movies.find().count() === 0) {
        var data = [
          {
            name: "North by northwest",
            score: "9.9"
          },
          {
            name: "Gone with the wind",
            score:"8.3"
          },
          {
            name: "1984",
            score: "9.9"
          }
        ];
    
        var timestamp = (new Date()).getTime();
        for (var i = 0; i < data.length; i++) {
          var itemId = Movies.insert({
            name: data[i].name,
            score: data[i].score,
            time: timestamp
          });
        }
      }
    });
  3. The first thing you might be wondering is, what is this Movies object? It is a collection that we will define in a different file that can be called publish.js, as in it we are going to publish the collection from the server. This file will include the following:
    Movies = new Meteor.Collection("movies");
    Meteor.publish('movies', function () {
      return Movies.find();
    });
  4. As for the client side, we have the generated files, so we start creating a simple HTML and a handlebar template to go with it. Inside the template, we will just iterate over Movies and print out a list of elements with the movie name and score. Additionally, in the template, we place button that will contain reference to an image:
    <body>
      <div id="main">
          {{> movies}}
      </div>
    </body>
    
    <template name="movies">
      <h3>List of favorite movies</h3>
      <div id="lists">
        <div>
          <ul>
            {{#each movies}}
              <li><b>{{name}}</b>  {{score}}<li/>
            {{/each}}
          </ul>
          <button>
            <img src="icon.png" width="30px" height="30px" />
          </button>
        </div>
      </div>
    </template>

    In order to make the icon.png image available as a static file, we need to create a folder named public and place the image there. This follows the principle of convention over configuration, and there is no real need why you should not follow it, at least most of the time.

  5. As for the client side, in the previously generated movies.js file, we should automatically subscribe to the servers collection of movies. Also, we will add a functionality to fill the movies variable and add an event also for the button that will trigger a save of a random new movie:
    // Define mongo style collections to match server/publish.js.
    Movies = new Meteor.Collection("movies");
    
    // Always be subscribed to the movies list.
    Meteor.autorun(function () {
        Meteor.subscribe('movies'),
    });
    
    // fill the movies variable with data from the collection sorted by name
    Template.movies.movies = function () {
      return Movies.find({}, {sort: {name: 1}});
    };
    
    // on click we insert a random movie
    Template.movies.events({
      'click button': function(){
        Movies.insert({
          name: "random awesome movie",
          score: Math.random() * 10
        });
      }
    });
  6. Now everything should be working. After starting the application with meteor, we can access it in the browser on the default port http://localhost:3000/. If we want to change the port on which the application runs, for example on port 3333, we can use the following command:
    meteor --port 3333
    

How it works...

We can first start with the data, if we have the server running, we can open up another console, where we can access the same directory. Then, after opening the same folder in the console, we run the following command:

meteor mongo
MongoDB shell version: 2.2.3
connecting to: 127.0.0.1:3002/meteor

That opens up a simple console on which we can query our database. Mongo stores the data as collections, and in order to get the names of all of our available movies, we can use the following command:

> db.getCollectionNames()
[ "movies", "system.indexes" ]

The movies collection is the one we defined in our bootstrap.js initialization; as for system.indexes, it is a collection contains all the indexes of the database. To manipulate data with this collection, we can use ensureIndex() and dropIndex().

In the console, we can assign the following variables:

> var x = db.getCollection("movies");
> x
meteor.movies

Collections can be queried with find(); if we try to call it without arguments, it returns all the elements:

> x.find();
{ "name" : "North by northwest", "score" : "9.9", "time" : 1360630048083, "_id" : "bc8f1a7a-71bd-49a9-b6d9-ed0d782db89d" }
{ "name" : "Gone with the wind", "score" : "8.3", "time" : 1360630048083, "_id" : "1d7f1c43-3108-4cc5-8fbf-fc8fa10ef6e2" }
{ "name" : "1984", "score" : "9.9", "time" : 1360630048083, "_id" : "08633d22-aa0b-454f-a6d8-aa2aaad2fbb1" }
...

The data is basic JSON, making it easy to manipulate with JavaScript. If you take a look at the objects, you can notice the "_id" : "08633d22-aa0b-454f-a6d8-aa2aaad2fbb1" key-value pair. This is a unique key generated by Mongo and we use it to reference and manipulate that object, commonly referred to as document.

If we wanted to delete the record with an ID of beef20a3-c66d-474b-af32-aa3e6503f0de, we can use the following command:

> db.movies.remove({"_id":"beef20a3-c66d-474b-af32-aa3e6503f0de"});

After that, we can call db.movies.find() to see to notice that one is now missing. There are plenty of other commands used for data manipulation, but most of them are intuitive, and you can easily guess by their names. As a quick reminder and a learning tool, there is a help function that can be called:

>help
>db.help()

These two bring up a list of commands and a short explanation of what each does. You should not get overwhelmed by the number of commands, as we will not use most of them, but it still makes a good reference.

Note

For a more detailed tutorial on MongoDB commands, visit http://mongodb.org and click on TRY IT OUT to try the online shell. There are tones of resources for NoSQL on the Web, but one great introduction done by Martin Flower is available at http://www.youtube.com/watch?v=qI_g07C_Q5I.

If we open a browser, we may notice that on every click on the Random button, a new record is added instantly. This looks extremely fast and it is not just because the server is running locally. Every time a client issues a write to the server, it instantly updates the local cache without response from the server if the write went successful. When the server receives the request and accepts the update, then the client does not have to do anything on the screen. This should happen most of the time and it saves the round trip waiting time, making the screen more responsive. On the other hand, if the server rejects the update, the client's cache gets updated with the correct result.

In Meteor, the same API is used for the client and the server in order to access the database. Emphasis is given to reducing the time for round trips to the server for every design decision in the framework. Requests and responses, as well as message invalidation, are orchestrated to do this.

We used autorun to automatically get updates from the server in our movies.js:

Meteor.autorun(function () {
    Meteor.subscribe('movies'),
});

The block of code in the autorun function is a so-called reactive context, enabling us to write code in an imperative style, but get a reactive behavior.

Reactive programming is one of the programming paradigms that are oriented around propagation of change. In imperative programming, if we have an expression such as z = x + y, this means that the result of the evaluation of x + y will be assigned to z as expected. For example, if we have x = 42 and y = 13, then z = 42 + 13 or z = 55. The values of x and y can be changed later, for example, they can be changed to x=4 and y=4, but this will not affect z in any way, it will still be 55.

The simplest example for this is a modern spreadsheet program, such as Microsoft Excel or Google docs spreadsheet. Spreadsheet cells commonly contain literal values such as number for example, or can contain formulas that have derived values from some other cells. In our cell C3 we could have the formula "=A1+B1" meaning when we change some of the values in A1 or B1, C3 will get autoupdated.

In MVC architecture, a simplification can be made using reactive programming, where we automatically propagate the changes from the view towards the model and back that can be very beneficial in real-time systems.

The use of reactive contexts saves us from writing a whole class of calls. In our example, we would first need to unsubscribe when something has changed and then again resubscribe to get the data back from the server. This reduces a substantial amount of code that could end up being error-prone and add more complexity in the maintenance phase.

Note

Besides Meteor.autorun, reactive context is applied in Templates and in the Meteor.render and Meteor.renderList functions.

As for the data sources that can trigger changes, we can use database collections and session variables, and a few other functions related to authentication and authorization. You can find more details about it in the documentation of Meteor about reactivity at http://docs.meteor.com/#reactivity.

If you open up two different browsers side by side, you may notice that the same data is shown even if the sessions are different. In order to have user-specific data, we will create an example in the next recipe.

You might wish to send the entire collection to the client, but first think thoroughly if that is what the client actually needs. Often, it might be a lot wiser to send only certain fields rather than entire documents. In order to lower the network traffic, certain parts of the client can have subscription turned off and the documents for those parts will be removed from the local cache unless used in other active subscription.

There's more...

Because the data we used is stored in the database, if we changed the data there using some external application, it will also trigger changes to the client. In the next recipe, we will see how we can allow multiple users to have their own favorite list for each of them instead of a single global list.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.138.67.27