I'm trying to get a helper to re-run and return the updated collection whenever this collection is updated.
For example:
Template.imagegallery.myimages = function() {
return Images.find({owner: Meteor.userId()}).fetch();
}
When I add data to the Images via a Meteor.call on the server side, my collection updates locally on the client, but the helper function does not re-run and the images don't update.....
any idea what i need to do to put the return collection from the helper object into a re-active context?
Return a cursor instead of a array:
Helper:
Template.imagegallery.myimages = function() {
return Images.find({owner: Meteor.userId()});
}
Template:
<template name="imagegallery">
{{#each myimages}}
!!!DOSTUFF!!!
{{/each}}
</template>
Fetch breaks reactivity in almost all cases. Because when you use fetch the Blaze only receives a array. So Blaze never creates the dependencies required for reactivity because it doesn't know that is data coming from a collection as that information is lost when you use fetch.
*Blaze is meteor's templating system
Related
I have a blaze template like this:
{{>jobsheetsTable companyId=companyId}}
In the JS for the template I have the onCreated function like this ...
Template.jobsheetsTable.onCreated(function(){
const instance = this;
instance.companyId = new ReactiveVar();
console.log(instance, instance.data, instance.data.companyId);
if(instance.data.companyId){
instance.companyId.set(instance.data.companyId);
}
}
The issue is that in the console.log statement I notice something odd ...
instance is correctly outputting the instance with the data object and a companyId
instance.data however returns {companyId: undefined}.
I am not changing instance.data anywhere and the function being passed into this template does not change the companyId.
Update: Using meteor 1.6.1.
The onCreated callback is only run once per template creation, so the data you get is the one that is provided to the initial creation (likely with you attribute set to undefined).
It is likely that the data context is changed after the initial rendering, and this does not trigger the function. as the template is not re-created.
If you are certain that you want to track the data context in the onCreated callback, you need to set a reactive dependency on it, using the Template.currentData() reactive data source. Since it needs to be inside a reactive context in order to be re-run when the data changes, you will need to create one, and a convenient method of doing so is via this.autorun(), which stops the computation for you when the template is destroyed.
Template.jobsheetsTable.onCreated(function(){
this.companyId = new ReactiveVar();
this.autorun(() => { // creates a reactive computation
const data = Template.currentData(); // creates a reactive dependency
console.log('data context', data);
if(data.companyId){
this.companyId.set(data.companyId);
}
})
});
The code above contains an autorun block that will re-run whenever the data context changes.
Template.Demo.onCreated(function() {
this.test = 'Text';
});
How to access that template's instance test key in Blaze directly (without creating a helper function)?
{{Template.instance.test}} seems not to work (it was suggested here).
I don't believe this is possible with current versions of Blaze. That being said, you can simulate this by declaring a single global Template helper function, like:
Template.registerHelper('instance', function () {
return Template.instance();
});
Just define this once in a common client location, and you can then reference instance in any of your Template's. So you could reference your test variable like:
{{instance.test}}
I have built a small meteor app based on code generated by the excellent Meteor Kitchen project. This code works and renders the collection to the page, but there is one thing I am confused about.
A subset of the code is here:
router.js
this.route("articles", {path: "/articles", controller: "ArticlesController"});
ArticlesController
this.ArticlesController = RouteController.extend({
template: "Articles",
onBeforeAction: function() {
this.next();
},
action: function(){
if (this.isReady()) {
this.render();
} else {
this.render("loading");
}
},
isReady: function() {
var ready = true;
var subs = [ Meteor.subscribe('allArticles') ];
_.each(subs, function(sub) {
if(!sub.ready())
ready = false;
});
return ready;
},
data: function() {
return {
articles: Articles.find({})
};
}
});
server/pubs/articles.js
Meteor.publish('allArticles', function() {
return Articles.find({});
});
Meteor.publish('singleArticle', function(articleId) {
check(articleId, String);
return Articles.find({_id: articleId});
});
As I understand how this code is working, the following takes place:
(1) Collection is published via allArticles and singleArticle subscriptions
(2) ArticlesController subscribes to allArticles
(3) data function in the ArticlesController extracts the data (from the subscription?) to the articles array which is then exposed to the Blaze template.
Where I am confused:
Why do we need to do a Articles.find({}) in the data function? Where do we access the allArticles data ... it seems we are going back to the Articles collection directly and how is that possible if we have subscribed only to allArticles ?
While you don't show it, I'm assuming you have the following line in your code, defined somewhere that will execute it on both the server, and the client:
Articles = new Mongo.Collection('articles');
/*CollectionName = new Mongo.Collection('DBCollectionName');*/
Docs. When this is executed on the server a Collection is created, and assigned to the variable name Articles. 'articles' is the name used to store this collection in MongoDB.
When this is executed on the client, a mini mongo Collection is created. It initially will have no documents in it.
Next you have server only code:
Meteor.publish('allArticles', function() {
return Articles.find({});
});
Meteor.publish('singleArticle', function(articleId) {
check(articleId, String);
return Articles.find({_id: articleId});
});
Docs. This defines two publications, 'allArticles' and 'singleArticle'. These are not Collections themselves, but are rules that specify a set of data that the server will publish, and a client may subscribe to. While these two publications return data from the Server's Articles collection, publications can return data from one or more collections, or by directly using the underlying ddp protocol you can publish data that comes from another data source (not mongodb).
Next on the client you subscribe to a collection:
Meteor.subscribe('allArticles')
Docs. This call takes the name of a publication defined on the server ('allArticles'), and subscribes to it. The server then executes the publish function, and sends over ddp the set of data returned. This data is stored in the Client-side Mini Mongo Collection created above, and named Articles.
Also the server will monitor the Articles collection for changes, and if the resultset of the 'allArticles' publication changes, will send these changes as updates to the client.
So next you have the data function in your Controller (Client side).
data: function() {
return {
articles: Articles.find({})
};
}
Docs. This sets the data context for the render function.
The reason this calls Articles.find rather than allArticles.find is because allArticles is not a collection, but was instead the name of the publication the client used to request the server send data, that was stored in the clients mini mongo collection named Articles.
Where do we access the allArticles data ... it seems we are going back
to the Articles collection directly and how is that possible if we
have subscribed only to allArticles ?
You return this as part of your data object so that you can access it in your template. In your Articles template you can now use {{#each articles}} directly without a helper function because articles is part of your data context. You can also access the articles returned from your controllers data portion inside of your Articles template helpers by using this.articles.
Why do we need to do a Articles.find({}) in the data function?
These queries being performed in your controllers data function act on the clients minimongo Articles collection as opposed to the servers. Once the information is published from the server, and the client has subscribed to it, the client has this information available in their minimongo instance, but still needs to access it somehow. Basically, the publication makes the information available, but the Articles.find({}) accesses it for the client.
Accessing this information inside of the data function of your controller is simply to avoid doing it inside of your template.
I think that your misunderstanding comes from the third step that you have described:
data function in the ArticlesController extracts the data (from the
subscription?) to the articles array which is then exposed to the
Blaze template.
The data function extracts the data from minimongo on the client which contains the information from the subscription. Minimongo lies between the subscription and the data function.
I would need to know more about your app to answer the question. Are you viewing just a single article, or is there a page that lists them all?
If you are viewing a single article you would need to subscribe to the singleArticle publication.
If you were showing a list of articles, you would need to subscribe to allArticles. If there are a lot of articles, you could improve the speed of your app by limiting the number of fields with a query projection.
I have messages collection.
Each message has an userId.
I also defined displayUsername() function, that gets the id of user, and returns fullName.
My question is can I extend it with underscorejs on the server. or what is pratical way to extend an Object
messages = new Meteor.Collection("messages");
Meteor.publish("messages", function () {
var allMessages = messages.find({}).fetch();
return _.each(allMessages, function (msg) {
return _.extend(msg, {
username: displayName(msg.userId)
});
});
so I want
{{#each messages}}
<p><strong>{{username}}:</strong> {{messageBody}}</p>
{{/each}}
I know, that it is possible on the client side, but I am going to use it some more time...
thanks..
check transform on Collection.find
http://docs.meteor.com/#find
chris has a video tut talk about "Transforming Collection Documents"
The transform option on Meteor Collections allows us to transform MongoDB documents before they're returned in a fetch, findOne or find call, and before they are passed to observer callbacks. It lays the foundation for a Model layer. In this episode I'll build a simple transform class that has a formatPrice method for a price that is stored as cents in the database.
http://www.eventedmind.com/posts/meteor-transforming-collection-documents
Unfortunately you can't send down a transformed collection. But you can transform it on the client side.
e.g when you define your collection on the client:
client side js
var messages = new Meteor.Collection("messages", {transform:function(doc) {
doc.username = displayName(doc.userId);
return doc;
}});
I am using meteor and i am a bit confused about the relationship between publishing/subscribing to documents and querying/returning collections to a client using the handlebars #each items helper.
I understand that by publishing and subscribing to certain documents, i get the reactive updating on the client side browser when things change.
I find my self writing very complex (role oriented) publish functions and writing the equivalent to return items to the client. For example,
Meteor.publish("directory", function () {
var user = Meteor.users.findOne({_id:this.userId});
//role and logic left out on purpose
return Meteor.users.find({}, {fields:{emails:1, profile:1}});
});
and the subscribe
if (Meteor.userId() != null) {
Meteor.subscribe("directory");
}
Template is called show people and the helper 'users'
Template.show_people.users = function () {
users = Meteor.users.find({}).fetch();
return users;
};
My question is, are things supposed to be done this way?. Do we return our list helpers the same query we used for publish?
You can give a query cursor to the #each Handlebars function. In fact, it's recommended. In this manner, there will be a smart update of the DOM: when a document is added to the Cursor, Handlebars will only create new DOM nodes for that document, and not recreate the DOM nodes for the documents that were already present. This is not the case when you provide an array.
So that third piece of code can just be:
Template.show_people.users = function () {
return Meteor.users.find({});
};
Note also that a collection.find() done client-side will only look in the documents inside your miniMongo storage... you're not doing a search through the entire server database, but only through the documents that the server has published to you.
So that complex, role-oriented logic is only necessary inside your Meteor.publish() function.