如何刷新绑定? [英] How to refresh bindings?

查看:70
本文介绍了如何刷新绑定?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要知道如何刷新 Aurelia 中的绑定.我已经搜索"一段时间了,但似乎找不到答案.我需要刷新绑定的原因是因为某些html(带有click.delegate绑定)是在调用服务器以检索数据之后生成的.我正在使用一些"编辑"和"删除"按钮更新网格.无论如何,当我使用 Durandal/KnockoutJS 时,我做了以下事情:

I need to know how to refresh the bindings in Aurelia. I have been "Googling" for some time now, but can't seem to find the answer. The reason I need to refresh bindings is because some of the html (with click.delegate bindings) is generated after a call to server to retrieve data. I am updating a grid with some "Edit" and "Delete" buttons. Anyway, when I was using Durandal / KnockoutJS, I did the following:

var body = this.element.find("tbody")[0];
if (body) {
    ko.cleanNode(body);
    ko.applyBindings(ko.dataFor(body), body);
}

我如何在Aurelia中做同样的事情?

How do I do the same thing in Aurelia?

更新:

感谢@ fred-kleuver的回复.我不确定这与我的情况是否相关,对于我想做的事情似乎有些过头了.可能是我需要按照您的建议进行操作,但是在我深入研究所有内容之前,请允许我在此处提供有关我正在做的事情的更多详细信息,因为您可能会为我找到一个更简单的解决方案:

Thanks @fred-kleuver for your response. I am not sure that is relevant in my case and it does seem like overkill for what I want to do. It may be that I need to do as you've suggested, but before I delve into figuring that all out, let me provide further details here of exactly what I am doing, as you might have a simpler solution for me:

我正在使用Kendo UI(2014年初的旧GPL版本),不幸的是,该版本不适用于Aurelia Kendo Bridge.因此,我必须自己初始化KendoGrid.我将以下代码复制到Aurelia的attached()生命周期方法中:

I am using Kendo UI (the old GPL version from early 2014), which unfortunately doesn't work with the Aurelia Kendo Bridge. Therefore, I have to initialize the KendoGrid myself. I am copying over code as follows into Aurelia's attached() lifecycle method:

$("#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刷新其绑定(以拾取每一行按钮上的单击绑定).

So for the grid's dataBound function, I want Aurelia to refresh it's bindings (to pickup the click bindings on the buttons in each row).

推荐答案

如果要生成html,则需要将其通过ViewCompiler,以便处理所有绑定(以及自定义元素,属性等)并启动工作.

If you're generating html you'll need to pass it through the ViewCompiler for all bindings (and custom elements, attributes, etc) to be processed and start working.

不久前,我写了一个自定义元素,可以在视图中使用它,然后通过可绑定属性将生成的html(以及绑定上下文)传递给它.这可能正是您所需要的,或者可能是过大了.它是生产代码,因此是所有try/catch的东西.

I wrote a custom element some time ago that I could use in a view and then pass generated html (as well as a binding context) to it via a bindable property. This may be just what you need, or it may be overkill. It's production code, hence all the try/catch stuff.

在后一种情况下,只需专注于render()方法中的工作即可,该方法包含编译,绑定和附加动态html的必要步骤.

In the latter case just focus on what I'm doing in the render() method which contains the necessary steps to compile, bind and attach dynamic html.

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;
  }
}

用法(当然,您将使用变量而不是内联):

Usage (of course you'd use variables instead of inlines):

<runtime-view
    html.bind="'<some-element some-property.bind="value"></some-element>'"
    context.bind="{ value: 'text' }">
</runtime-view>

好吧,根据更新后的答案,似乎您生成的html中没有任何html行为,因此您无需调用生命周期.

Ok, based on your updated answer it seems you don't have any html behaviors in the generated html, so you won't need to invoke the lifecycles.

如果不花大量的时间来获得与您相同的设置,我将无法进行测试,因此,我将为您提供一些尝试方法:

I can't test this without spending a fair amount of time getting the same setup as you, so I'll give you a few things to try:

(对于this.somethings,只需将第一个字母大写-为您提供需要注入的Aurelia组件)

(as for this.somethings, just capitalize the first letter - that gives you the Aurelia component you need to inject)

使用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

完全替换正文的innerHTML

Option 3

Completely replace the body's 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);
    }
}

document.addEventListener

您已经在很大程度上以使用Kendo的方式绕过Aurelia,并且您甚至没有对任何内容进行数据绑定.现在,您有点像在建立自己的脆性桥梁.

document.addEventListener

You're already largely bypassing Aurelia with the way you're using Kendo, and you're not even databinding to anything. Now you're kind of making your own brittle bridge.

如果您只使用click.delegate,为什么不只在按钮上使用.addEventListener("click", someFunction)?

If all you're using is click.delegate then why not just use .addEventListener("click", someFunction) on the buttons?

我敢肯定,有很多更清洁的方法可以在您的应用程序上下文中完成此操作,但是,如果您不提供plunkr repro或类似的东西,就不可能提出任何建议"的建议.

I'm sure there are much cleaner ways to accomplish this in the context of your app, but it's impossible to make any "spot-on" suggestions if you don't provide a plunkr repro or something similar.

但是,如果您不能花太多时间在学习Aurelia上,那么我建议您尝试寻找开箱即用的组件. aurelia-v-grid 是本地Aurelia网格的一个很好的例子,它可以为你比半集成的剑道桥更重要.

But I'd recommend to try and find components that work out-of-the-box if you can't invest too much time in learning Aurelia. aurelia-v-grid is a great example of a native Aurelia grid that can probably do a lot more for you than a semi-integrated kendo bridge.

这篇关于如何刷新绑定?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆