Dynamic Imports File Names

I’m curious to know if I can do an import dynamically, concatenating a string into a file path. Something like:

import {Home} from '/client/templates/' + whatever + '.jsx';

I might be way off base, but using FlowRouter with React I feel like I could dynamically import and mount with something like:

// Setup the route
  FlowRouter.route("/", {
    name: route,
    action (a,b,c) {
      /* Import the templates */
      import {Template} from '/client/templates/' + route +'.jsx';
      /* Import the content that we need */
      import {Content} from '/client/pages/' + route +'.jsx';
      /* Mount the content*/
      mount(Home, {
        header: <Navbar/>,
        content: <Content/>,
        footer: <Footer/>
      });
    }
  });

I guess the alternative could be something like this:

const Home = ({name}) => (<p>Hello, {name}</p>);

mount(Home, {name: 'whatever'});

I’m not entirely sure yet how to get the contents of the html file in the second example. Maybe readFileSync or something.

Perhaps I’m a little unclear on importing. I’m new to Meteor.

Thoughts? Ideas?

you could do it probably with require("/client/templates/"+whatever + ‘.jsx’) but thats generally a bad idea.

If imports are not static, the bundler cannot know which files to take into the bundle and which files not. It’s working (at the moment) with require, because meteor bundles all the files together.

Even if the code would be lazy loaded whenever an import or require is called, this could not work, because how would the server know which files can be loaded by the client? (ok you could solve this with a manifest, but still…)

Imports are static, so better just declare some kind of map for the possible templates and pages:

 import Home from '/client/pages/home.jsx';
 import About from '/client/pages/about.jsx';

const Pages = {Home, About, ...};

// somewhere else

const Content = Pages[route]; // route == "Home", or "About, ...

But sometimes code just should be stupid. In your case, i would manually set all the routes that you need without magic dynamic loading:

FlowRouter.route("/, {name: "home", action() { mount(MainLayout, {content: </Home />}}});
1 Like

Sometime code should just be stupid.

That’s awesome.

So what’s the plan if you have 200 templates/pages/content (whatever)?

I’m going to guess that you’re not using 200 pages, rather you’re abstracting them out into like 5 or 10 and is accessing dynamic data.

Do you actually have an application with 200 templates/pages/contents ?

No. I’m more-or-less trying to understand the patterning. I’m new to Meteor, and coming from a LAMP background, I’m trying to keep things DRY while simultaneously learning/constructing a basic file structure that would innately keep my code base smaller. (If that makes sense).

So, for instance, you could potentially use two to three directories, and the files they contain, to do all your routing and mounting dynamically. So you’d never have to write that code again.

Beeing DRY is always good, however there is also the saying “you ain’t gonna need it” :wink:

I think its always best to learn a new technology by solving a real problem with it. Maybe you one day really need to route 200 different files and layouts, but then you would build more like a framework, then an app.

As for filestructure, would take a look at the official guide: https://guide.meteor.com/structure.html

Also the mantra-paradigm is a good architecture pattern: https://github.com/kadirahq/mantra

You can’t dynamically import on the client because the bundler needs to be able to follow all of the imports ahead of time.

1 Like

Can you just import the HTM for FlowRouter mount through an FS object or something?

Again, this is more exploratory than anything.

You can’t use FS from the client either. I guess there might be some bundling systems out there that support this kind of totally dynamic importing but I’m not aware of any.

Could you use a collection from the server?

You probably could - I guess I’m just not sure why this is necessary though.

Wouldn’t using FS to import html conceivably remove what could be a large block of code, not only for the current project, but all projects you might undertake going forward? Like automating FlowRouter via the filesystem.

I disagree, routing files are only a small part of the application.

Also on my current project, some routes have special params, layouts, etc. I don’t see the point of generalizing that.

1 Like

It seems foundational to me, but again I’m new to this, and might not be abstracting it properly. But it seems like if you could dynamically import, using the filesystem as your abstract, you could basically just start dropping files into directories and viola build out your app.

Guys it’s module based approach, this is what module based approach states, each dependency should be specified (aka imported) when used.

https://github.com/cult-of-coders/meteor-react-routing this is what I use to make my route file less cluttered :slight_smile:

1 Like

To confirm, when you say:

Couldn’t routing innately do that by including that in a very simple loop, without having to import 1,2,5,100 lines of code?

Wasn’t sure about reviving this topic, but this seems to be the only place I’ve seen conversation about “dynamic, dynamic imports”.

My use case was using Blaze.renderWithData to create some dialogs, which is done by passing in the name of the dialog template you want to use and the data. I also wanted to be able to dynamically import the templates, just based on the string being passed in:

{
  this.template = 'someTemplateName';
  this.data = {};

  await import(`/imports/ui/components/dialogs/${this.template}.html`);

  Blaze.renderWithData(
    Template[this.template], this.data, document.querySelector('body')
  );
}

Of course, this doesn’t work as the build system didn’t know it had to look for this template.

So I added this to client-side main.js:

Meteor.startup(async () => {
  if (true === false) {
    await import('/imports/ui/components/dialogs/someTemplateName.html');
  }
});

Voila! The initial import is now working. It also worked using an index file:

// imports/components/ui/dialogs/index.js
import './someTemplateName.html;

and

Meteor.startup(async () => {
  if (true === false) {
    await import('/imports/ui/components/dialogs');
  }
});

This got me thinking about either:

  1. Using index files to build a recursive tree so that I could import the root index file in my startup function OR
  2. Building a package to evaluate all of the files and create startup code that would “ready” them for “dynamic, dynamic imports”

Thoughts?

1 Like