我需要知道如何刷新 Aurelia 中的绑定(bind)。我已经“谷歌搜索”有一段时间了,但似乎找不到答案。我需要刷新绑定(bind)的原因是因为一些 html(带有 click.delegate
绑定(bind))是在调用服务器检索数据后生成的。我正在使用一些“编辑”和“删除”按钮更新网格。不管怎样,当我使用 Durandal/KnockoutJS 时,我做了以下事情:
var body = this.element.find("tbody")[0];
if (body) {
ko.cleanNode(body);
ko.applyBindings(ko.dataFor(body), body);
}
我如何在 Aurelia 中做同样的事情?
更新:
感谢@fred-kleuver 的回复。我不确定这与我的情况相关,而且对于我想做的事情来说,这似乎有点矫枉过正。我可能需要按照您的建议进行操作,但在我深入研究所有问题之前,让我在这里提供有关我正在做的事情的更多详细信息,因为您可能会为我提供更简单的解决方案:
我正在使用 Kendo UI(2014 年初的旧 GPL 版本),不幸的是它无法与 Aurelia Kendo Bridge 一起使用。因此,我必须自己初始化 KendoGrid。我将如下代码复制到 Aurelia 的 attached()
生命周期方法中:
$("#grid").kendoGrid({
data: null,
dataSource: {
type: "odata",
transport: {
read: {
url: this.apiUrl,
dataType: "json"
},
parameterMap: function (options, operation) {
var paramMap = kendo.data.transports.odata.parameterMap(options);
if (paramMap.$inlinecount) {
if (paramMap.$inlinecount == "allpages") {
paramMap.$count = true;
}
delete paramMap.$inlinecount;
}
if (paramMap.$filter) {
paramMap.$filter = paramMap.$filter.replace(/substringof\((.+),(.*?)\)/, "contains($2,$1)");
}
return paramMap;
}
},
schema: {
data: function (data) {
return data.value;
},
total: function (data) {
return data["@odata.count"];
},
model: {
fields: {
Name: { type: "string" }
}
}
},
pageSize: this.gridPageSize,
serverPaging: true,
serverFiltering: true,
serverSorting: true,
sort: { field: "Name", dir: "asc" }
},
dataBound: function (e) {
var body = this.element.find("tbody")[0];
if (body) {
// TODO: Figure out how to do this in Aurelia
//ko.cleanNode(body);
//ko.applyBindings(ko.dataFor(body), body);
}
},
filterable: true,
sortable: {
allowUnsort: false
},
pageable: {
refresh: true
},
scrollable: false,
columns: [{
field: "Name",
title: this.translations.columns.name,
filterable: true
}, {
field: "Id",
title: " ",
template:
'<div class="btn-group">' +
'<button type="button" click.delegate="edit(#=Id#)" class="btn btn-default btn-xs">' + this.translations.edit + '</button>' +
'<button type="button" click.delegate="remove(#=Id#)" class="btn btn-danger btn-xs">' + this.translations.delete + '</button>' +
'</div>',
attributes: { "class": "text-center" },
filterable: false,
width: 120
}]
});
因此,对于网格的 dataBound
函数,我希望 Aurelia 刷新其绑定(bind)(以获取每行按钮上的单击绑定(bind))。
最佳答案
如果您要生成 html,则需要将其传递给 ViewCompiler,以便处理所有绑定(bind)(以及自定义元素、属性等)并开始工作。
我不久前编写了一个自定义元素,我可以在 View 中使用它,然后通过可绑定(bind)属性将生成的 html(以及绑定(bind)上下文)传递给它。这可能正是您所需要的,也可能是多余的。它是生产代码,因此有所有的 try/catch 内容。
在后一种情况下,只需关注我在 render() 方法中所做的事情,该方法包含编译、绑定(bind)和附加动态 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>
编辑:
好的,根据您更新的答案,生成的 html 中似乎没有任何 html 行为,因此您不需要调用生命周期。
如果不花费大量时间获得与您相同的设置,我就无法对此进行测试,因此我将向您提供一些尝试:
(对于 this.somethings
,只需将第一个字母大写 - 这将为您提供需要注入(inject)的 Aurelia 组件)
选项 1
使用TemplateEngine.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
完全替换body的innerHTML
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);
}
}
文档.addEventListener
您使用 Kendo 的方式已经在很大程度上绕过了 Aurelia,而且您甚至没有与任何东西进行数据绑定(bind)。现在你正在 build 自己的脆弱桥梁。
如果您使用的只是 click.delegate
那么为什么不在按钮上使用 .addEventListener("click", someFunction)
呢?
找到一个可用的桥梁或不使用剑道
我确信在您的应用上下文中可以有更简洁的方法来完成此任务,但如果您不提供 plunkr 重现或类似内容,则不可能提出任何“准确”的建议。
但如果您不能投入太多时间来学习 Aurelia,我建议您尝试找到开箱即用的组件。 aurelia-v-grid是原生 Aurelia 网格的一个很好的例子,它可能比半集成剑道桥为您做更多的事情。
关于javascript - 如何刷新绑定(bind)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50022746/