FlowRouter and FlowLayout 101

WHAT WE'LL BE LEARNING

In this tutorial, we'll learn how to use meteorhacks:flow-router and meteorhacks:flow-layout in our application. We we'll also learn how to create a meteor appplication, adding and removing packages, organizing files and folders and more.

Flow Router is a minimalistic router which only handles routing and subscriptions. You can't have any kind of reactive code inside the router. But there is a rich reactive API to watch changes in the routes.

Flow Layout is a layout manager designed for flow architecture. But, this can be used without other layers of flow.

WHAT WE'LL BE BUILDING

A simple blog application, made by only two pages (and two routes). A home page that shows a list of posts, and a post page that show only a single post.

Getting Started

If you have not meteor installed, go to https://www.meteor.com/install.

To get started, we just need to create a new meteor application, open your terminal and type

$ meteor create flow-my-blog
$ cd flow-my-blog

When creating a new application with meteor create, Meteor automatically add two packages: insecure and autopublish, let's see in detail.

The insecure package, allow almost all collection methods, such as insert, update, and remove, to be called from the client. This package is useful for prototyping an app without worrying about database permissions, but should be removed as soon as the app needs to restrict database access.

The autopublish package, publish all server collections to the client. This package is useful for prototyping an app without worrying about which clients have access to certain data, but should be removed as soon as the app needs to restrict which data is seen by the client.

We don't need both of them.

~/flow-my-blog$ meteor remove insecure autopublish
~/flow-my-blog$ meteor add meteorhacks:flow-router meteorhacks:flow-layout

The arillo:flow-router-helpers package, are Template helpers for meteorhacks:flow-router.

In this tutorial, we'll be using the {{pathFor}} helper, used to build a path to a route.

For more info, take a look at the README.

~/flow-my-blog$ meteor add arillo:flow-router-helpers
Structuring our application

By default, any JavaScript files in your Meteor folder are bundled and sent to the client and the server. However, the names of the files and directories inside your project can affect their load order, where they are loaded, and some other characteristics.

http://docs.meteor.com/#/full/structuringyourapp

This will be the our Project Structure

flow-my-blog/  
├── client
│   └── templates
│       ├── header.html
│       ├── header.js
│       ├── layout.html
│       ├── posts-list.html
│       ├── posts-list.js
│       ├── single-post.html
│       └── single-post.js
├── lib
│   ├── posts.js
│   └── routes.js
└── server
    ├── fixtures.js
    └── publications.js

Files in /lib folder, are loaded in both client and server side, and before the other files: this is the right place for our collections and routes definition.

COLLECTION

Our Posts collection.

// file /lib/posts.js
Posts = new Mongo.Collection('posts');  
FIXTURES

We need some fake data to display, we'll be writing a simple function that generate some random data, and then we'll be inserting the JSON object into the Post collection using Posts.insert().

// file: /server/fixtures.js
function randomDate(start, end) {  
    return new Date(start.getTime() + Math.random() * (end.getTime() - start.getTime()));
}

Meteor.startup(function () {  
  if (Posts.find().count() === 0) {
    _(5).times(function (n) {
      Posts.insert({
        title: 'Title ' + n,
        body: 'Content body ' + n,
        createdAt: randomDate(new Date(2015, 0, 1), new Date())
      });
    });
  }
});
PUBLICATIONS

Remember that we've removed the autopublish package before? This means our Posts collection data is not available in the client side.
We need to publish the data we want, so that they are available when we subscribe.

// file: /server/publications.js
Meteor.publish('allPosts', function () {  
  return Posts.find({});
});

Meteor.publish('singlePost', function (postId) {  
  return Posts.find({ _id: postId });
});
ROUTES

Our Routes definitions.

// file: /lib/routes.js
FlowRouter.route('/', {  
  subscriptions: function (params, queryParams) {
    this.register('posts', Meteor.subscribe('allPosts'));
  },
  action: function (params, queryParams) {
    FlowLayout.render('layout', { top: 'header', main: 'postsList' });
  },
  name: 'home'
});

FlowRouter.route('/blog/:postId', {  
  subscriptions: function (params, queryParams) {
    this.register('post', Meteor.subscribe('singlePost', params.postId));
  },
  action: function (params, queryParams) {
    FlowLayout.render('layout', { top: 'header', main: 'singlePost' });
  },
  name: 'blog'
});

As you can see, and as Arunoda Susiripala - the creator of FlowRouter and FlowLayout - has written, it's minimalistic and handles only routing and subscriptions.

LAYOUT

We also need a layout template where to show our templates: header, postsList and singlePost.

<!-- file: /client/layout.html -->  
<template name="layout">  
  {{> Template.dynamic template=top}}
  {{> Template.dynamic template=main}}
</template>  

Flow Router has rich API to help you to navigate the router and reactively get information from the router.

To get the name of the route reactively, we'll be using the FlowRouter.getRouteName() API.

// file: /client/header.js
Template.header.helpers({  
  routeName: function () {
    return FlowRouter.getRouteName();
  }
});

In the header template, we want - also - the ability to go back to the home route /: here it is where {{pathFor 'routeName'}} helper form the arillo:flow-router-helpers come in handy.
In our case the home route is named home.

<!-- file: /client/header.html -->  
<template name="header">  
  <h1>Flow my blog</h1>
  <h2>You are in <i>{{routeName}}</i> route</h2>
  <ul>
    <li><a href="{{pathFor 'home'}}">Home</a></li>
  </ul>
</template>  
POSTS LIST

We'll be retrieving all the Posts from the Minimongo collection, stored in the client side.

// file: /client/posts-list.js
Template.postsList.helpers({  
  getPosts: function () {
    return Posts.find({}, { sort: { createdAt: -1 }});
  }
});

Then in the template, we'll be using the block helper {{#each}}{{/each} to iterate over a database cursor.
As you can see, {{pathFor}} accepts also parameters, in this case the id of the post.

<!-- file: /client/posts-list.html -->  
<template name="postsList">  
  {{#if isSubReady 'posts'}}
    {{#each getPosts}}
      <h2>{{title}}</h2>
      <p>{{body}}</p>
      <a href="{{pathFor 'blog' postId=_id}}">Link to post</a>
      <hr>
    {{/each}}
  {{else}}
    Loading...
  {{/if}}
</template>  
SINGLE POST

Same story here but with a single record to show.

// file: /client/single-post.js
Template.singlePost.helpers({  
  getPost: function () {
    return Posts.findOne();
  }
});
<!-- /client/single-post.html -->  
<template name="singlePost">  
  {{#if isSubReady 'post'}}
    {{#with getPost}}
    <h2>{{title}}</h2>
    <p>{{body}}</p>
    {{/with}}
  {{else}}
    Loading...
  {{/if}}
</template>  
RESOURCES

If you want to use the meteorhacks:flow-router package in your new application, or want to replace the actual you are using please consider to take a look to:

FlowRouter 2 is coming soon, take a look at this GitHub issue, and if you want to know more about Arunoda Susiripala take a look at his AMA on creater.io.

Enjoy coding!