I am facing a little challenge here that I assume is very common… I am loading a collection inside a Template Helper and would like to edit a field’s formatting before showing it on the client.
However, I can’t edit this field after calling Collection.findOne because it is an asynchronous operation that doesn’t accept callbacks!
Here is my code:
Template.UpdateForm.helpers({
'company'() {
let companyId = FlowRouter.getParam("companyId");
let company = Companies.findOne({
_id: companyId
});
company.status = 'OK';
return company;
}
});
The result of this is an error telling me that it is impossible to read the property ‘status’ of undefined.
When I console.log( ) company, it is indeed undefined - but not because it doesn’t exist/match, but rather because it hasn’t loaded on time.
Any idea what I could do? (this is a situation that appears quite a few times here in my project)
In terms of performance, is the solution you suggested (loading data on the onCreated and then using reactiveVar) equivalent? From my (amateur) perspective, it sounds even better since the collection will be called only once (or triggered by autorun), not like inside the template helper where it is called every time something changes.
I am using {{#each}} instead of {{#let}} because later there will be more companies, not just one - and I will also change the helper to .find().fetch().
The problem here is because the helper runs before the data has arrived through the subscription. When the helper runs, findOne returns undefined because it doesn’t have the data yet (note, findOne is sync on the client because of minimongo).
Because the helper is reactive, it will re-run if the data returned by findOne changes. So all you need to do is add a guard clause before modifying the status, like so:
Template.UpdateForm.helpers({
'company'() {
let companyId = FlowRouter.getParam("companyId");
let company = Companies.findOne({
_id: companyId
});
if (!company) return;
company.status = 'OK';
return company;
}
});
When the subscription finishes loading the data needed by the helper, it will re-run and pass the result to Blaze to render
Everything else can stay the same, no need for reactiveVars or storing extra variables on the template instance
Yes, much better. Another way to avoid your helper being called before the subscription has returned is to wrap the <div class="section"> block in a {{#if Template.subscriptionsReady}} block. Though this will not protect against the document missing even after the subscription has returned.