30
OSLO Meteor routing 28 September 2015

Meteor iron:router

Embed Size (px)

Citation preview

OSLO

Meteor routing28 September 2015

Iron Router

> meteor add iron:router

Concepts• Client and Server

Define a route that only should run on the server, or a route that should only run on the client

• ReactivityRoute functions and most hooks are run in a reactive computation

First route

Router.route('/', function () { this.render('Home'); });

Rendering Templates

<template name="Post"> <h1>Post: {{title}}</h1> </template>

Router.route('/post/:_id', function () { this.render('Post'); });

Rendering Templates with Data

Router.route('/post/:_id', function () { this.render('Post', { data: function () { return Posts.findOne({_id: this.params._id}); } }); });

Layouts<template name="ApplicationLayout"> <header> <h1>{{title}}</h1> </header>

<aside> {{> yield "aside"}} </aside>

<article> {{> yield}} </article>

<footer> {{> yield "footer"}} </footer> </template>

Router.route('/post/:_id', this.layout('ApplicationLayout');

// render the Post template into the "main" // {{> yield}} this.render('Post');

// render the PostAside into the yield "aside" // {{> yield "aside"}} this.render('PostAside', {to: 'aside'});

// render the PostFooter into the yield "footer" // {{> yield "footer"}} this.render('PostFooter', {to: 'footer'}); });

Setting Region Data ContextsRouter.route('/post/:_id', function () { this.layout('ApplicationLayout', { data: function () { return Posts.findOne({_id: this.params._id}) } });

this.render('Post', { data: function () { return Posts.findOne({_id: this.params._id}) });

this.render('PostAside', { to: 'aside', data: function () { return Posts.findOne({_id: this.params._id}) }); });

Route Parameters

// given the url: "/post/5?q=s#hashFrag" Router.route('/post/:_id', function () { var id = this.params._id; var query = this.params.query;

// query.q -> "s" var hash = this.params.hash; // "hashFrag" });

• Parameters• Query string • Hash

Using Links

With JavaScriptRouter.go('/one');

With RedirectsRouter.route('/one', function () { this.redirect('/two'); });

Links to Server RoutesRouter.route('/download/:filename', function () { this.response.end('some file content\n'); }, {where: 'server'});

<a href="/download/name">Download File</a>

Named Routes

Access by nameRouter.route('/posts/:_id', function () { this.render('Post'); }, { name: 'post.show' });

Router.routes[‘post.show’];

Router.go(‘post.show');

Router.go('post.show', {_id: 1}, {query: 'q=s', hash: 'hashFrag'});

Template Lookup

The router will by default look for a template named ItemsShow

Router.route('/items/:_id', {name: 'items.show'});

Router.setTemplateNameConverter(function (str) { return str; });

Set the converter

Path and Link Template Helpers

pathFor<a href="{{pathFor route='post.show' data=getPost query='q=s' hash='frag'}}">Post Show</a>

urlForFor route /posts/1 would generate http://mysite.com/posts/1

linkToThe linkTo helper automatically generates the html for an anchor tag along with the route path for the given route, parameters, hash and query.

{{#linkTo route="post.show" data=getData query="q=s" hash="hashFrag" class="my-cls"}}

SubscriptionsRouter.route('/post/:_id', function () { // add the subscription handle to our waitlist this.wait(Meteor.subscribe('item', this.params._id)); // OR this.subscribe('item', this.params._id).wait(); // OR

subscriptions: function() { // returning a subscription handle or an array of subscription handles return Meteor.subscribe('item', this.params._id); },

if (this.ready()) { this.render(); } else { this.render('Loading'); } });

The waitOn Option

Router.route('/post/:_id', { // this template will be rendered until the subscriptions are ready loadingTemplate: 'loading', waitOn: function () {

// return one handle, a function, or an array return Meteor.subscribe('post', this.params._id); },

action: function () { this.render('myTemplate'); } });

Same effect but automatically short-circuits your route action and any before hooks, and renders a loadingTemplate instead

Server RoutingRouter.route('/download/:file', function () { // NodeJS request object var request = this.request;

// NodeJS response object var response = this.response;

this.response.end('file download content\n'); }, {where: 'server'});

Restful Routes

Router.route('/webhooks/stripe', { where: 'server' }) .get(function () { // GET /webhooks/stripe }) .post(function () { // POST /webhooks/stripe }) .put(function () { // PUT /webhooks/stripe })

The waitOn Option

Router.route('/post/:_id', { // this template will be rendered until the subscriptions are ready loadingTemplate: 'loading', waitOn: function () {

// return one handle, a function, or an array return Meteor.subscribe('post', this.params._id); },

action: function () { this.render('myTemplate'); } });

Same effect but automatically short-circuits your route action and any before hooks, and renders a loadingTemplate instead

HooksHooks that are running in reactive computation, provide a way to plug into the process of running a route, typically to customize rendering behavior or perform some business logic

Router.onBeforeAction(function () { only: [‘admin'],

if (!Meteor.userId()) { this.render('Login'); } else { this.next(); } });

Available Hooks• onRun• onRerun

Must call this.next()

• onBeforeActionMust call this.next()

• onAfterAction• onStop

Controller

Betefits• Inheritance• Organization

PostController = RouteController.extend({ layoutTemplate: 'PostLayout',

template: 'Post',

waitOn: function () { return Meteor.subscribe('post', this.params._id);

},

data: function () { return Posts.findOne({_id: this.params._id}) },

action: function () { this.render(); } });

Iron.Router loading order

1. Route2. RouteController3. Router

Reactive State Variables

Router.route('/posts/:_id', {name: 'post'});

PostController = RouteController.extend({ action: function () { // set the reactive state variable "postId" with a value // of the id from our url this.state.set('postId', this.params._id); this.render(); } });

controller.state.get('postId')

Legacy Browser Support

http://localhost:3000/items/5?q=s#hashFrag

http://localhost:3000/#/items/5?q=s&__hash__=hashFrag

Router.route('/items/:_id', function () { var id = this.params._id; // "5" var query = this.params.query; // {q: "s"} var hash = this.params.hash; // "hashFrag" });

Route Options

Router.route('/post/:_id', { name: 'post.show', path: '/post/:_id', controller: 'CustomController', template: 'Post', layoutTemplate: 'ApplicationLayout', yieldRegions: { 'MyAside': {to: 'aside'}, 'MyFooter': {to: 'footer'} }, subscriptions: function() { this.subscribe('items'); // add the subscription to the waitlist this.subscribe('item', this.params._id).wait(); }, waitOn: function () { return Meteor.subscribe('post', this.params._id); }, //This function can also be used by hooks and plugins. data: function () { return Posts.findOne({_id: this.params._id}); }, onRun: function () {}, onRerun: function () {}, onBeforeAction: function () {}, onAfterAction: function () {}, onStop: function () {}, action: function () { // render all templates and regions for this route this.render(); } });

Global Default Options

Router.configure({ layoutTemplate: 'ApplicationLayout',

template: 'DefaultTemplate' });

Plugins

Router.plugin('dataNotFound', {notFoundTemplate: 'notFound'});

For Specific RoutesRouter.plugin('dataNotFound', { notFoundTemplate: 'NotFound', except: ['server.route'] // or only: ['routeOne', 'routeTwo'] });

Creating Plugins

Iron.Router.plugins.loading = function (router, options) { // this loading plugin just creates an onBeforeAction hook router.onBeforeAction('loading', options); };

Packages

meteor add multiply:iron-router-progress meteor add reywood:iron-router-ga joshowens:accounts-entry …

Questions?