Greetings! I am building an application for customizing the product grid order of an e-commerce website. Being able to present product order in responsive and flexible ways, would really make it easier for project managers to create a better experience for their customers.
Part of this grid order application is based on an article by Ryan Glover (to whom I owe many thanks), called Importing CSV’s https://themeteorchef.com/tutorials/importing-csvs, using PapaParse.
I am a UX Developer, and still being relatively new to Meteor, I am developing the MVP (minimally viable product), in a very vanilla, or stripped down way - first, and then refactoring it.
Here’s my pretty stripped down question: How do I get the event object
into the template
? I get the event object
fine from the Papa.parse()
function, and could console.log(it)
. But, how do I pass the event object
into the template.
import { Template } from 'meteor/templating';
import { ReactiveVar } from 'meteor/reactive-var';
import './main.html';
// NOTE: trying to figure out how to pass event object to template
Template.upload.events({
'change [name="uploadCSV"]' ( event, template ) {
// Handles the conversion and upload
var fileInput = document.querySelector('[name="uploadCSV"]');
// Parse local CSV file
Papa.parse(fileInput.files[0], {
header: true,
complete: function(results) {
// console.log(results); // includes data, error, and misc
// NOTE: This is the data object to send to the template
let itemData = results.data;
console.log(itemData) // here is the data object
// This test correctly iterates over the object, but should be done in the template
itemData.forEach(function(item) {
console.log(item)
// console.log(item['itemcode'])
});
// HELP! How do I send the object itemData to the template?
} // END complete
}); // END parse
} // END change
}) // END events
Here is a link to the repo. https://github.com/dylannirvana/gridorderapp/tree/master/meteor/vanilla
This has got to be embarrassingly easy. Many thanks in advance!
The answer is to use the ReactiveVar you are already importing.
In your template’s onCreated define a new reactive var to hold the result.
this.something = new ReactiveVar();
Then, in your event, set the variable using the template parameter
template.something.set(itemData);
You can then use the data within a reactive context, such as a template helper. Note that the reactive var is referenced with
Template.instance().something.get();
in a template helper.
Apologies for the formatting - I’m on my phone at the moment.
Thank you @robfallows. Looks right; I am going to give this a swirl right now…
Very close. I keep hitting a type error. Updating repo now.
Uncaught TypeError: Cannot read property 'set' of undefined
at Object.complete (main.js:46)
Can you post your onCreated
function block?
import { ReactiveVar } from 'meteor/reactive-var';
import './main.html';
Template.grid.onCreated( () => {
Template.instance().grid = new ReactiveVar();
})
Template.upload.helpers({
})
Template.upload.events({
'change [name="uploadCSV"]' ( event, template ) {
var fileInput = document.querySelector('[name="uploadCSV"]');
Papa.parse(fileInput.files[0], {
header: true,
complete: function(results) {
let itemData = results.data;
console.log(itemData)
// return template.grid.set(itemData)
Template.instance().grid.set(itemData)
} // END complete
}); // END parse
} // END change
}) // END events
Check my post again. In the onCreated you need to use
this.grid = new ReactiveVar();
Still getting the TypeError for the setter.
Template.instance().grid.set(itemData)
}```
_Uncaught TypeError: Cannot read property 'set' of undefined_
The `console.log(itemData)` is fine. Just no yet rendered data to the template.
I cleaned up main.js though, and updated the GitHub repo: https://github.com/dylannirvana/gridorderapp/tree/master/meteor/vanilla
I’m finally able to read this on a computer and I see the problem: you have different templates for the ReactiveVar
instantiation (grid
) and use (upload
). So, the template contexts are not the same (this
in grid
is not Template.instance()
in upload
).
There are ways to resolve this cleanly (for example, passing the context down the templates, much like props in React), but the easiest (not clean) way is to use a global ReactiveVar
. So, instead of using this.grid
and Template.instance().grid
, use window.grid
everywhere.
Hopefully, that will get you started.
Thank you @robfallows. I cleaned things up in the repo. Put everything in the same template context. Tried ReactiveVar with a setter (TypeError). Made a collection. Subbed out the window object. Close but still no cigar.
Template.grid.onCreated( () => {
Template.instance().grid = new ReactiveVar();
})
Template.grid.helpers({ // All the same template now
grid() {
// return Template.instance().grid.get();
// return Grid.find({})
},
})
Template.grid.events({
'change [name="uploadCSV"]' ( event, template ) {
const inventory = event.target.files[0];
Papa.parse(inventory, {
header: true,
complete: function(results) {
let itemData = results.data;
console.log(itemData); // works
Template.instance().grid.set(itemData) // Why doesn't this work?
// console: grid.js:28 Uncaught TypeError: Cannot read property 'grid' of null
at Object.complete (grid.js:28) // ugh!
// // With collection
// Grid.insert({
// itemData,
// })
} // END complete
}); // END parse
} // END change
}) // END events
The problem in the event handler is that you’re calling Template.instance
in the callback of an async method, at which time, you’re no longer in the template’s scope and Template.instance
won’t work.
Either save it to a variable before making the async call, or better, use the one passed into the handler as the second argument:
Template.grid.events({
'change [name="uploadCSV"]' ( event, template ) {
const inventory = event.target.files[0];
Papa.parse(inventory, {
header: true,
complete: function(results) { // Because this function is called later, it doesn't run in this scope
let itemData = results.data;
console.log(itemData); // works
template.grid.set(itemData) // use the instance passed as second paramater
} // END complete
}); // END parse
} // END change
}) // END events
I wonder if part of this also stems from the use of an arrow funciton in onCreated
, since the this
binding is actually important in lifecycle hooks. I assumed that you could still use Template.instance
inside it just fine but maybe not?
Try using a regular function instead, where this
is the template instance:
Template.grid.onCreated(function () {
this.grid = new ReactiveVar();
})
Or if you prefer being explicit, keep the Template.instance()
, but still use a regular function.