javascript - 如何刷新绑定(bind)?

标签 javascript aurelia

我需要知道如何刷新 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/

相关文章:

javascript - 将 MySQL 数据从 Flask 传递到 JavaScript 以用于 Google Charts

javascript - Aurelia - 更改表格中选定行的颜色

browser - Edge 不显示带有我可以在其他浏览器中看到的 Aurelia 绑定(bind)的 div?

Aurelia:如何在现有 html 中注入(inject)组件模板?

javascript - JS 脚本应该放在 HTML 文件中的什么位置?

javascript - 将包含 <span> 的 div 文本替换为图标

javascript - 如何使用 Canvas 调整图像大小然后裁剪图像

javascript - 基于 redux 状态控制 react 组件的正确方法是什么

javascript - Aurelia 验证 - 停止属性名称出现在错误消息中

javascript - Aurelia show.bind 是否有回调或 promise ?