-
Notifications
You must be signed in to change notification settings - Fork 1
meteor
- call
meteorin the bash in your porjectfolder --> meteor will start -
Strg+Cin the bash --> stops running meteor
-
/servercode only runs on the server -
/clientcode only runs on the client-
/client/compatibilityJavaScript libraries that rely on variables declared with var at the top level being exported as globals.
-
-
/libgets loaded before anything else -
/publicstatic assets (images, fonts ...) -
/privatestatic assets, server only - Any
main.*file is loaded after everything else
everything else runs on server and client
Die Datenbank liegt unter .meteor/local/db. Änderungen werden allerdings nicht von git erkannt weil meteor selbstständig eine .gitignore anlegt:
# .meteor/.gitignore
local
... Template sections, on the other hand, are converted into JavaScript functions, available under the Template namespace. It's a really convenient way to ship HTML templates to the client...
Die meteor Template Engine nennt sich wohl Spacebars. Hier ist die Doku dazu
Generell kann man die Template Tags in irgend welchen .html Dokumenten verwenden und in anderen .js Dateien dann die Helper zu dem jeweiligen Template definieren.
Beispiel-Template:
<template name="postsList">
<div class="posts">
{{#each posts}}
{{> postItem}}
{{/each}}
</div>
</template>Beispiel Helper für postsList:
var postsData = [
{
title: 'Introducing Telescope',
author: 'Sacha Greif',
url: 'http://sachagreif.com/introducing-telescope/'
},
{
title: 'Meteor',
author: 'Tom Coleman',
url: 'http://meteor.com'
},
{
title: 'The Meteor Book',
author: 'Tom Coleman',
url: 'http://themeteorbook.com'
}
];
Template.postsList.helpers({
posts: postsData
});
innerhalb von <template name="postsList"> wird ein anderes Template postItem angesprochen.
<template name="postItem">
<div class="post">
<div class="post-content">
<h3><a href="{{url}}">{{title}}</a><span>{{domain}}</span></h3>
</div>
</div>
</template>da postItem innerhalb von postList ausgeführt wird erhält es automatisch auch die Helper von postList. Das bedeutet obwohl im dazugehörigen Helper:
Template.postItem.helpers({
domain: function() {
var a = document.createElement('a');
a.href = this.url;
return a.hostname;
}
});kein title und domain definiert werden, hat das Template die Daten vom übergeordneten Template.
- meteor startet eine MongoDB im Hintergrund
- Konsole kann via
meteor mongoaufgerufen werden
- Konsole kann via
- eine Collection hält Daten für alle Benutzer gleichzeitig
- kein Sessionbezug
- wenn die Collection nicht innerhalb der Ordner
/clientoder/serverinitialisiert wird steht sie beiden Komponenten zur Verfügung - Das Schlüsselwort
varlimitiert den Scope des Objekts auf die aktuelle Datei. Ohnevarist es in der kompletten App verfügbar
Die Collection auf der Server-Seite entspricht der Collection in der Datenbank und stellt das API zur Datenbank zur Verfügung, während die Collection auf der Client Seite (genannt MiniMongo) einer Teilmenge der realen Datenbank darstellt.
Posts = new Meteor.Collection('posts');
Posts.find().count(); // Anzahl der Objekte
Posts.find(); // LocalCollection Cursor
Posts.find().fetch(); // Alle Daten aus der Collection
Posts.insert({title: "new post"}); // neues Objekt hinzufügen-
$ meteor resetlöscht die Datenbank und setzt das Projekt zurück
Per default ist das meteor-Package autopublish aktiv, welches jede Collection ins Frontend lädt. Die Collection wird dann ungefragt an alle Clients verteilt. Dies lässt sich durch
$ meteor remove autopublishentfernen. Jetzt muss die Collection vom Server published, und vom Client subscribed werden
Bsp:
// --- server/publications.js ---
Meteor.publish('posts', function() {
return Posts.find();
});
// ------------------------------
// --- client/main.js ---
Meteor.subscribe('posts');
// ----------------------In diesem Beispiel wird allerdings eine komplette Kopie der Datenbank an den Client geschickt. Das ist sicherheits- und performancetechnisch doof. Deshalb kann man im serverseitigen Meteor.publish() eine Selektion einbauen.
// on the server
Meteor.publish('posts', function(author) {
return Posts.find({flagged: false, author: author});
});
// on the client
Meteor.subscribe('posts', 'bob-smith');Zu Beginn eines Meteor-Projekts wird das Package insecure mit geladen. Das sorgt dafür dass es keine Einschränkungen beim insert() in eine Collection gibt. Dies kann durch
meteor remove insecureentfernt werden. Sinn davon ist es z.B. nur eingeloggten Usern die Möglichkeit zu geben Collections zu verändern. Hier ein Bespiel:
Posts = new Meteor.Collection('posts');
Posts.allow({
insert: function(userId, doc) {
// only allow posting if you are logged in
return !! userId;
}
});-
Posts.allow()erhält ein Objekt mit Optionen -
function(userId, doc)wird hier beim Insert aufgerufen- wenn diese Funktion
trueliefert dann darf inserted werden -
userIdist die ID des eingeloggten Users -
docist das Element welches inserted werden soll - das
!!ist sowas wie ein TypeCast auf boolean (erst wird durch!userIdgetestet ob siefalseist, dann wird durch!!userIdgetestet ob sietrueist. Macht man wohl so um sicherzustellen dass hier definitiv ein Boolean zurückkommt.)
- wenn diese Funktion
-
Collection.insert(...)returns the generated id
Routing dient dazu spezielle Informationen aus der URL zu extrahieren. Beispiel hierfür ist:
We’d like these pages to be accessible via a permalink, a URL of the form
http://myapp.com/posts/xyz(wherexyzis a MongoDB_ididentifier) that is unique to each post.
Ohne Routing könnte man, für den Fall dass statt http://localhost:3000 z.b. http://localhost:3000/foo/bar eingegeben wird, kein gesondertes Verhalten definieren.
Via Routing können auch load-screens definiert werden, welche angezeigt werden solange auf Daten gewartet wird. Der Iron-Router bringt ebenfalls eine eigene Datenkontext Verwaltung mit. Das bedeutet Datenquellen können über den Router subscribed werden und die Daten stehen dann den geladenen Templates zur Verfügung.
Ist das gesamte Konstukt einer bestimmten Routings. Es sagt der Anwendung wohin gegangen und was getan werden soll sobald eine URL auftaucht.
ist eine konkrete URL innerhalb der App. z.B. /terms_of_service oder /search?keyword=foo
Ein Segment eines Paths sind die einzelnen Teile zwischen den /
Hooks sind Aktionen die vor oder nach einem Routing-Prozess ausgeführt werden sollen. Z.B. überprüfen ob der User genügend Rechte zum Anzeigen der Seite hat.
Filters sind Hooks die global für eine oder mehrere Routen konfiguriert werden
Jede Route muss auf ein Template zeigen. Sofern kein Template spezifiziert ist sucht der Router eins mit dem gleichen Namen wie die Route
Layouts sind der HTML Code der das Template umschließt. Dieser ändert sich nicht.
Controller werden benötigt wenn viele Templates die selben Parameter benutzen. Dann kann man, statt Code zu duplizieren, die Routen mit einem einzelnen Controller konfigurieren, der die Routing-Logik beinhaltet.
Das Iron Router Package bringt einen speziellen Template - Helper mit. {{> yield}} sorgt dafür dass an dieser Stelle das, aktuell zur Route passende, Template eingefügt wird.
Im Buch wird empfohlen eine Datei lib/router.js anzulegen. Da der Inhalt des /lib Ordners vor allen anderen ausgeführt wird, empfiehlt es sich hier Konfigurationen anzulegen. In der Datei wird im ersten Schritt folgendes getan:
// legt default Layout für alle Routen fest
Router.configure({
layoutTemplate: 'layout'
});
// legt fest dass unter "/" die Route `postsList` aktiv werden soll
Router.map(function(){
this.route('postsList',{path: '/'});
});By default, Iron Router will look for a template with the same name as the route. In fact, it will even look for a path based on the route name, meaning that if we hadn’t defined a custom path (which we did by providing a path option in our route definition), our template would’ve been accessible at URL
/postsListby default.
Ein weiterer Vorteil von Routen ist dass man sie durch ihren Namen auch weiterverwenden kann... z.B. kann man einen Link auf das Ziel der aktuellen postsList-Route durch
<a href="{{pathFor 'postsList'}}">bla</a>erzeugen.
Mit Iron-Router kann man ein loadingTemplate definieren, welches greift solange eine bestimmte Funktion noch nicht ausgeführt ist.
Router.configure({
layoutTemplate: 'layout',
loadingTemplate: 'loading',
waitOn: function() { return Meteor.subscribe('posts'); }
});
// wird ausgeführt bevor das Routing aktiv wird
Router.onBeforeAction('loading');Wenn man das spin package installiert hat, kann man hiermit einen einfachen Spinner auf der Website erzeugen der den Ladevorgang verdeutlicht.
<template name="loading">
{{> spinner}}
</template>Mit dynamischen Routen können Parameter aus den Segmenten des Path extrahiert werden, wie hier z.B. eine id
Router.map(function(){
this.route('postsList',{path: '/'});
this.route('postPage', {
path: '/posts/:_id',
data: function() { return Posts.findOne(this.params._id); }
});
});die unter data: definierten Daten stehen dem geladenen Template dann direkt zur Verfügung. Die Daten können dann innerhalb des Templates via this angesprochen werden.
Ok, jetzt wirds magisch. Im obrigen Beispiel wurde ja eine dynamische Route definiert. Wenn man jetzt via
<a href="{{pathFor 'postPage'}}"...
einen Link zur postPage erzeugen lässt erkennt der Router automatisch das dafür wohl eine id benötigt wird und schaut im geladenen Datenkontext der postPage nach!
-
Router.go()kann verwendet werden um den Browser auf eine andere Seite umzulenken
Im Session-Objekt kann man Daten speichern die nur in der aktuellen Sitzung erhalten bleiben. (Bis zum Browser-Reload, beim HotCode Reload bleiben die Daten erhalten) Das ganze ist reaktiv, d.h. wenn sich etwas ändert werden auch alle Helper aktualisiert und neu gerendert. Das Session - Objekt ist global verfügbar, d.h. Änderungen können von überall aus geschehen.
Session.set('mySessionProperty','foo');
Session.get('mySessionProperty');Auf die Daten kann dann innerhalb des HTMLs mit {{mySessionProperty}} zugegriffen werden denn diese als Template Helper hinzugefügt wurden:
Template.layout.helpers({
pageTitle: function() { return Session.get('pageTitle'); }
});Autorun mit Deps
Deps.autorun(function() {
alert(Session.get('message'));
});wird immer ausgeführt wenn sich an einer beteiligten Datenquelle etwas ändert. Es macht wohl Sinn das ganze in Meteor.startup(function(){...}) zu kapseln, da dieser Code erst ausgeführt wird wenn alles geladen ist (und auch sichergestellt ist dass eventuelle Collections geladen sind.)
Durch das hinzufügen des accounts packages legt Meteor eine neue Collection namens Meteor.users an. Zum Registrieren von Usern gibt es ein gefühltes Kilo an Packages auf atmospherejs. Im Tutorial wird mrt:accounts-ui-bootstrap-dropdown verwendet. Dies kann dann via {{> loginButtons}} überall ins Template eingebaut werden und bringt alle Funktionalitäten zum Login und zum erstellen eines neuen Accounts mit. Im Beispiel sieht dass dann auf der Javascript Konsole so aus:
Meteor.users.findOne();
Object {_id: "EirZiT75izQ44L47n", username: "tom"}
Meteor.user(); //gibt das aktuell eingeloggte Nutzerobjekt zurück
Object {_id: "EirZiT75izQ44L47n", username: "tom"}Das accounts Package published nur den aktuellen Nutzer automatisch. Das bedeutet für den aktuellen Client sieht es immer so aus als ob Meteor.users.find().count() nur 1 ist. (Macht ja auch Sinn, ansonsten könnte jeder Client alle User durchschauen). In der mongodb selber findet man allerdings via db.users.find().count() die korrekte Anzahl der User heraus.
Auf Seite 11 wird empfohlen das npm-package meteorite zu installieren. Laut der Doku zu meteorite wird das allerdings nicht mehr benötigt. Es sieht auf den ersten Blick erstmal so aus als ob es keinen wirklichen Unterschied gibt ob man das Projekt mit
$ mrt create microscopeoder
$ meteor create microscopeanlegt.
Auf Seite 12 wird durch mrt add bootstrap ein Atmosphere-Bootstrap-Package installiert. Das lässt sich ohne mrt via meteor add bootstrap tun.
iron:router package | Atmosphere
$ meteor add iron:routerDer Code auf Seite 122 wirft folgenden Fehler:
Route dispatch never rendered. Did you forget to call this.next() in an onBeforeAction?
Grund dafür ist eine Änderung im iron:router Package seit Version 1.0. Der falsche Code lautet:
var requireLogin = function(pause) {
if (!Meteor.user()) {
if (Meteor.loggingIn()) {
this.render(this.loadingTemplate);
} else {
this.render('accessDenied');
}
pause();
}
}richtig ist:
var requireLogin = function() {
if (!Meteor.user()) {
if (Meteor.loggingIn()) {
this.render(this.loadingTemplate);
} else {
this.render('accessDenied');
}
} else {
this.next();
}
}- Codestyle
- ScrumPrimer
- Git - Workflow
- Git Magic
-
[[Ordner Struktur]] -
[[Entwicklungsumgebung]] -
[[Definition of Done|dod]]