I'm new to Meteor and barely understand any of it but let's say I have a collection called mycollection, declared way up top so it's available in both the client and server section:
mycollection = new Meteor.Collection('MyDumbCollection');
And then I have something like this:
if (Meteor.isClient) {
Deps.autorun(function() {
Meteor.subscribe('mycollectionSubscription');
});
Template.myshittytemplate.rendered = function() {
$("#heynow").prepend(shitfuck).dosomething();
godammit = thisfuckingthing;
//paraphrasing
mycollection.insert({thing1: thisfuckingthing});
};
}
if (Meteor.isServer) {
Meteor.publish('mycollectionSubscription', function () {
return mycollection.find();
});
};
And then in my template:
<template name="myshittytemplate">
<div id ="heynow">
{{#each mycollection}}
{{{thing1}}}
{{/each}}
</div>
</template>
What I'm trying to do is have the 'thisfuckingthing' html that's created in the #heynow div saved to the collection and published to everybody. If there's a way to make Meteor simply observe changes to the dom and save them, that's even better.
I do have autopublish uninstalled, if that makes a difference. Halp.
In client Template
Template.myshittytemplate.mycollection = function() {
return mycollection.find({}).fetch();
};
Template.myshittytemplate.rendered = function() {
$(function(){
$("#heynow").prepend(shitfuck).dosomething();
godammit = thisfuckingthing;
//paraphrasing
mycollection.insert({thing1: thisfuckingthing},function(err,_id){
if(err){
console.log(err);
return;
});
console.log(_id);
});
};
}
I needed this in the Client part:
Template.myshittytemplate.mycollection = function() {
return mycollection.find();
};
Hopes this helps somebody!
Related
I'm trying to make a Meteor helper non-reactive with this code:
let titleNonReactive;
Template.articleSubmission.onCreated(function () {
this.autorun(function() {
titleNonReactive = Template.currentData().title;
});
});
Template.articleSubmission.helpers({
titleNonreactive: function() {
return titleNonReactive;
}
});
However the resulting output is still reactive. If I save a new value in the background, it's automatically updated on the frontend where I'm displaying the result of this helper with {{ titleNonreactive }}.
How can I fix this?
This is likely caused by your Blaze data context (will need to see your Template code to confirm), but here's a possible solution that doesn't involve using Tracker.nonreactive. Since you want the value of titleNonreactive to not be reactive, you can just use a standard local / non-reactive variable to store a copy of the original reactive title. For example:
import { Template } from 'meteor/templating';
import { articles } from '/imports/api/articles/collection';
import './main.html';
let originalTitle;
Template.body.onCreated(function onCreated() {
this.autorun(() => {
const article = articles.findOne();
if (article && !originalTitle) {
originalTitle = article.title;
}
});
});
Template.body.helpers({
article() {
return articles.findOne();
},
titleNonreactive() {
return originalTitle;
}
});
Then in your Template:
<ul>
{{#with article}}
<li>Reactive Title: {{title}}</li>
<li>Non-Reactive Title: {{titleNonreactive}}</li>
{{/with}}
</ul>
Template.recent.created = function () {
this.autorun(function () {
this.subscriptions = [
this.subscribe('users'),
this.subscribe('posts'),
this.subscribe('comments')
];
}.bind(this));
};
Template.recent.rendered = function () {
this.autorun(function () {
var allReady = _.every(this.subscriptions, function (subscription) {
return subscription.ready();
});
...
Is this the correct way to subscribe to more than one DB source in a template? When I render this template again while it's still loading, then it seems to go into infinite loading state.
Related doc: https://www.discovermeteor.com/blog/template-level-subscriptions/
There is no need to wrap your subscriptions in a Tracker.autorun. In fact, each sub has a onReady callback that you can use:
this.subscribe('subName', {onReady: function() {
//Do something when ready
}});
But besides that, there is a subscriptionsReady() function that returns true when all your template subs are ready (see the doc):
So your code become:
Template.recent.onCreated(function () {
this.subscriptions = [
this.subscribe('users'),
this.subscribe('posts'),
this.subscribe('comments')
];
if(this.subscriptionsReady()) {
//do something when all subs are ready
}
});
And in your template you can also check if all template's subs are ready:
<template name="templateName">
{{#if Template.subscriptionsReady}}
Everything is ready!
{{else}}
Loading...
{{/if}}
</template>
I'm new to Meteor.
Trying to render items from collection but Meteor.renderList(observable, docFunc, [elseFunc]) alway go to elseFunc.
this.ComponentViewOrdersFlow = Backbone.View.extend({
template: null,
initialize: function() {
var frag;
Template.ordersFlow.events = {
"click a": function(e) {
return App.router.aReplace(e);
}
};
this.template = Meteor.render(function() {
return Template.ordersFlow();
});
console.log(Colors);
frag = Meteor.renderList(
Colors.find(),
function(color) {
console.log(color);
},
function() {
console.log('else consdition');
}
);
},
render: function() {
this.$el.html(this.template);
return this;
}
});
Initially I thought that Collection is empty, but console.log(Colors) shows that there are items in collection. Moreover if I use Meteor.render(... -> Template.colors({colors: Colors.find()}) ) it renders template end show Collection items there.
Meteor version 0.6.6.3 (Windows 7, 64bit)
Mongo - connected to MongoLab
Thank you for any help.
Jev.
Can't really explain this well in the comments, so here is a very, very simple example of using the Meteor template engine. This is a 100% functional app, showcasing basic reactivity. Note that I never call render() or renderList() anywhere.
All this app does is show a button, that when clicked, adds a number to a list. The number is reactively added to the list, even though I never do anything to make that reactivity explicit. Meteor's templates are automatically reactive! Try it out - this is all of the code.
numbers.html:
<body>
{{> numberList}}
</body>
<template name="numberList">
<ul>
{{#each numbers}}
<li>{{number}}</li>
{{/each}}
</ul>
<button>Click Me</button>
</template>
numbers.js:
var Numbers = new Meteor.Collection("numbers");
if (Meteor.isClient) {
Template.numberList.numbers = function() {
return Numbers.find();
};
var idx = 0;
Template.numberList.events({
"click button": function() {
Numbers.insert({
number: idx
});
idx++;
}
});
}
I'm developing a keno game. When the user presses the start button a Meteor.Call() executes everything for that card pick. Including updating the user balance. I have a setTimeout for the winning numbers, so that they display over a period of about 20 seconds. The problem is that when the call is made, the balance updates instantly, and then the numbers start displaying with the delay. I not familiar with how to solve this. I appreciate any help.
server-side:
Meteor.methods({
process: function(){
// generate numbers
// update user balance
}
});
client-side:
Template.keno.events({
'click #start' : function(){
Meteor.call('process',function(err,numbers){
//setTimeout on displaying numbers
// as setTimeout displays numbers, balance already updated. I need to delay
// the balance update, until all numbers are displayed.
// otherwise, the player see that they won before all numbers come out.
});
}
});
** Update **
The only help I need is to understand how to make a variable like {{balance}} unreactive, until I finish the setTimeout, and then have it update. Should I be using sessions? Should I not use a template variable and instead, insert the balance with jquery? It's just a simple solution, the difficulty is that I don't know what function / method I'm looking for that can help me turn off the reactivity for a set amount of time, and then update, after the Meteor.call() for then numbers finishes it's setTimeout.
If I understand your situation correctly, you need the template {{balance}} expression to be set at a time you decide vs. when the collection gets a result from the server. So you could use Session to set a value when you like. Below is an example:
<body>
{{> game}}
</body>
<template name="game">
<button id="process">Process</button>
<div>{{firstNumber}}</div>
<div>{{secondNumber}}</div>
<div>balance: {{balance}}</div>
</template>
if (Meteor.isClient) {
Template.game.events({
'click #process': function (e, tmpl) {
Meteor.call('process', function (err, result) {
Session.set('firstNumber', result[0]);
setTimeout(function () {
Session.set('secondNumber', result[1]);
Session.set('balance', result[0] + result[1]);
}, 2000);
});
}
});
Template.game.helpers({
firstNumber: function () { return Session.get('firstNumber'); },
secondNumber: function () { return Session.get('secondNumber'); },
balance: function () { return Session.get('balance'); }
});
}
if (Meteor.isServer) {
function randomNumber () {
return Math.floor(Math.random() * 100);
}
Meteor.methods({
process: function () {
return [randomNumber(), randomNumber()];
}
});
}
Try to wrap your Meteor.call() inside the setTimeout() itself, like:
Template.keno.events({
'click #start' : function(){
setTimeout(function(){
Meteor.call('process',function(){
//do something.
});
}, 20000);
}
});
Maybe the solution is to use reactivity on a duplicated collection :
You set your main collection on server side only.
You create another collection on the client side that will be a duplicate collection but used only for display
Then you pusblish the main collection to the client.
On the client side, you set all required observer on it that will replicate all modification on the duplicated collection. But this way you can manage animation or any other wished features. All actions on client side will do call on server-side but it won't affect immediatly the templates because the templates only use the duplicated collections.
I hope it will help you.
OK, so I threw this demo together in 5 mins, works though.
Here's the demo: http://keno-test.meteor.com/
Of course it needs LOT's more work, but the delayed thing works.
HTML:
<head>
<title>keno-test</title>
</head>
<body>
{{> hello}}
</body>
<template name="hello">
<input id="callCardThing" type="button" value="Start card-thing" />
<h1>Here are the cards!</h1>
<ul>
{{#each cards}}
<li>{{value}}</li>
{{/each}}
</ul>
</template>
JS:
Cards = new Meteor.Collection('cards');
if (Meteor.isClient) {
Deps.autorun(function () {
Meteor.subscribe("cards");
});
Template.hello.events({
"click #callCardThing": function (event) {
Meteor.call("doCardThingOnServer");
}
});
Template.hello.helpers({
cards: function () {
return Cards.find({});
}
});
}
if (Meteor.isServer) {
Meteor.startup(function () {
Meteor.publish("cards", function () {
return Cards.find({});
});
});
Meteor.methods({
doCardThingOnServer: function () {
// I remove all the cards every time just for the demo…
Cards.remove({});
var numberOfcards = 10;
var counter = Meteor.setInterval(function () {
Cards.insert({value: 'whatever! no: '+numberOfcards });
numberOfcards--;
if (numberOfcards < 1) Meteor.clearInterval(counter);
}, 1500);
}
});
}
Ok, so how about conditional {{balance}} rendering?
var shouldRender = false;
setTimeout(function () {
shouldRender = true;
}, 2000);
Template.template_name.shouldRender = function () {
return shouldRender;
}
{{#if shouldRender}}
{{>balance}}
{{/if}}
Have a look at the Atmosphere animation package : https://atmosphere.meteor.com/package/animation!
I've just done this package to explore one way of doing animation on database reactivity.
You have to register a cursor and the template to animate. There is a project that will show you how to do that.
I hope it will help you.
I've run into an interesting possible bug, but in this case it may be caused by the lack of a function for me to use to delete a document when observing a collection. Or I am misusing observe... which could very well be the case!
Here is sample code that will reproduce the issue I'm having.
I'm using the devel branch as of this writing, so I'm not sure if this works in 0.3.5
observebug.html
<head>
<title>observebug</title>
</head>
<body>
{{> main}}
</body>
<template name="main">
<h1>Example showing possible bug in Meteor wrt observe</h1>
<div>
<p>Try to delete a note. You will notice that it doesn't appear to get deleted. However, on the server, the delete did occur. Refresh the page to see that the delete did in fact occur.</p>
<h2>Notes:</h2>
<ul>
{{#each notes}}
{{> note_row}}
{{/each}}
</ul>
</div>
</template>
<template name="note_row">
<li>{{title}} <button name="delete">delete</button></li>
</template>
observebug.js
// CLIENT
if (Meteor.is_client) {
Notes = new Meteor.Collection("notes_collection");
Meteor.autosubscribe(function () {
Meteor.subscribe("notes_subscription");
});
Template.main.notes = function () {
return Notes.find();
};
Template.note_row.events = {
"click button[name='delete']": function (evt) {
Meteor.call("deleteNote", this._id, function (error, result) {
if (!error) {
console.log("Note deletion successful.");
} else {
console.log("Error when deleting note.");
}
});
}
};
}
// SERVER
if (Meteor.is_server) {
Notes = new Meteor.Collection("notes_collection");
Meteor.methods({
"deleteNote": function (note_id) {
try {
Notes.remove(note_id);
return true;
} catch (e) {
return false;
}
}
});
Meteor.publish("notes_subscription", function () {
var notes = Notes.find({}, {sort: {title: 1}});
var self = this;
// What we're doing here is basically making an exact duplicate
// of the notes collection. A possible use-case for this would be
// to actually make changes to the copied collection on the fly,
// such as adding new fields without affecting the original
// collection.
var upsertHandler = function (note, idx) {
note.some_new_field = 100;
self.set("notes_collection", note._id, note);
self.flush();
};
var handle = notes.observe({
added: upsertHandler,
changed: upsertHandler,
removed: function (note, idx) {
// As far as I can tell, unset won't remove the document,
// only attributes of the document. I don't think there's
// a method to handle actually removing a whole document?
self.unset("notes_collection", note._id);
self.flush();
}
});
self.onStop(function () {
handle.stop();
self.flush();
});
});
// Add example notes
Meteor.startup(function () {
if (Notes.find().count() === 0) {
Notes.insert({title: "Note #1"});
Notes.insert({title: "Note #2"});
Notes.insert({title: "Note #3"});
Notes.insert({title: "Note #4"});
Notes.insert({title: "Note #5"});
Notes.insert({title: "Note #6"});
}
});
}
What you'll be seeing
When you start this application up, you'll see 6 example "notes", each with a delete button. I suggest having your console open so you can see the console.logs. Click the delete button on any note. The note does get deleted, however this doesn't get reflected back up to the client.
I suspect the issue lies in how I'm leveraging observe to create a copy of the collection (which I can then manipulate without affecting the original collection). I feel like I need a function that deletes a whole document, not just some attributes (unset).
EDIT: See the example in action over at http://observebug.meteor.com/
So I've got it figured out. I did indeed need to use observe, and there is no Meteor bug on that end. Just a lack of understanding of what was involved in what I was trying to accomplish. Luckily I found a great starting point within the Meteor code itself and wrote an adjusted version of the _publishCursor function, which I've called publishModifiedCursor.
Here are the adjusted project templates and code:
observe.html
<head>
<title>observe</title>
</head>
<body>
{{> main}}
</body>
<template name="main">
<h1>Example in trying to publish a modified copy of a collection</h1>
<div>
<h2>Notes:</h2>
<ul>
{{#each notes}}
{{> note_row}}
{{/each}}
</ul>
<p><button name="add">add note</button></p>
</div>
</template>
<template name="note_row">
<li>
<strong>Original title:</strong> <em>{{title}}</em><br />
<strong>Modified title:</strong> <em>{{__modified_title}}</em><br />
<button name="delete">delete</button>
</li>
</template>
observe.js
// CLIENT
if (Meteor.is_client) {
ModifiedNotes = new Meteor.Collection("modified_notes_collection");
Meteor.autosubscribe(function () {
Meteor.subscribe("modified_notes_subscription");
});
Template.main.notes = function () {
return ModifiedNotes.find();
};
Template.main.events = {
"click button[name='add']": function (evt) {
Meteor.call("addNote", function (error, result) {
if (!error) {
console.log("Note addition successful.");
} else {
console.log("Error when adding note.");
}
});
}
};
Template.note_row.events = {
"click button[name='delete']": function (evt) {
Meteor.call("deleteNote", this._id, function (error, result) {
if (!error) {
console.log("Note deletion successful.");
} else {
console.log("Error when deleting note.");
}
});
}
};
}
// SERVER
if (Meteor.is_server) {
Notes = new Meteor.Collection("notes_collection");
Meteor.methods({
"addNote": function () {
try {
Notes.insert({title: "Note #" + (Notes.find().count() + 1)});
return true;
} catch (e) {
return false;
}
},
"deleteNote": function (note_id) {
try {
Notes.remove(note_id);
return true;
} catch (e) {
return false;
}
}
});
Meteor.publish("modified_notes_subscription", function () {
// Pull up the original notes_collection
var notes = Notes.find({}, {sort: {title: 1}});
// Publish a near identical collection, with modifications
this.publishModifiedCursor(notes, "modified_notes_collection", function (note) {
note.__modified_title = getTitleModifiedByServer(note.title);
return note;
});
});
var getTitleModifiedByServer = function (title) {
return title + "!!!";
};
// Add example notes
Meteor.startup(function () {
if (Notes.find().count() === 0) {
Notes.insert({title: "Note #1"});
Notes.insert({title: "Note #2"});
Notes.insert({title: "Note #3"});
Notes.insert({title: "Note #4"});
Notes.insert({title: "Note #5"});
Notes.insert({title: "Note #6"});
}
});
_.extend(Meteor._LivedataSubscription.prototype, {
publishModifiedCursor: function (cursor, name, map_callback) {
var self = this;
var collection = name || cursor.collection_name;
var observe_handle = cursor.observe({
added: function (obj) {
obj = map_callback.call(self, obj);
self.set(collection, obj._id, obj);
self.flush();
},
changed: function (obj, old_idx, old_obj) {
var set = {};
obj = map_callback.call(self, obj);
_.each(obj, function (v, k) {
if (!_.isEqual(v, old_obj[k])) {
set[k] = v;
}
});
self.set(collection, obj._id, set);
var dead_keys = _.difference(_.keys(old_obj), _.keys(obj));
self.unset(collection, obj._id, dead_keys);
self.flush();
},
removed: function (old_obj, old_idx) {
old_obj = map_callback.call(self, old_obj);
self.unset(collection, old_obj._id, _.keys(old_obj));
self.flush();
}
});
self.complete();
self.flush();
self.onStop(_.bind(observe_handle.stop, observe_handle));
}
});
}
See it live over at http://observebug.meteor.com/. The final effect is underwhelming, since it's just a test involving adding/removing notes... but hey now it works!
Hope this helps someone else out in the future.
this.unset requires three parameters, the first and second one are correct; it however requires a third parameter to tell which attributes to unset. This is a bug in your code and not in Meteor.
However, note that when your callback is called tha the document is already deleted from the collection and that you are just working on a copy of the object. Exactly, Notes.find({}).count() reveals the remaining count at any point during your callback.
// I don't think there's
// a method to handle actually removing a whole document?
If you want two collections, create two collections. By only keeping one and trying to do all sorts of magic, you are calling functions that are not even meant to be doing what you want them to do. If you just create
Notes_copy = new Meteor.Collection("notes_collection_copy");
and use that to keep track of temporary notes, you won't run into any of the trouble you had now.