In Meteor, how can I prevent certain collection fields or certain portions of the page from being affected by the Live Updating/Re-Rendering system?
I found some answers such as {{#constant}} and {{#isolate}} but they are depreciated now.
Also reactive: false doesn't seem to work for me either.
You could try Tracker.nonreactive. That lets you access it a reactive data source non-reactively. For example:
<template name="tmplt">
The counter is {{getCounter}}.
</template>
Template.tmplt.helpers({
getCounter: function () {
return Tracker.nonreactive(function () {
return Session.get("counter");
});
}
});
Changes to the "counter" session variable will not cause tmplt to update.
When passing a database query to #each, make sure you call fetch:
// Don't do this
return Tracker.nonreactive(function () {
return Collection.find(...);
});
// Do this
return Tracker.nonreactive(function () {
return Collection.find(...).fetch();
});
Related
Here is my template onCreated and helper function. I have provided inline comments to add some clarity. I can see that self.post is eventually set because it shows up on my console when I log Template.instance(). However, when I log Template.instance().post, it is always undefined.
Template.default.onCreated(function() {
var self = this;
self.autorun(function() {
var postId = FlowRouter.getParam('post_id');
self.subscribe('onePost', postId);
self.post = Posts.findOne(FlowRouter.getParam('post_id'));
});
});
Template.default.helpers({
poststage: function(stage) {
console.log(Template.instance()); // I can see post object here
console.log(Template.instance().post; //always undefined
if(Template.instance().post) {
// never true
return Template.instance().post.stage == stage;
}
}
});
Template.default.events({
'submit form':function(event, instance) {
Meteor.call('someFunc', instance.post.anotherField);
}
});
Edit: I should add, I'm trying to avoid writing the query twice because I'm also using it in the Template events. (see code).
This feels like a timing issue. You are subscribing to the onePost publication but you aren't waiting for that subscription to be .ready() before assigning self.post The autorun also seems superfluous. You shouldn't need to rerun that block of code in the onCreated block. I suggest:
Template.default.onCreated(function() {
this.postId = FlowRouter.getParam('post_id');
self.subscription = subscribe('onePost', this.postId);
});
Template.default.helpers({
poststage: function(stage) {
if ( this.subscription.ready() ){
return Posts.findOne(this.postId).stage;
}
}
});
I have the following in server/publications.js...
Meteor.publish("users", function(){
return Meteor.users.find({}, {fields: {profile: 1}});
});
... and in my iron router route...
Router.route('/', function() {
this.layout('ConfLayout');
this.render('UserList', {
waitOn: function () {
return Meteor.subscribe("users");
},
data: function () {
return {
users: function () {
return Meteor.users.find();
}
};
}
});
});
...then in my template....
<template name="UserList">
<h1>Users</h1>
<ul>
{{#each users}}
<li>
{{#linkTo route='user.show'}}
<div>
{{profile.lastName}}, {{profile.firstName}}
</div>
{{/linkTo}}
</li>
{{/each}}
</ul>
</template>
...and it sort of works except the only user on the client is the currently logged in user. I am trying to get a list of ALL users for admins (don't worry about the for admins part for now).
What is also odd is that is I add a console.log statement to the publish function it never gets logged. However all the other publications in the same file seem to work fine. Also if I enable autopublish, then all the users show up as expected.
What an I missing here? Based on all I could find it seems like publishing specific fields should work for displaying all users in the client, but it almost seems like Meteor is ignoring any publications on Meteor.users altogether. I am using Meteor 1.1.0.3.
Any thoughts or help appreciated!
TIA
OK... not entirely sure why, but I suspect I may have been missing a "this" somewhere or something, but if I change the route to not use a function as the 2nd param and just pass options and then leave EVERYTHING else EXACTLY the same, it works...
Router.route('/', {
waitOn: function () {
return Meteor.subscribe('users');
},
data: function () {
return {
users: function () {
return Meteor.users.find();
}
};
},
template: 'UserList',
layoutTemplate: 'ConfLayout'
});
So I made something super simple to test out the reactivity in Meteor but when I came to make a server and client folder the reactivity broke. I can no longer manually edit the database and see the change instantly in the browser.
Template:
<template name="hello">
<input type="button" value="Click" />
{{#each tt}}
{{test}}
{{/each}}
</template>
client/test.js:
Template.hello.events(
{
'click input': function ()
{
Meteor.call('set');
}
});
Template.hello.helpers(
{
tt: function()
{
Meteor.call('get', function(error, result)
{
Session.set('aa', result);
});
return Session.get('aa');
}
});
server/testS.js:
Test = new Meteor.Collection("test");
Meteor.methods(
{
set: function()
{
Test.insert({test: "test 1"});
},
get: function()
{
return Test.find().fetch();
}
});
What am I missing to get reactivity using this folder structure?
The following is an issue.
Meteor.call('get', function(error, result) {
Session.set('aa', result);
});
This only occurs once in your case. Meteor.call is generally meant as a singular request, and is completely different than the publication/subscription model. The only "reactivity" you would experience in this case is if you manually do Session.set('aa', result);
If you want reactivity between the client/server DB, you need to set up publication/subscription code (see http://docs.meteor.com/#meteor_publish). By default, all documents in the database are published to the client via the auto-publish package, so keep that in mind. This is to auto-allow you do stuff like Collection.find() on the client, which returns a cursor, and is reactive by default.
In other words, your Meteor.call is redundant. The Test collection already exists on the client, allowing you to do the following.
Template.hello.helpers({
tt: function() {
return Test.find();
}
});
I'm trying to get a document from the server and display it on the client but the subscription always return a collection with no document.
// server/publications.js
Meteor.publish('myPages', function() {
return Pages.findOne({userId: this.userId});
});
// collection/pages.js
MyPages = new Meteor.Collection('myPages');
// client/main.js
Meteor.subscribe('myPages');
// client/view.js
Template.myView.helpers({
myPages: function(e, t) {
console.debug(MyPages.find({}));
return MyPages.find({});
}
});
You cannot move a document between collections via a subscription. If you subscribe to get a document that's in Pages collection, defined as new Meteor.Collection("pages"), then no matter how your pubsub channels look like, on the client the document will be found in the collection defined as new Meteor.Collection("pages"). So remove all traces of MyPages and use Pages on the client as well. You'll find the document there.
I don't think you can use findOne to publish collections: it doesn't return a cursor but an actual object.
Does this not work?
Meteor.publish('myPages', function() {
return Pages.find({userId: this.userId});
});
or, if necessary:
Meteor.publish('myPages', function() {
return Pages.find({userId: this.userId}, {limit: 1});
});
I have a MongoDB with a large "messages" collection; all messages belonging to a specific groupId. So have started with a publication like this:
Meteor.publish("messages", function(groupId) {
return Messages.find({
groupId: groupId
});
});
and a subscription like this:
Deps.autorun(function() {
return Meteor.subscribe("messages", Session.get("currentGroupId"));
});
This got me into trouble because initially currentGroupId is undefined but sill mongod would use up the CPU to find messages with groupId == null (although I know there are none).
Now, I tried to rewrite the publication as follows:
Meteor.publish("messages", function(groupId) {
if (groupId) {
return Messages.find({
groupId: groupId
});
} else {
return {}; // is this the way to return an empty publication!?
}
});
and/or to rewrite the subscription to:
Deps.autorun(function() {
if (Session.get("currentGroupId")) {
return Meteor.subscribe("messages", Session.get("currentGroupId"));
} else {
// can I put a Meteor.unsubscribe("messages") here!?
}
});
which both helps initially. But as soon as currentGroupId becomes undefined again (because the user navigates to a different page), mongod is still busy requerying the database for the last subscribed groupId. So how can I unsubscribe from a publication such that the mongod is stopped being queried?
According to the documentation it must be http://docs.meteor.com/#publish_stop
this.stop()
Call inside the publish function. Stops this client's subscription;
the onError callback is not invoked on the client.
So something like
Meteor.publish("messages", function(groupId) {
if (groupId) {
return Messages.find({
groupId: groupId
});
} else {
return this.stop();
}
});
And I guess on the client side you can just remove your if/else like in your first example
Deps.autorun(function() {
return Meteor.subscribe("messages", Session.get("currentGroupId"));
});
I found it more simple and straight-forward to call the .stop() function on the handler which is returned from the .subscribe() call:
let handler = Meteor.subscribe('items');
...
handler.stop();
Simply adding a condition to the publication:
Meteor.publish("messages", function(groupId) {
if (groupId) {
return Messages.find({
groupId: groupId
});
});
and keeping the subscription:
Deps.autorun(function() {
return Meteor.subscribe("messages", Session.get("currentGroupId"));
});
does the job.
There is no need to stop the publication explicitly. Eventually, the MongoDB is not queried anymore after finishing the currently running query and issuing yet another one (which seems to be queued somewhere in the system).
in your case, you should stop the autorun
there is an example in the documentation
Your autorun is actually called with a parameter that allows you to stop it:
Deps.autorun(function (c) {
if (! Session.equals("shouldAlert", true))
return;
c.stop();
alert("Oh no!");
});