IronRouter

человек и пароход

Что будет в докладе

 

server-client. что выбрать?

layouts. разметка одна, страниц много

helpes. какие же удобные штуки

subscriptions. Без никуда или "капитан реактивность"

plugins. просто, юзабельно, охрененно

hooks. уровень погружения - 2

route controllers. глубже некуда

Основы

meteor add iron:router

<template name="Post">
  <h1>Post: {{title}}</h1>
</template>
Router.route('/post/:_id', function () {
  this.render('Post');
});

.html

.js

Несколько уровней абстракции:

Route - класс обработки конкрентного пути

RouterController - название говорит само за себя

Router - одно месте, через которое все работает (в хорошем смысле)

server-client

Router.route('/post/:_id', function () {
  var params = this.params; // { _id: "5" }
  var id = params._id; // "5"
}, {where: 'client'}); //default

Router.route('/post/:_id', function () {
  var params = this.params; // { _id: "5" }
  var id = params._id; // "5"
}, {where: 'server'});

Реактивность!

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');
    this.subscribe('item', this.params._id).wait();
  },
  waitOn: function () {
    return Meteor.subscribe('post', this.params._id);
  },
  data: function () {
    return Posts.findOne({_id: this.params._id});
  },
  onRun: function () {},
  onRerun: function () {},
  onBeforeAction: function () {},
  onAfterAction: function () {},
  onStop: function () {},
  action: function () {
    this.render();
  }
});
Router.route(
        '/webhooks/stripe', 
        { where: 'server' }
    )
  .get(function () {
    // GET /webhooks/stripe
  })
  .post(function () {
    // POST /webhooks/stripe
  })
  .put(function () {
    // PUT /webhooks/stripe
  })

layouts

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

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

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

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

<template name="PostFooter">
  Some post specific footer content.
</template>

<template name="PostAside">
  Some post specific aside content.
</template>
Router.route('/post/:_id', function () {
  this.layout('ApplicationLayout');
});

Router.configure({
  layoutTemplate: 'ApplicationLayout'
});

Router.route('/post/:_id', function () {
  this.layout('ApplicationLayout');
  this.render('Post');
  this.render('PostAside', {to: 'aside'});
  this.render('PostFooter', {to: 'footer'});
});

helpers

<template name="Post">
  <p>
    {{post_content}}
  </p>

  {{#contentFor "aside"}}
    Some post specific aside content.
  {{/contentFor}}

  {{#contentFor "footer"}}
    Some post specific footer content.
  {{/contentFor}}
</template>

<template name="Post">
  <p>
    {{post_content}}
  </p>

  {{> contentFor region="aside" template="PostAside"}}

  {{> contentFor region="footer" template="PostFooter"}}
</template>
<template name="pathForExample">
    {{#with posts}}
      <a href="{{pathFor route='post.show'}}">
        {{postName}}
      </a>
    {{/with}}
</template>

<template name="linkToExample">
    {{#linkTo 
            route="post.show" 
            data=getData 
            query="q=s" 
            hash="hashFrag" 
            class="my-cls"
    }}
      <span style="color: orange;">
        Post Show
      </span>
    {{/linkTo}}
</template>

subscriptions

Router.route('/post/:_id', function () {
  this.wait(Meteor.subscribe('item', this.params._id));

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

Router.route('/post/:_id', {
  loadingTemplate: 'loading',

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

  action: function () {
    this.render('myTemplate');
  }
});
Router.route('/post/:_id', {
  subscriptions: function() {
    return Meteor.subscribe('item', this.params._id);
  },

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

Session.set/Session.get, ReactiveVar

plugins

Iron.Router.plugins.loading = function (router, options) {
  // this loading plugin just creates an onBeforeAction hook
  router.onBeforeAction('loading', options);
};
Router.plugin('dataNotFound', {
  notFoundTemplate: 'NotFound', 
  except: ['server.route']
  // or only: ['routeOne', 'routeTwo']
});

hooks

Router.onBeforeAction(function () {
  if (!Meteor.userId()) {
    this.render('Login');
  } else {
    this.next();
  }
});
Router.onBeforeAction(myAdminHookFunction, {
  only: ['admin']
});
  • onRun

  • onRerun

  • onBeforeAction

  • onAfterAction

  • onStop

route controllers

PostController = RouteController.extend();

Router.route('/post/:_id', {
  name: 'post.show',
  controller: 'PostController'
});

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.RouteController

  • Наследование: Можно наследоваться от этого класса. И создавать правила обработки для "группы" маршрутов.
  • Организация кода: Можно разделять логику маршрутизации в разных контроллерах и использовать несколько файлов и не хранить все в router.js

Итого

  • IronRouter - не часть платформы Meteor,но де-факто стал его сердцем
  • Расширяемый, имеет несколько уровней абстракции и возможность подключения плагинов
  • Легок в освоении, простая документация
  • Его можно заменить (таких не встречал)

Напутствие:

Попробуйте все возможности и выберите свою стратегию работы с роутером.

Не плодите лапшу!

Спасибо!

Iron Router

By lawrentiy

Iron Router

  • 1,985