Dynamic compose


#1

Hi,

I would to use compose element for real dynamic creation. I mean, I want to set the view and viewModel from string variable.

<compose view.bind=“viewHtml” view-model.bind=“viewModelCode” ></compose>
where
viewHtml="<template>apple</template>"
and
viewModelCode=“export class JSCode {}” <-!!!

Is there a way to set viewModel of compose element with javascript source code at runtime?


#2

Can you describe a bit about your scenario? I think this is possible, but probably requires a bit of code for patching loader + and/or creating a different <compose/> element.


#3

That’s sounds hopeful.

I have a contentWindow page. I would use it for different content. The view and view model of content comes from a restful service. (The service creates them dynamically)

So I would create ( compose ) an object/content from a restful web service source, what I want to embed into the contentWindow (with tag).

I have investigated the aurelia’s composition-engine source code and I wanted to figure out,how should I create viewModel instance from the source, but I don’t understand fully yet.


#4

Composition engine accepts:

  • a constructor
  • an object
  • a string pointing to a module with an exported class

From the above, I think it depends on how you would want to do it, but you can tell requirejs/fusebox/systemjs (not webpack as it’s not a loader) to load module dynamically:

The following is some fake/pseudo code, as I don’t know the exact API of the loaders:

require.add('fake-module-name', function(onLoad, onError) {
  return fetch('https://my-end-point/my-new-module')
    .then(r => r.text())
    .then(onLoad, onError);
});

Then in your composition engine, you can just point it to there:

<compose view-model="fake-module-name"></compose>

About other too, they are also easy to do, but there are details to cover, so it varies based on project.


I just want to emphasize that the nature of Aurelia is ultra dynamic, when combined with loader that supports dynamic code, you get … easy time :smile:


#5

Thank you for your suggestion, I’ve made a simple “loader” for my self, because it usable with webpack too, and I use it for make a viewModel class for compose.


#6

Do you mind sharing loader, if it works with webpack? :slight_smile:


#7

Hi,

I guess, it should work with webpack, because it’s independent from any loader/bundler, but actually I’ve not tested.

load(contentName) {
self=this;
if (! (contentName in this.content) ) {
this.content[contentName]={};
this.app.http.fetch(’/ui?name=’+contentName)
.then( resp => resp.text())
.then( data=> {
let ui=JSON.parse(data);
loadClass(ui.viewModel).then( (c)=> {
this.content[contentName].view=ui.view;
this.content[contentName].viewModel=new c(self);
this.content[contentName].title=this.toTitleCase(contentName);
self.contentView=new InlineViewStrategy(this.content[contentName].view);
self.contentViewModel=this.content[contentName].viewModel;
});
})
.catch(error => { throw new Error(error);} );
} else {
self.contentView=new InlineViewStrategy(this.content[contentName].view);
self.contentViewModel=this.content[contentName].viewModel;
}
}

loadClass(code) {
return new Promise((resolve, reject) => {
let node = document.createElement(“script”);
node.type = “module”;
node.async=true;
let classId = “dynClass”+Math.floor(Math.random() * 1000000);
let code2=" “+code;
let m=new RegExp(”[ ]+export[ ]+default[ ]+class[ ]+([^{]+)", ‘g’).exec(code2);
if (m==null) m=new RegExp("[ ]+export[ ]+class[ ]+([^{]+)", ‘g’).exec(code2);
if (m==null) throw new Error(“Exported class not found !”);
node.src = URL.createObjectURL( new Blob([code+\n window.${classId}=${m[1]};], {“type”:“text/javascript”}) );
node.onload = (m) => {
resolve(window[classId]);
destroy();
};
node.onerror = (m) => {
reject(new Error(loadClass error));
destroy();
};
let destroy = () => {
delete window[classId];
node.onerror = null;
node.onload = null;
URL.revokeObjectURL(node.src);
node.src = null;
node.remove();
};
document.head.appendChild(node);
});
}


#8

Thank you so much. As you said it works like a charm in a webpack setup as well.


#9

You’re welcome, great to hear that :slight_smile: