Aurelia动态绑定

时间:2016-03-31 02:35:50

标签: aurelia aurelia-binding

我创建了一个生成表格数据的自定义元素。出于好的理由,这会生成实际的HTML并在不使用模板的情况下插入DOM。

我需要将click观察者附加到特定元素,以便我可以在自定义元素中运行一个函数以响应单击。如果使用模板,我会使用click.delegate,但我无法使用生成的HTML。

除了使用jQuery之外,如何使用Aurelia附加事件处理程序?

1 个答案:

答案 0 :(得分:7)

我知道这个答案已经很晚了,但是如果还没有(正确)解决这个问题和/或其他人在将来发现这个问题:

为了使任何aurelia行为在动态生成的HTML中有效,您需要编译该HTML。

我已经开发了一个自定义元素(基于aurelia' s enhancecompose如何工作),它允许您传入一串HTML并将其编译,因此任何行为,如可绑定,自定义元素/属性将只是工作。当html发生变化时,它也会重新编译。

以下是一个例子:https://gist.run?id=1960218b52ba628f73774822aef55ad7

<强>的src / app.html

<template> 
  <dynamic-html html.bind="dynamicHtml"></dynamic-html>
</template>

<强>的src / app.ts

export class App {
  public dynamicHtml: string = `
<button click.delegate="handleClick()">Click me</button>
`;

  public handleClick(): void {
    alert("Hello!")
  }
}

<强>的src /动态html.ts

import {
    customElement,
    TaskQueue,
    bindable,
    ViewCompiler,
    ViewSlot,
    View,
    ViewResources,
    Container,
    ViewFactory,
    inlineView,
    inject,
    DOM
} from "aurelia-framework";

@customElement("dynamic-html")
@inlineView("<template><div></div></template>")
@inject(DOM.Element, TaskQueue, Container, ViewCompiler)
export class DynamicHtml {
    @bindable()
    public html: string;

    public element: HTMLElement;

    private tq: TaskQueue;
    private container: Container;
    private viewCompiler: ViewCompile;

    private runtimeView: View;
    private runtimeViewSlot: ViewSlot;
    private runtimeViewFactory: ViewFactory;
    private runtimeViewAnchor: HTMLDivElement;

    constructor(element, tq, container, viewCompiler) {
        this.element = <HTMLElement>element;
        this.tq = tq;
        this.container = container;
        this.viewCompiler = viewCompiler;
    }

    public bindingContext: any;
    public overrideContext: any;
    public bind(bindingContext: any, overrideContext: any): void {
        this.bindingContext = bindingContext;
        this.overrideContext = overrideContext;

        if (this.html) {
            this.htmlChanged(this.html, undefined);
        }
    }

    public unbind(): void {
        this.disposeView();

        this.bindingContext = null;
        this.overrideContext = null;
    }

    public needsApply: boolean = false;
    public isAttached: boolean = false;
    public attached(): void {
        this.runtimeViewAnchor = this.element.firstElementChild;

        this.isAttached = true;
        if (this.needsApply) {
            this.needsApply = false;
            this.apply();
        }
    }

    public detached(): void {
        this.isAttached = false;

        this.runtimeViewAnchor = null;
    }

    private htmlChanged(newValue: string, oldValue: void): void {
        if (newValue) {
            if (this.isAttached) {
                this.tq.queueMicroTask(() => {
                    this.apply();
                });
            } else {
                this.needsApply = true;
            }
        } else {
            if (this.isApplied) {
                this.disposeView();
            }
        }
    }

    private isApplied: boolean = false;
    private apply(): void {
        if (this.isApplied) {
            this.disposeView();
        }

        this.compileView();
    }

    private disposeView(): void {
        if (this.runtimeViewSlot) {
            this.runtimeViewSlot.unbind();
            this.runtimeViewSlot.detached();
            this.runtimeViewSlot.removeAll();
            this.runtimeViewSlot = null;
        }

        if (this.runtimeViewFactory) {
            this.runtimeViewFactory = null;
        }

        if (this.runtimeView) {
            this.runtimeView = null;
        }

        this.isApplied = false;
    }

    private compileView(): void {
        this.runtimeViewFactory = createViewFactory(this.viewCompiler, this.container, this.html);

        this.runtimeView = createView(this.runtimeViewFactory, this.container);

        this.runtimeViewSlot = createViewSlot(this.runtimeViewAnchor);
        this.runtimeViewSlot.add(this.runtimeView);
        this.runtimeViewSlot.bind(this.bindingContext, this.overrideContext);
        this.runtimeViewSlot.attached();

        this.isApplied = true;
    }

}

function createViewFactory(viewCompiler: ViewCompiler, container: Container, html: string): ViewFactory {
    if (!html.startsWith("<template>")) {
        html = `<template>${html}</template>`;
    }
    let viewResources: ViewResources = container.get(ViewResources);
    let viewFactory = viewCompiler.compile(html, viewResources);
    return viewFactory;
}

function createView(viewFactory: ViewFactory, container: Container): View {
    let childContainer = container.createChild();
    let view = viewFactory.create(childContainer);
    return view;
}

function createViewSlot(containerElement: Element): ViewSlot {
    let viewSlot = new ViewSlot(containerElement, true);
    return viewSlot;
}