Loading Components from outside the application

Background
I’m using Aurelia 1 in an application with a jspm build.
I’m transpiling to es5.1 and building in some basic polyfills/shims for things like Fetch, Promise, and webcomponents in order to support IE11 (argh, I know).

Without getting super-into unnecessary details, my users include 3rd party developers, and one of my extensibility points for user-devs is being able to tailor page life cycles, pulling in files they’ve written.

Some limitations I impose on those files: they’re expected already to be transpiled into es5.1 (I’m not attempting to support transpilation of client code) so that it’s compatible in IE.

The issue
I’m exposing dialog service indirectly through an api given to our extensibility points, and I wanted to allow the user-devs to be able to include Aurelia Components in the files I pull-in for them, to use them in the dialog service call that the api boils into.

What I’ve done so far
I took a look at the bundle file my app build generates to see what the components look like post-transpilation, to get an idea of how the user-supplied file might need to look if it were supplied now, without any additional support on my end for simplifying things.

I realize this is build-strategy-specific and would fall through were I to switch to a webpack build on my end. But as-is, I’m seeing the inputs within my bundle file for each component appears to be:

  1. System.amdDefine for view (html)
  2. System.register for viewModel (js)

It looked to me like Aurelia must associate the viewModel and the view by a convention where the name used in the calls 1+2 above were something like:

var path = "somePath/index";
var js = ".js";
var html = ".html!github:systemjs/plugin-text@0.0.9.js";
var jsKey = path + js;
var htmlKey = path + html;

So maybe Aurelia’s convention might be looking for a view registered with the matching “path” portion of the key.

To test this out, I wrote a really simple component which holds a model with two trivial properties name and adjective. The view is just a div with text inside of it:

var name = "UserComponent";
var UserComponent;
System.register((jsKey), [], function (_export, _context) {
    return {
        setters: [],
        execute: function () {
            _export(name, UserComponent = function () {
                function UserComponent() {}
                UserComponent.prototype.attached = function attached() {};
                UserComponent.prototype.activate = function activate(model) {
                    this.model = model;
                }
                return UserComponent;
            });
            _export(name, UserComponent);
        }
    };
});
var define = System.amdDefine;
define((htmlKey), [], function () {
    return "<template><div>Hello ${model.name} this is a {model.adjective} user-supplied Aurelia component.</div></template>";
}

Looking at the way this worked in practice, I noticed that the callbacks for the define and register only execute on the first attempt to pull them, and so I’ve added something like this:

System.import(htmlKey).then(function (module) { /* not important */ });
System.import(jsKey).then(function (module) { /* not important */ });

My attempting to use the component looks like this:

// user code
api.openModal(UserControl, { name: "Gene", adjective: "Groovy" });

// api code
api.openModal = function(type, model, cb) {
    return this.dialogService.open({
        viewModel: type,
        model: model
    }).then(function (openDialogResult){
        if (typeof cb === "function) { cb(openDialogResult); }
        return openDialogResult.closeResult
    });
};

Looking into Aurelia, it seems that

_aureliaPal.PLATFORM.eachModule

contains the both items. However getViewStrategy fails by exception because Origin.get returns unknownOrigin. The error thrown is: "Cannot determine default view strategy for object."

Ultimate questions

  1. Is there something I can do within my current approach which will enable Aurelia to determine the viewStrategy, and tie my view to the viewModel?
  2. Is there something available within Aurelia which would allow me to the what I’m trying to do without having to care about whether this is System-based or not?
1 Like

There’re a few way to let your component know where to load your view.

One is remote view strategy example https://codesandbox.io/s/8ljmrq3vql

With that in place, you need not to care about whatever the bundler is. Can you give it a try?