Flow Router + Meteor@1.5 = per route dynamic import

What if you have multip template options per route?

Using your example:

FlowRouter.route('/page/:_id', {
  name: 'page',
  action() {
    import('../../ui/pages/Page/Page.js').then(() => {
      BlazeLayout.render('layout', { main: 'Page', SideBars: 'Sidebar' });
    });
  },
});

How would you dynamically import both Page and Sidebar in this case?

I use Promise.all([]).then()

2 Likes

Promise.all? Sorry, I’m not familiar with Promise syntax, will you help with a code snippet @rmuratov?

FlowRouter.route('/page/:_id', {
  name: 'page',
  action() {
    Promise.all([
      import('../../ui/pages/Page/Page.js',
      import('../../ui/pages/Sidebar/Sidebar.js'
    ]).then(() => {
      BlazeLayout.render('layout', { main: 'Page', SideBars: 'Sidebar' });
    });
  },
});
3 Likes

Thanks @robfallows!

Just curious, why isn’t the original example wrapped in a promise? Is the import statement a wrapper on a promise?

FlowRouter.route('/page/:_id', {
  name: 'page',
  action() {
    import('../../ui/pages/Page/Page.js').then(() => {
      BlazeLayout.render('layout', { main: 'Page', SideBars: 'Sidebar' });
    });
  },
});

Yes - it returns a Promise.

You could also use async/await:

FlowRouter.route('/page/:_id', {
  name: 'page',
  async action() {
    await import('../../ui/pages/Page/Page.js');
    await import('../../ui/pages/Sidebar/Sidebar.js');
    BlazeLayout.render('layout', { main: 'Page', SideBars: 'Sidebar' });
  },
});

Although that could be slower, since it waits each time, whereas Promise.all runs them concurrently.

5 Likes

Very good. Thank you @robfallows!

1 Like

Or:

FlowRouter.route('/page/:_id', {
  name: 'page',
  async action() {
    await Promise.all([
      import('../../ui/pages/Page/Page.js'),
      import('../../ui/pages/Sidebar/Sidebar.js')
    ]);
    BlazeLayout.render('layout', { main: 'Page', SideBars: 'Sidebar' });
  },
});

source: http://2ality.com/2017/01/import-operator.html#async-functions-and-import

3 Likes

That’s a good solution if you don’t care about order of evaluation - so in this case, if Sidebar.js does not depend on Page.js, then Promise.all will evaluate faster because Promises in the array of Promises will be started concurrently.

However, they are not guaranteed to finish in the same order they were requested and in order to avoid complicating my answer I chose to use the form which is guaranteed to evaluate in order.

Some Promise packages have a Promise.waterfall method for this use case.

Ideally, one can conceive of a complex Promise construct which is able to intermix concurrent with waterfall evaluations to achieve speed and order of resolution where necessary.

2 Likes

I’m having issues trying to do that with Meteor 1.7.

If i only use “import” the error says: “import statements must be
at top-level scope”

if i use import(’…/path/to/my/file’) the error says: “unexpected (”

Have you added your own babel config which is conflicting with Meteor’s configuration?

Make sure you’re using import keyword with parenthesis (round brackets) like: import(/path/to/my/file')
This error very specific to the different import - ES replacement for require, and it is used with no parenthesis.

Tip: It’s always better to speak code, - share piece of the code where you’re having this exception, and 10-20 lines before/after that line.

1 Like

I haven’t, but i’m using coffeescript@2 on the project and having a .coffee file instead of regular .js

yes that’s what i’m doing.

will post a code snippet later, good idea.

mb coffee compiles code into the different result, or import keyword is used by coffeescript itself.
try to write this part purely in JS.

I am using Meteor 1.8.2 with dynamic import and it fails when I attempt to connect to the running pages via network.

Reproduction

  • Meteor create --full test
    This create a meteor project and can be accessed via network with route.js file as below
import { FlowRouter } from 'meteor/kadira:flow-router';
import { BlazeLayout } from 'meteor/kadira:blaze-layout';

// Import needed templates
import '../../ui/layouts/body/body.js';
import '../../ui/pages/home/home.js';
import '../../ui/pages/not-found/not-found.js';

// Set up all routes in the app
FlowRouter.route('/', {
  name: 'App.home',
  action() {
    BlazeLayout.render('App_body', { main: 'App_home' });
  },
});

FlowRouter.notFound = {
  action() {
    BlazeLayout.render('App_body', { main: 'App_notFound' });
  },
};

// At this point the pages are accessible via network (my.local.ip:3000 ) without any errors

  • Then edit the route file by introducing dynamic imports, using original FlowRouter
 FlowRouter.route('/', {
   name: 'App.home',
   action() {
     import('../../ui/pages/home/home.js').then(() => {
       BlazeLayout.render('App_body', { main: 'App_home'})
     })
   },  
});

or FlowRouter Extra

FlowRouter.route('/', {
  name: 'App.home',
  waitOn() {
     return import('/imports/ui/pages/home/home.js');
   },
    action() {
      this.render('App_body', { main: 'App_home'});
    }
});

both of them fail when you try to connect to this page via network
ie my.local.ip:3000 fails.

@dr.dimitru
Anyone with a solution please help. Regards;

Have you tried this?

FlowRouter.route('/', {
   name: 'App.home',
   action(params, queryParams, data) {
       require('../../ui/pages/home/home.js');
       BlazeLayout.render('App_body', { main: 'App_home'});
   },  
});
1 Like

Have you set your root url correctly it’s odd that it uses a different ip address.

This seems to work. Thank you!!!

Hello everyone, sorry for the late reply. See my answer here :man_technologist: