Então, analisei como construir um aplicativo barebones, funcional e fullstack com meteoro em Como construir um aplicativo usando Meteor.js . Dito isso, não é impressionante. Na semana passada, fiz o excelente curso do Eventedmind sobre como construir um aplicativo de várias páginas com meteoro de ferro . É um pouco como construir um blog barebones completo, com autenticação.
O DEMO está aqui. Eu usei a implantação embutida de meteoros.
SOURCE CODE está nos githubs
Primeiramente
Em vez de copiar o tutorial passo a passo do evento mental, irei apontar algumas partes interessantes de como o aplicativo funciona.
O aplicativo é estruturado usando a ferramenta de linha de comando Iron. O ferro é como o yeoman, mas para aplicativos de meteoros. Se isso não ajudar, ignore. Para lidar com o roteamento com meteoros, você provavelmente usará o Iron Router. Este é um andaime de ferro. É uma forma opinativa e estruturada de construir seus aplicativos de meteoros. Para construir um esqueleto vazio você vai como:
$ npm install -g iron-meteor
$ git clone <repo_url>
$ cd meteor-todos
$ iron
Em seguida, adicionaremos pacotes úteis e removeremos alguns de baixa qualidade.
$ iron add less
$ iron add bootstrap
$ iron add accounts-ui
$ iron add accounts-meteor-developer
$ iron add momentjs:moment
$ iron remove autopublish
$ iron remove insecure
As contas-interface do usuário e contas-desenvolvedor-meteoro eliminam toda a dor de cabeça de autenticação. Existem também pacotes accounts-facebook, accounts-google, accounts-twitter. Os dois últimos vêm por padrão e queremos desativá-los. O Insecure permite que você grave no banco de dados a partir do console (não quero isso) e a publicação automática torna todos os dados acessíveis. O código do tutorial usa as práticas recomendadas.
Estrutura
Todo o código do aplicativo está na pasta do aplicativo . Este é exatamente o código que seria gerado pela execução meteor create my_app_name
. Se você quiser executar qualquer meteor
comando, faça cd na pasta do aplicativo e pronto. (É exatamente assim que implantei [ $ cd app $ meteor deploy
]).
Os principais pastas dentro de aplicativo que se preocupar são client
, lib
e server
. Conforme especificado na documentação, o cliente funciona apenas no cliente, o servidor apenas no servidor e a lib é executada em ambos.
lib
Vou começar com lib, porque é onde nossas rotas são definidas. Eles parecem:
Router.configure({
layoutTemplate: 'MasterLayout',
loadingTemplate: 'Loading',
notFoundTemplate: 'NotFound'
});
Router.route('/', {
name: 'home',
controller: 'HomeController',
action: 'action',
where: 'client'
});
Router.route('/todos/:_id', {
name: 'todos.detail',
controller: 'TodosController',
action: 'detail',
where: 'client'
});
Router.route('/todos/:_id/edit', {
name: 'todos.edit',
controller: 'TodosController',
action: 'edit',
where: 'client'
});
Router.route('/users/:_id', {
name: 'users.detail',
controller: 'UsersController',
action: 'detail',
where: 'client'
});
Em meu post anterior sobre meteoro, não usamos controladores, agora usamos. O controlador tem esta aparência:
TodosController = RouteController.extend({
subscriptions: function () {
this.subscribe('todoDetail', this.params._id);
},
// set data context for controller
data: function () {
return Todos.findOne({_id: this.params._id});
},
detail: function(){
this.render('TodosDetail', {});
},
edit: function(){
// reactive state variable saying we're in edit mode
this.state.set('isEditing', true);
this.render('TodosDetail');
}
});
E, finalmente, temos uma coleção em :app/collections/todos.js
Todos = new Mongo.Collection('todos');
// if server define security rules
// server code and code inside methods are not affected by allow and deny
// these rules only apply when insert, update, and remove are called from untrusted client code
if (Meteor.isServer) {
// first argument is id of logged in user. (null if not logged in)
Todos.allow({
// can do anythin if you own the document
insert: function (userId, doc) {
return userId === doc.userId;
},
update: function (userId, doc, fieldNames, modifier) {
return userId === doc.userId;
},
remove: function (userId, doc) {
return userId === doc.userId;
}
});
// The deny method lets you selectively override your allow rules
// every deny callback must return false for the database change to happen
Todos.deny({
insert: function (userId, doc) {
return false;
},
update: function (userId, doc, fieldNames, modifier) {
return false;
},
remove: function (userId, doc) {
return false;
}
});
}
Isso é um monte de código, mas não é para o que faz.
A coleção de tarefas cria uma coleção do MongoDB no banco de dados que é acessível na Todos
variável global no cliente e no servidor. Os callbacks de permissão e negação lidam com as permissões e segurança do nosso banco de dados.
Servidor
A coisa mais legal do servidor é o código que publica nossos dados:
/**
* Meteor.publish('items', function (param1, param2) {
* this.ready();
* });
*/
var allUsersCursor = Meteor.users.find({}, { fields: { profile: 1 }});
var getCursorForUser = function(id){
return Meteor.users.find({_id: id}, {fields: { profile: 1 }})
};
Meteor.publish('todos', function () {
// no data published if you're not logged in
if(!this.userId) return this.ready();
// only allow people to see their own todos
// this is currently logged in user
return Todos.find({userId: this.userId});
});
Meteor.publish('todoDetail', function (id) {
if(!this.userId) return this.ready();
var todo = Todos.findOne({ _id:id });
// get cursors for user who owns todo and todo itself
// fields specifies what to make available
return [
getCursorForUser(todo.userId),
Todos.find({_id: id}),
Comments.find({todoId: id}, { sort: {createdAt: 1}})
];
});
Meteor.publish('users', function (/* args */) {
if(!this.userId) return this.ready();
// publish all users but specify which fields to make available
return allUsersCursor;
});
Meteor.publish('user', function (userId) {
if(!this.userId) return this.ready();
// publish user data and their todos
return [
getCursorForUser(userId),
Todos.find({userId: userId})
];
});
No meteoro, você especifica no servidor quais dados tornar acessíveis por meio das funções de publicação. No cliente, você assina esses dados. Lidamos com nossas assinaturas no controlador. Portanto, role para cima e verifique o código do controlador. Você vai ver:
subscriptions: function () {
this.subscribe('todoDetail', this.params._id);
},
// set data context for controller
data: function () {
return Todos.findOne({_id: this.params._id});
},
Então, publicamos todoDetail no servidor e, em seguida, o assinamos no controlador. Isso separa onde os dados podem ser acessados em seu aplicativo. O valor dos dados é o que está especificamente disponível em seus modelos. Confira este guia para contextos de dados . A diferença entre contextos de dados e assinaturas me confundiu um pouco. Vamos ver como funciona no …
cliente
Nós geramos templates usando e comandos como esse. Você pode ver que o aplicativo está muito quebrado e cada arquivo html tem um arquivo javascript associado a ele. É aqui que entra o contexto dos dados. Cada modelo tem um nome e funções associadas . Os ajudantes basicamente usam o contexto de dados para mostrar os dados. Os eventos são onde você lida com o envio de formulários, cliques, focos etc. usando basicamente o jQuery direto. Sem diretivas nem nada. Aqui está o código para mostrar um detalhe todos e ser capaz de editá-lo:$ iron g:template todos/todos_list
helpers
events
app / client / templates / todos / todos_detail / todos_detail.js
“ `
Template.TodosDetail.events ({
// submit edit todo form
‘submit form.edit-todo’: function (e, tmpl) {
e.preventDefault () ;
var subject = tmpl.find('input[name=subject]').value;
var description = tmpl.find('[name=description]').value;
var id = this._id;
Todos.update({_id: id}, {
$set: {
subject: subject,
description: description,
updatedAt: new Date
}
});
// reroute and pass data context
Router.go('todos.detail', {_id: id})
}
});
Template.TodosDetail.helpers ({
isMyTodo: function () {
return this.userId === Meteor.userId ();
},
todoOwner: function () {
// contexto de dados é o todo
var todo = this;
return Meteor.users .findOne ({_ id: todo.userId});
}
});
“ `
Portanto, especifique eventos para o modelo com seletores jquery como os valores, como uma função que obtém o modelo e o evento como argumentos. Escrevemos no banco de dados usando a coleção global . e são do MongoDB. Meteor usa mongodb, então familiarize-se. A razão pela qual podemos fazer esta atualização é porque permitimos quando definimos a coleção todos. No controlador todos (mostrado no diretório lib), definimos o valor como o todo específico cujo id é um parâmetro da barra de url.submit form.edit-todo
Todos
update()
$set
data
O controlador todos define o contexto dos dados: data: function () { return Todos.findOne({_id: this.params._id}); },
Portanto, em nosso auxiliar de modelo this
está o contexto de dados que especificamos no controlador. É meio louco e fácil de se confundir. É por isso que damos a ele um nome de variável. Somos humanos, não máquinas.
O código do qual estou falando é onde usamos o contexto de dados do controlador: todoOwner: function(){ // data context is the todo var todo = this; return Meteor.users.findOne({_id: todo.userId}); }
Dê uma olhada no código. Eu recomendo o screencast eventedmind, foi assim que eu construí isso. Se você tiver mais perguntas, entre em contato comigo no twitter .
Outras coisas úteis
Autenticação
{{> loginButons }}
no modelo ajuda a configurar a autenticação.
Configure programaticamente o provedor OAuth:$ iron add service-configuration
Comece
server/bootstrap.js
é para onde vai qualquer código que precise ser executado quando inicializamos o servidor. Acesse variáveis de ambiente usandoconfig/development/env.sh
process.env['variable_name']
Base de dados
Ative um shell mongo para o banco de dados atual com $ iron mongo
. Como são os registros do usuário no banco de dados:
$ meteor mongo
MongoDB shell version: 2.6.7
connecting to: 127.0.0.1:3001/meteor
meteor:PRIMARY> show collections
meteor_accounts_loginServiceConfiguration
meteor_oauth_pendingCredentials
system.indexes
users
meteor:PRIMARY> db.meteor_accounts_loginServiceConfiguration.find().pretty()
{
"_id" : "MJRdreKXQ8NCPnhH7",
"service" : "meteor-developer",
"clientId" : "Dyv7PX6SWqQXhLCxZ",
"secret" : "hDePgZTKMMw6ksGKbGj3gC75TwXRpkhECh",
"loginStyle" : "popup"
}
O texto acima é o que o meteoro usa para registrar nossos usuários. A seguir está a aparência dos registros do usuário.
meteor:PRIMARY> db.users.find().pretty()
{
"_id" : "kj8iq7dpzbMGseDPy",
"createdAt" : ISODate("2015-05-21T21:59:46.840Z"),
"services" : {
"meteor-developer" : {
"accessToken" : "EzYgczXRvYRR6AyMG",
"expiresAt" : NaN,
"username" : "connorleech",
"emails" : [
{
"address" : "connorleech@gmail.com",
"primary" : true,
"verified" : true
}
],
"id" : "zkS6ewa9nyYcPAgah"
},
"resume" : {
"loginTokens" : [ ]
}
},
"profile" : {
"name" : "connorleech"
}
}
Usuários de acesso no console do navegador, digitando: .Meteor.users.find().fetch()
.find()
retorna um cursor. transforma o cursor em uma matriz.fetch()
regras de segurança para evitar gravações não autorizadas no banco de dados.lib/collections/todos.js
comandos de ferro
Adicionar uma função de publicação: Criar coleção no banco de dados mongo:$ iron g:publish todos
$ iron g:collection todos
Assine as publicações em seu controlador.
Heroku
Tentei implantar com isto:
$ heroku login
$ heroku git:remote -a <name-of-heroku-app>
$ heroku config:set BUILDPACK_URL=https://github.com/lirbank/meteor-buildpack-horse.git
$ heroku config:set ROOT_URL=https://<yourapp>.herokuapp.com
$ git push heroku master
$ heroku open
Acho que algo estava errado com minhas variáveis de ambiente. A plataforma de implantação de meteoros fornecida funciona para mim por enquanto.
Espero que isso seja útil! sim estou no twitter