Organização do código Meteor

Algumas “idéias atuais” sobre a melhor forma de usar o Meteor ( https://www.meteor.com/):

Pacotes

Use o Meteor 0.9, que inclui os recursos de gerenciamento de pacote encontrados anteriormente no Meteorite.

Alguns pacotes úteis (instalar com meteor add)

Organização de código

Aqui está um layout de código simples que se ajusta à minha maneira de pensar:

.
├── client/ Client-side-only assets
   ├── layout.html Defines an overall page layout for routed pages
   ├── notfound.html A default "not found" page for routed requests
   ├── *.html Top level pages rendered by the router
   ├── components/ Shared templates used by top level pages
      ├── *.html
   ├── css/ Custom stylesheets
      └── *.css
   ├── js/ Client-side JavaScript logic
      ├── *.js Template helpers and events for components
      └── startup.js `Meteor.startup()` and `iron-router` configuration
├── lib/ Shared library code, loads early
   └── _namespaces.js Defines global namespaces
├── models.js Shared model/collection definitions
├── server/ Server-side-only methods
   └── *.js

lib/_namespaces.jsdefine namespaces de nível superior. Consulte https://coderwall.com/p/tk5yug . Por exemplo:

/* global Models: true, Collections: true, Schemata: true */

// This file exists so that we can do "use strict" in all other files and
// still have some global namespace variables.

// It is called what it's called and placed where it's placed so that it loads
// as early as possible.

Schemata = {};
Collections = {};

models.js(ou possivelmente um diretório de arquivos se houver muitos modelos) define coleções e esquemas. Por exemplo:

/* global Collections, Schemata, SimpleSchema */
"use strict";

Schemata.MyModel = new SimpleSchema({

...

});


Collections.MyModels = new Meteor.Collection("MyModels", {
schema
: Schemata.MyModel
});

if(Meteor.isServer) {
Meteor.publish("models", function() {
return Collections.MyModels.find({});
});
} else if(Meteor.isClient) {
Meteor.subscribe("models");
}

Aqui está um exemplo de um client/layout.htmlque usa um modelo Bootstrap 3 simples:

<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">

<title>Analysator</title>

<!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>

<script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>

<![endif]-->

</head>

<template name="layout">

{{> header }}


<div class="container-fluid">

<div class="row">
<div class="col-sm-2 col-md-2 sidebar">
{{> sidebar }}

</div>
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
{{> yield }}

</div>
</div>

</div>

{{> yield region='footer'}}


</template>

e um client/notfound.html:

<template name="notFound">
<h1 class="page-header">Not found</h1>
<p>
The page you are looking for cannot be found.

</p>
</template>

Os vários componentes gostam headere sidebarsão definidos em client/components/*.htmlcomo modelos separados, com lógica associada (por exemplo, Template.[name].helpers()e Template.[name].events) em client/js/*.js.

Outras páginas em client/*.htmlcontêm páginas alternativas. Esses são navegados para usar iron-router. A configuração está em client/js/startup.js. Aqui está um exemplo que direciona para duas páginas, força o login em qualquer coisa, exceto na página inicial e tem um manipulador do tipo 404:

/* global Collections */
"use strict";

Meteor.startup(function() {

// any initialisation here

});

Router.map(function() {

// if `data` returns undefined/null, redirect to the `notFoundTemplate`.
Router.onBeforeAction('dataNotFound');
Router.configure({
layoutTemplate
: 'layout',
notFoundTemplate
: 'notFound'
});

// force login
Router.onBeforeAction(function(pause) {
var self = this;

if (!this.ready()) {
return;
}

var userId = Meteor.userId(),
currentRoute
= Router.current();

// Redirect to home page if user is not logged in
if(currentRoute && currentRoute.route.name !== 'home' && !userId) {
pause
();
Router.go('home');
return;
}
});

// Routes

this.route('home', {
path
: '/',
data
: {}
});

this.route('new', {
path
: '/new-entry',
template: 'edit-entry',
yieldTemplates
: {
configurationModal
: {to: 'footer'}
},
onRun
: function() {
// any preparation, e.g. update Session
},
data
: function() {
// return a blank object
return {foo: null, bar: null};
}
});

this.route('entry', {
path
: '/entry/:_id',
template: 'edit-entry',
yieldTemplates
: {
configurationModal
: {to: 'footer'}
},
onRun
: function() {
// any preparation, e.g. update Session
},
data
: function() {
// find the data object
return Collections.MyModels.findOne(this.params._id);
}
});
});

Configuração do editor