On Github kcharvey / intro-to-meteor
Requests vs. Subscription
1. Traditional web applications rely on the request/response loop... 2. This is the key differentiator... unless you install a router package
The reason it's compared to Angular most often is it's great at single-page appsThis is some serious hocus pocus
Client UI and database are updated first Changes sent to server Issues are resolved after the factContinuing the wizadry theme
Changes from the server are updated automatically Models are simulated locally$ meteor create myapp $ meteor add accounts-ui accounts-password $ meteorNot only is does meteor create and run you app for development, it's also it's own package manager
$ curl https://install.meteor.com | /bin/sh
$ meteor create the-questionator $ cd the-questionator $ meteor [[[[[ ~/dev/the-questionator ]]]]] => Started proxy. => Started MongoDB. => Started your app. => App running at: http://localhost:3000/
$ ls .meteor the-questionator.css the-questionator.html the-questionator.js
<head> <title>the-questionator</title> </head> <body> <h1>Welcome to Meteor!</h1> {{> hello}} </body> <template name="hello"> <button>Click Me</button> <p>You've pressed the button {{counter}} times.</p> </template>The default templating language is called "Spacebars"
if (Meteor.isClient) { // counter starts at 0 Session.setDefault("counter", 0); Template.hello.helpers({ counter: function () { return Session.get("counter"); } }); Template.hello.events({ 'click button': function () { // increment the counter when button is clicked Session.set("counter", Session.get("counter") + 1); } }); } if (Meteor.isServer) { Meteor.startup(function () { // code to run on server at startup }); }
$ meteor add twbs:bootstrap
$ the-questionator.html ... <body> <div class="container-fluid" style="padding-top:60px;"> <nav role="navigation" class="navbar navbar-default navbar-fixed-top"> <div class="container-fluid"> <div class="navbar-header"> <a class="navbar-brand" href="#"> The Questionator </a> </div> </div> </nav> {{> hello}} </div> </body> ...
$ the-questionator.html ... <form role="form"> <div class="form-group"> <textarea id="question" class="form-control" rows="2"> </textarea> </div> <div class="form-group"> <input class="btn btn-default" type="submit" value="Ask away" /> </div> </form> {{> hello}} </div> </body> ...
$ the-questionator.html ... {{> hello}} ... <template name="hello"> <button>Click Me</button> <p>You've pressed the button {{counter}} times.</p> </template>
$ the-questionator.html ... {{> questionsList}} </div> </body> <template name="questionsList"> <div id="questions"> {{#each questions}} <hr /> <h6>{{text}} <small>Votes: {{votes}}</small></h6> <a class="btn btn-default btn-xs vote-up">up</a> <a class="btn btn-default btn-xs vote-down">down</a> {{/each}} </div> <hr /> </template>
$ the-questionator.js if (Meteor.isClient){ var questionsData = [ { text: 'Why does the sun shine?', votes: 0 }, { text: 'If you were a hot dog, and you' + 'were starving to death, would you eat yourself?', votes: 0 }, { text: 'What is the airspeed velocity' + ' of an unladen swallow?', votes: 0 } ]; Template.questionsList.helpers({ questions: questionsData }); }
$ the-questionator.js Questions = new Mongo.Collection('questions'); ...c48d697579cb52f7f065a4f00928f013878100ac
$ meteor mongo MongoDB shell version: 2.4.9 connecting to: 127.0.0.1:3001/meteor Welcome to the MongoDB shell. meteor:PRIMARY> db.questions.find()
> Questions.find() > LocalCollection.Cursor {collection: LocalCollection, sorter: null, _selectorId: undefined...}'meteor' command starts MongoDB on port 3001 on your local machine
$ the-questionator.js Questions = new Mongo.Collection('questions'); if (Meteor.isClient){ Template.questionsList.helpers({ questions: Questions.find(), }); }
$ the-questionator.js if (Meteor.isServer) { if (Questions.find().count() === 0) { Questions.insert({ text: 'Why does the sun shine?', votes: 0 }); Questions.insert({ text: 'If you were a hot dog, and you were...', votes: 0 }); Questions.insert({ text: 'What is the airspeed velocity of...', votes: 0 }); } }
$ meteor remove autopublish
if (Meteor.isClient) { Meteor.subscribe('questions'); ... } if (Meteor.isServer) { ... Meteor.publish('questions', function() { return Questions.find(); }); }
Template.questionsList.helpers({ questions: Questions.find({}, {sort: {votes: -1}}), });
if (Meteor.isClient) { ... Template.questionsList.events({ 'click .vote-up': function(e) { e.preventDefault(); Meteor.call('upvote', this._id); }, 'click .vote-down': function(e) { e.preventDefault(); Meteor.call('downvote', this._id); } }); }
Meteor.methods({ upvote: function(questionId) { var question = Questions.findOne(questionId); Questions.update( questionId, { $set: { votes: question.votes + 1 }} ); }, downvote: function(questionId) { var question = Questions.findOne(questionId); Questions.update( questionId, { $set: { votes: question.votes - 1 }} ); } });
{{> questionForm}} ... <template name="questionForm"> <form role="form"> <div class="form-group"> <textarea id="question" class="form-control" rows="2"></textarea> </div> <div class="form-group"> <input class="btn btn-primary" type="submit" value="Ask away" /> </div> </form> </template>
if (Meteor.isClient) { ... Template.questionForm.events({ 'submit form': function(e) { Questions.insert({ 'text': $(e.target).find('#question').val(), 'votes': 0 }); } }); }
$ meteor add accounts-twitter $ meteor add ian:accounts-ui-bootstrap-3
{{> loginButtons}}
<div class="navbar-header"> <a class="navbar-brand" href="#">The Questionator</a> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navigation"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> </div> <div class="collapse navbar-collapse" id="navigation"> <ul class="nav navbar-nav navbar-right"> {{> loginButtons}} </ul> </div>
Questions.allow({ insert: function(userId, doc) { return !! userId; } }); // .. and the methods we added earlier
$ meteor remove insecure
$ meteor deploy questionator.meteor.com Deploying to http://questionator.meteor.comThat's how I deployed the sample app for this talk. Will ask you for an email address, and then just work.
$ npm install -g modulus $ modulus login $ modulus project createPaaS privder specializing in Node.js apps. Open sourced 'demeteorizer' for turning metoer apps into straight up Node.js
$ npm install -g mup $ mkdir ~/deploy-the-questionator $ cd ~/deploy-the-questionator $ mup initInstall to your own AWS, Digital Ocean, etc.
A departure from the request/response loop
https://developer.mozilla.org/en-US/docs/WebSockets Opens a socket between a server and the client browser.Publication & Subscription
http://2012.realtimeconf.com/video/matt-debergalis Meteor-specific protocol for managing publications and subscriptions over a web socketGetting realtime updates from MongoDB
https://github.com/meteor/meteor/wiki/Oplog-Observe-Driver How Meteor gets realtime updates from MongoDBHot code pushes, reactions to database changes, etc.
https://github.com/meteor/meteor/tree/devel/packages/tracker Keeps track of dependent computations Previously known as 'Deps'