Если вы генерируете html, вам нужно пропустить его через ViewCompiler, чтобы все привязки (и пользовательские элементы, атрибуты и т. Д.) Были обработаны и начали работать.
Некоторое время назад я написал пользовательский элемент, который мог бы использовать в представлении, а затем передать ему сгенерированный html (а также контекст привязки) через связываемое свойство. Это может быть только то, что вам нужно, или это может быть излишним. Это производственный код, отсюда и все, что можно попробовать / поймать.
В последнем случае просто сконцентрируйтесь на том, что я делаю в методе render()
, который содержит необходимые шаги для компиляции, связывания и присоединения динамического HTML.
TLDR: «мясо» полностью внизу, в render()
import { bindingMode, createOverrideContext } from "aurelia-binding";
import { Container } from "aurelia-dependency-injection";
import { TaskQueue } from "aurelia-task-queue";
import { bindable, customElement, inlineView, ViewCompiler, ViewResources, ViewSlot } from "aurelia-templating";
@customElement("runtime-view")
@inlineView("<template><div></div></template>")
export class RuntimeView {
@bindable({ defaultBindingMode: bindingMode.toView })
public html: string;
@bindable({ defaultBindingMode: bindingMode.toView })
public context: any;
public el: HTMLElement;
public slot: ViewSlot;
public bindingContext: any;
public overrideContext: any;
public isAttached: boolean;
public isRendered: boolean;
public needsRender: boolean;
private tq: TaskQueue;
private container: Container;
private viewCompiler: ViewCompiler;
constructor(el: Element, tq: TaskQueue, container: Container, viewCompiler: ViewCompiler) {
this.el = el as HTMLElement;
this.tq = tq;
this.container = container;
this.viewCompiler = viewCompiler;
this.slot = this.bindingContext = this.overrideContext = null;
this.isAttached = this.isRendered = this.needsRender = false;
}
public bind(bindingContext: any, overrideContext: any): void {
this.bindingContext = this.context || bindingContext.context || bindingContext;
this.overrideContext = createOverrideContext(this.bindingContext, overrideContext);
this.htmlChanged();
}
public unbind(): void {
this.bindingContext = null;
this.overrideContext = null;
}
public attached(): void {
this.slot = new ViewSlot(this.el.firstElementChild || this.el, true);
this.isAttached = true;
this.tq.queueMicroTask(() => {
this.tryRender();
});
}
public detached(): void {
this.isAttached = false;
if (this.isRendered) {
this.cleanUp();
}
this.slot = null;
}
private htmlChanged(): void {
this.tq.queueMicroTask(() => {
this.tryRender();
});
}
private contextChanged(): void {
this.tq.queueMicroTask(() => {
this.tryRender();
});
}
private tryRender(): void {
if (this.isAttached) {
if (this.isRendered) {
this.cleanUp();
}
try {
this.tq.queueMicroTask(() => {
this.render();
});
} catch (e) {
this.tq.queueMicroTask(() => {
this.render(`<template>${e.message}</template>`);
});
}
}
}
private cleanUp(): void {
try {
this.slot.detached();
} catch (e) {}
try {
this.slot.unbind();
} catch (e) {}
try {
this.slot.removeAll();
} catch (e) {}
this.isRendered = false;
}
private render(message?: string): void {
if (this.isRendered) {
this.cleanUp();
}
const template = `<template>${message || this.html}</template>`;
const viewResources = this.container.get(ViewResources) as ViewResources;
const childContainer = this.container.createChild();
const factory = this.viewCompiler.compile(template, viewResources);
const view = factory.create(childContainer);
this.slot.add(view);
this.slot.bind(this.bindingContext, this.overrideContext);
this.slot.attached();
this.isRendered = true;
}
}
Использование (конечно, вы бы использовали переменные вместо встроенных):
<runtime-view
html.bind="'<some-element some-property.bind="value"></some-element>'"
context.bind="{ value: 'text' }">
</runtime-view>
EDIT:
Хорошо, на основании вашего обновленного ответа кажется, что в сгенерированном html нет поведения html, поэтому вам не нужно вызывать жизненные циклы.
Я не могу проверить это, не потратив достаточно времени на то, чтобы получить ту же настройку, что и у вас, поэтому я дам вам несколько вещей, чтобы попробовать:
(что касается this.somethings
, просто наберите первую букву с заглавной буквы - это дает вам компонент Aurelia, который вам нужно ввести)
Вариант 1
Использование TemplatingEngine.enhance
dataBound: e => {
const body = document.querySelector("#grid tbody");
if (body) {
this.templatingEngine.enhance({ element: body, bindingContext: this });
}
}
Вариант 2
Вручную улучшить tbody
на месте
dataBound: e => {
const body = document.querySelector("#grid tbody");
if (body) {
const factory = this.viewCompiler.compile(body);
factory.create(this.container, { enhance: true });
}
}
Вариант 3
полностью заменить внутреннюю часть тела HTML
dataBound: e => {
const body = document.querySelector("#grid tbody")
if (body) {
const html = body.innerHTML;
body.innerHTML = "";
const factory = this.viewCompiler.compile(html);
const view = factory.create(this.container);
const slot = new ViewSlot(body, true);
slot.add(view);
}
}
document.addEventListener
Вы уже в значительной степени обходите Аурелию тем, как используете Кендо, и вы даже не привязываете данные ни к чему. Теперь вы создаете свой собственный хрупкий мост.
Если все, что вы используете, это click.delegate
, то почему бы просто не использовать .addEventListener("click", someFunction)
на кнопках?
Найти рабочий мост или не использовать кендо
Я уверен, что есть намного более чистые способы сделать это в контексте вашего приложения, но невозможно сделать какие-либо «точечные» предложения, если вы не предоставите репродукцию plunkr или что-то подобное.
Но я бы порекомендовал попробовать и найти компоненты, которые работают «из коробки», если вы не можете тратить слишком много времени на изучение Aurelia. aurelia-v-grid - отличный пример нативной сетки Aurelia, которая, вероятно, может сделать для вас гораздо больше, чем полуинтегрированный мост кендо.