We have a template like this.
the-template.html
<template><div>${Foo}</div></template>
We want to do this with it.
some-file.ts
let htmlString = makeItHappen('the-template.html', { Foo = 'bar' });
console.info(htmlString); // <div>bar</div>
What is the equivalent of our makeItHappen
function?
Ok so here's the gist: https://gist.run/?id=d57489d279b69090fb20938bce614d3a
Here's the code in case that goes missing (with comments):
import {bindable} from 'aurelia-framework';
import {ViewLocator,ViewSlot,ViewEngine,ViewCompileInstruction} from 'aurelia-templating';
import {inject, Container} from 'aurelia-dependency-injection';
@inject(Element,ViewLocator,ViewEngine,Container)
export class LoadViewCustomAttribute {
@bindable view;
@bindable viewModel;
constructor(element,vl,ve,container) {
this.element = element;
this.vl = vl;
this.ve = ve;
this.container = container;
}
attached() {
// Get a view strategy for this view - this will let Aurelia know how you want to locate and load the view
var view = this.vl.getViewStrategy(this.view);
// Create a view factory from the view strategy (this loads the view and compiles it)
view.loadViewFactory(this.ve, new ViewCompileInstruction()).then(vf => {
// Create a view from the factory, passing the container (you can create a child container at this point if you want - this is what Aurelia usually does for child views)
var result = vf.create(this.container);
// Bind the view to the VM - I've passed the current VM as the override context which allows Aurelia to do away with the $parent trick
result.bind(this.viewModel, this);
console.log(result); // for inspection
// Optional - create a viewslot and add the result to the DOM -
// at this point you have a view, you can just look at the DOM
// fragment in the view if you want to pull out the HTML. Bear in
// mind, that if you do add to the ViewSlot - since nodes can only
// belong to 1 parent, they will be removed from the fragment in
// the resulting view (don't let this confuse you when debugging
// since Chrome shows a LIVE view of an object if you console.log(it)!)
// var vs = new ViewSlot(this.element, true);
// vs.add(result);
// Since you can't just get a fragments HTML as a string, you have to
// create an element, add the fragment and then look at the elements innerHTML...
var div = document.createElement('div');
div.appendChild(result.fragment);
console.log(div.innerHTML);
});
}
}
That should do it - and the usage:
<template>
<require from="load-view"></require>
<section>
<div load-view="view.bind: 'view-to-load.html'; view-model.bind: { someData: 'test' }"></div>
</section>
</template>
And finally view-to-load.html
<template>
<div>
this is the template... ${someData}
</div>
</template>
Obviously, this doesn't have to be a custom attribute - you can just inject the bits and compile in a helper class or something like that (which can just return the raw HTML string).
This would make the equivalent of your makeItHappen
function the attached
method in the custom attribute. Of course you need all the deps so you need to at least have Aurelias dependency injection support to get hold of those.
Note: I'd suggest always using a ViewSlot if you plan on adding the content to the DOM (assuming you have an element that can act as the anchor) since that's the way Aurelia works and it will have more consistent results since ViewSlots know how to add/remove grandchildren gracefully
This may not be possible in the case that you have a 3rd party plugin that accepts strings as template input - but if possible look for extension points that work with DOM nodes instead.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With