I have a row-based (table-like) custom element, which renders its template dynamically based on a bindable columns attribute. It composes the row template in string and uses ViewCompiler, ViewFactory and View to render.
import {inject} from 'aurelia-dependency-injection';
import {bindable, ViewCompiler, ViewResources} from 'aurelia-templating';
@inject(ViewCompiler, ViewResources)
export class MyDynamicGrid {
@bindable columns: any[];
@bindable rows: any[];
constructor(viewCompiler: ViewCompiler, viewResources: ViewResources) {
const template = '<template><custom-element></custom-element></template>'; //this is rather complex in practice
viewResources.registerElement('custom-element', /* HtmlBehaviorResource? */);
this._viewFactory = viewCompiler.compile(template, viewResources);
}
_render() : void {
const view = this._viewFactory.create(/* some container */);
view.bind(someContext, someOverrideContext);
//attach view to the DOM
}
}
This works fine, until the custom template contains standard HTML elements. Once I start putting custom elements in the template, it stops working. It still renders the HTML, but the custom element's behaviour is not being attached by Aurelia.
I'm aware, that all custom elements should be "registered" in order to be used. "Normal registration" happens either in the view via <require>
, or in the view-model @viewResources
, or registering it globally.
In this particular case, however, the injected ViewCompiler
only inherits the view resources of the view-model's parents. My question is: how can be any additional view-resources registered? I'm aware of the second parameter in ViewCompiler
's compile
method, but couldn't make it work. The only way I was able to make it work, if I register it globally.
Note: this question is focusing on registering view resources. The dynamic rendering works just fine
I've found a solution by diggin into the docs+github. I've created two samples for two different approaches:
HtmlBehaviorResource
instance manually, this is to register one specific element.Sample (based on: https://github.com/aurelia/templating-resources/blob/master/test/repeat-integration.spec.js)
import {CustomElement} from 'my-components/my-custom-element';
import {inject, Container} from 'aurelia-dependency-injection';
import {ViewCompiler, ViewResources} from 'aurelia-templating';
import {metadata} from 'aurelia-metadata';
@inject(ViewCompiler, ViewResources, Container)
export class MyDynamicGrid {
//bindables and constructor is ommitted
init(): void {
const resource: HtmlBehaviorResource = metadata.get(metadata.resource, CustomElement);
resource.initialize(this._container, CustomElement);
resource.load(this._container, CustomElement)
.then(() => {
resource.register(this._viewResources);
this._viewFactory = viewCompiler.compile(template, this._viewResources);
});
}
}
Remark: the line resource.register(this._viewResources);
is equivalent to this._viewResources.registerElement('custom-element', resource);
. The only difference is the first reads the name from convention or decorator.
ViewEngine
and import (a) whole module(s). This is more appropriate if importing multiple resources, even different types (attribute, element, converter, etc.) and from different files.Sample:
import {CustomElement} from 'my-components/my-custom-element';
import {inject} from 'aurelia-dependency-injection';
import {ViewCompiler, ViewResources, ViewEngine} from 'aurelia-templating';
@inject(ViewCompiler, ViewResources, ViewEngine)
export class MyDynamicGrid {
//bindables and constructor is ommitted
init(): void {
this._viewEngine
.importViewResources(['my-components/my-custom-element'], [undefined], this._viewResources)
.then(() => {
this._viewFactory = viewCompiler.compile(template, this._viewResources);
});
}
}