The aim is to create a simple voting app. The full project is available here : https://github.com/Rebolon/meteor-sample-simpleVote
meteor create lyonJS && cd $_
Then edit the lyonJS.html and fill it like this :
<head> <title>meteor-sample-listVote</title> </head> <body> {{> main}} </body> <template name="main"> <h1>Votez pour un sujet</h1> {{#if atLeatOneSubject}} <ol> {{#each subjects}} <li><div class="btn-group"> <button class="btn-vote-up btn btn-xs btn-success"><span class="glyphicon glyphicon-thumbs-up"></span></button> <button class="btn-vote-down btn btn-xs btn-warning"><span class="glyphicon glyphicon-thumbs-down"></span></button> </div> {{label}} : {{count}}</li> {{/each}} </ol> {{/if}} {{#unless atLeatOneSubject}} <cite>Aucun sujet en base, allez !</cite><br /> {{/unless}} <div class="form-group {{#if hasError}}has-error{{/if}}"> <div class="col-xs-2"> <input type="text" id="fld-subject-label" class="form-control" /> </div> <button id="btn-add-subject" class="btn btn-default">Ok</button> {{> error}} </div> </template> <template name="error"> <label class="control-label" for="fld-subject-label">{{error}}</label> </template>
Here we define 2 templates : main and error.
Main is included into the body using "{{> main}}
Main contains 3 helpers :
* "atLeastOneSubject" return true if there is items to display
* "subjects" return a cursor to loop over with the "each" helper
* "hasError" is used to modify the display of the field
Error is a simple template that will just display current Error.
Helpers will help us to make reactive zone inside template. If something change from those helpers, all the template will be re-calculated by the Render Engine. So you may need to do small template to improve client performance.
Now edit the lyonJS.js file and fill it with :
// Exemple simpe : pas de sécurité, autopublish, utilisation de minimongo // Autre possibilité : sécurisation en supprimant le package insecure // Et encore : passage par Meteor.methods pour les accès en écriture dans la base => Ajax like // Collection, disponible client/serveur Subjects = new Meteor.Collection('Subjects' /*'msl-subjects'*/); if (Meteor.isClient) { // fonctions côtés client resetError = function () { Session.set('error', ''); }; // Template main Session.setDefault("error", ""); // Helper du template error Template.error.helpers({ error: function () { return Session.get('error'); } }); // Template main Template.main.helpers({ atLeatOneSubject: function () { return Subjects.find().count(); }, subjects: function () { return Subjects.find({}, {sort: {count: -1}}); }, hasError: function () { return !!Session.get('error'); } }); Template.main.events({ 'click button': function() { resetError(); }, 'focus #fld-subject-label': function() { resetError(); }, 'click .btn-vote-up': function () { Subjects.update({_id: this._id}, {$inc: {count: 1}}); }, 'click .btn-vote-down': function () { if (this.count === 0) { Session.set('error', 'on ne peut voter - si on est déjà à 0'); return; } Subjects.update({_id: this._id}, {$inc: {count: -1}}); }, 'click #btn-add-subject': function () { var el = document.querySelector('#fld-subject-label'); if (!el || !el.value.trim().length) { Session.set('error', 'Le champ ne doit pas être vide'); return; } if (!Subjects.find({label: el.value}).count()) { Subjects.insert({'label': el.value, 'count': 1}); el.value = ''; } else { Session.set('error', 'label déjà présent'); } }, }); }
First you may notice the Meteor.isClient property. It allows Meteor to execute the code only if it is on client browser. Meteor.isServer is also available to execute code on server-side only. Everything that is not inside those 2 blocks are executed on both server and client side.
Don't forget one thing, here we have only one file (it's the first step of our app). And this file will be fully downloaded by the client. So don't put password or secret string in this file. Even if you put it inside Meteor.isServer block, it won't be ran on the client but with a debugger it's easy to read the file content.
Then you can see that the first line creates a Meteor Collection. It's out of the "isClient" block, so the collection will be available on both server and client.
On the server, Meteor will check if a Mongo collection exist with the name 'app-subjects' and it will create it if needed. The variable Subjects is now linked to that mongo collection, and it will observe any changes from it.
On the client, Meteor will create the required subscription (and the required publication on server-side) that allows the client to get a copy of the Mongo collection locally. Any changes from the server will be "pushed" to the client. And any changes in the client (insert/remove/update) will be pushed to the server.
Then we can see that we use a Session object. Those sessions are reactive object that the Renderer Engine listen to. Each modifications inside Session will re-render all dependant Template. This Reactive concept is client only.
We also have Helpers that we defined in the html file. Those helpers work with Collections, but also with Session or, whatever you want. Each time you include something reactive inside the Helpers (like Sessions or Collections), those helpers will be re-run if something change in the dependancies.
Finally we have Events that declare the behaviour of the app on Client. An Event is a Dom event (click, focus, mousemove...) and a css selector. Here with those events we allow user to insert data into Collection.
Open 2 browsers to the url of your server, and play with. Each time you add an item you will see modifications on both browser.