Import templates dynamically on click event

I am trying to figure out how I can import code when an event handler in a template is clicked. The following unfortunately doesn’t work. And when I use require it doesn’t find the file

	'click .edit-preference': async function editPreferenceClick(event, instance){
		const template = event.currentTarget.dataset.preferenceTemplate;
		// await import `/imports/client/account/preference/${template}.js`;
		// require(`/imports/client/account/preference/${template}.js`);
		instance.data.parent.template.set(template);
	},

Here is how Meteor Candy does it:

var open = function () {
	import("./imports/startup.js").then(function (api) {
		api.toggle();
	})
}

You would just need to run than open function inside of a click event.

Inside imports/startup.js, its just:

import { CandyTools } from "meteor/meteor-candy" // 
import "./views" // the Blaze Templates

var toggle = function () {
	return CandyTools.general.toggle();
}

export { toggle }

To import a Blaze template into a module, you just need to place it inside an imports directory, and then import it like this:

import "templates.html"

Both those examples are static imports. Dynamic imports are functions:

const x = await import('x');

Not true - when you run import() as a function, it returns a promise and imports the code.

Thank you for responding, but I get Unhandled Promise Rejection: Error: Cannot find module '/imports/client/account/preferences/movies.js' when I try that. The file does exist, though. When I import the same path outside of the click handler it works.

As the await clearly shows.

I think where the confusion might be is… if you put this right into your file, it will be imported right away because the code will be ran when the file is loaded. However, if you put it into a function, then it will not import until that function runs.

That is clear and the desired behavior. What I don’t understand is how the path is being resolved when run inside a function. I can use an import statement in an onCreated function no problem, but the same does not work in the even handler function

I was replying to @robfallows there.

I have no idea why you are having this paths issue. If you can post the two code samples maybe I could better help you. Also - maybe its worth to try putting the import() call inside of a function and then have your event handler call that?

Hi,
Maybe you need to use relative path instead of absolute path.

Thanks, but I tried that as well, with the same results

The issue here is that the imported module is generated at runtime.
Meteor’s build process builds a graph of all files that are imported or required using static analysis. It then bundles the referenced files together to make them available.

When you create the import string dynamically at runtime, Meteor can’t know at build time which file that points to and include it in the bundle

The solution (At least my dumb solution) was to create a module white-list as part of startup:

/**
 * This file exists to provide static references to Meteor's dynamic imports
 * implementation so they can be requested dynamically on the client
 * see: https://github.com/meteor/meteor/pull/8327#issuecomment-280876264
 */
// eslint-disable-next no-constant-condition
if (false) {
    import('meteor/dg:base');
    import('meteor/dg:base/client/modules/m_scrollingGrid');
    import('meteor/dg:base/client/modules/m_nav');
    import('meteor/dg:base/client/modules/m_horiz');
    import('meteor/dg:base/client/modules/m_vertical');
    import('meteor/dg:base/client/modules/m_video');
    import('meteor/dg:base/client/modules/m_mediaTile');
    import('meteor/dg:base/client/modules/m_debugFill');
    import('meteor/dg:base/client/modules/m_dynamicSlides');
    import('meteor/dg:base/client/modules/m_dynamicThumbs');
    import('meteor/dg:base/client/modules/m_image');
    import('meteor/dg:base/client/modules/m_imageDeck');
    import('meteor/dg:base/client/modules/m_menu');
    import('meteor/dg:base/client/pages/p_fillColour');
    import('meteor/dg:base/client/pages/p_fillImage');
    import('meteor/dg:base/client/pages/p_loading');
    import('meteor/dg:base/client/pages/p_mediaModal');
    import('meteor/dg:base/client/modules/m_text');
    import('meteor/dg:base/client/modules/m_3dText');
}

Just by including these statements, the referenced files are made available to the dynamic imports system!

3 Likes

Thank you very much for the explanation and solution :grinning: