Skip to content

Instantly share code, notes, and snippets.

@kristianmandrup
Last active April 23, 2018 10:05
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kristianmandrup/b79aca9f7e314082404a02bfa0513465 to your computer and use it in GitHub Desktop.
Save kristianmandrup/b79aca9f7e314082404a02bfa0513465 to your computer and use it in GitHub Desktop.
Aurelia dynamic view based on dynamic models

How to write a generic View renderer

Taken in part from discussions/solutions mentioned [here]this aurelia/templating#35)

Please also look at view-manager and aurelia-form for inspiration. Maybe also look here for example of dynamic data grid with rows and columns :)

Notes: This works for me as well. I only had to change view.bind(this.bindingContext); to view.bind(this); as I wanted to bind to the model itself (not its parent) and initially failed on using click delegates.

Alternative!?

For this I use <compose> and InlineViewStrategy - just binding an InlineViewStrategy instance to the view property of <compose>.

<template>
    <compose view.bind="viewStrategy"></compose>
</template>
export class DynamicCustomElement {
    viewStrategy: InlineViewStrategy;
    ...
    bind(bindingContext: any, overrideContext: any) {
        ...
        this.viewStrategy = new InlineViewStrategy(`<template><${editorName} data.bind="data"></${editorName}></template>`, [dep1, dep2, ...]);
        ...
    }
    ...
}
import {inject, noView, processContent, bindable, TargetInstruction} from 'aurelia-framework';
import {InlineViewStrategy} from 'aurelia-templating';
@noView
@processContent((compiler, resources, element, instruction) => {
if (!!instruction.attributes.view) {
instruction.viewModel = instruction.attributes.viewModel;
} else {
let html = element.innerHTML;
if (html !== '') {
instruction.template = new InlineViewStrategy(`<template>${html}</template>`);
}
}
return true;
})
@inject(TargetInstruction)
export class Column {
@bindable header
@bindable viewModel
@bindable field
// styles
@bindable compact
constructor(targetInstruction) {
this.template = targetInstruction.elementInstruction.template;
this.viewModel = targetInstruction.elementInstruction.viewModel;
}
}
<tbody repeat.for="interface of interfaces">
...
<tr if.bind='interface.view != undefined'><td colspan='8' style="padding-left:50px"><inline-view model.bind='interface'></inline-view></td></tr>
</tbody>
import {inject, noView, ViewCompiler, ViewSlot, Container, ViewResources, bindable} from 'aurelia-framework';
@noView
@inject(ViewCompiler, ViewSlot, Container, ViewResources)
export class InlineView {
constructor(viewCompiler, viewSlot, container, viewResources){
this.viewCompiler = viewCompiler;
this.viewSlot = viewSlot;
this.container = container;
this.viewResources = viewResources;
}
attached() {
// Compile an html template, dom fragment or string into ViewFactory instance, capable of instantiating Views.
var viewFactory = this.viewCompiler.compile('<template>' + this.bindingContext.interface.view + '<template>', this.viewResources);
// Creates a view or returns one from the internal cache, if available
var view = viewFactory.create(this.container);
// Bind the view and it's children
view.bind(this.bindingContext);
// Add the view to the slot
this.viewSlot.add(view);
// Trigger the attach for the slot and its children.
this.viewSlot.attached();
}
bind(bindingContext) {
this.bindingContext = bindingContext;
}
}
<table class="ui compact striped table" border="1">
<tbody>
<tr repeat.for="row of rows">
<td repeat.for="column of columns" class="${!!column.compact ? 'collapsing' : ''}">
<compose if.bind="!!column.template" view.bind="column.template"></compose>
<compose if.bind="!!column.viewModel" view-model.bind="column.viewModel" model.bind="row"></compose>
${ (!column.template && !column.viewModel && !!column.field) ? row[column.field] : '' }
</td>
</tr>
</tbody>
</table>
import {inject, children, bindable} from 'aurelia-framework';
export class Grid {
@children('column') columns;
@bindable options;
rows = [
{ one: 'hello', two: 'world', three: 'test' },
{ one: 'goodnight', two: 'moon', three: 'test' }
];
constructor() {
setInterval(() => {
this.rows = [
{ one: Math.floor(Math.random() * 10), two: Math.floor(Math.random() * 10), three: Math.floor(Math.random() * 10) },
{ one: Math.floor(Math.random() * 10), two: Math.floor(Math.random() * 20), three: Math.floor(Math.random() * 10) }
];
}, 2000);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment