在运行时更改元素类型

Change element type at runtime

是否可以在运行时动态定义自定义组件模板内的元素类型?

我想避免在以下示例中重复 buttona 元素的内部内容:

<template>
    <button if.bind="!isLinkBtn">
        <span class="btn-icon">${icon}</span>
        <span class="btn-text">${contentText}</span>
    </button>

    <a if.bind="isLinkBtn">
        <!--
        The content is a 1:1 duplicate of the button above which should be prevented
        somehow in order to keep the view DRY
        -->
        <span class="btn-icon">${icon}</span>
        <span class="btn-text">${contentText}</span>
    </a>
</template>

是否可以这样写:

<template>
    <!--
    The type of element should be defined at runtime and can be a standard HTML "button"
    or an anchor "a"
    -->
    <element type.bind="${isLinkBtn ? 'a' : 'button'}">
        <span class="btn-icon">${icon}</span>
        <span class="btn-text">${contentText}</span>
    </element>
</template>

我知道 <compose view="${widget.type}-view.html"></compose> 的动态组合,但据我所知,这不允许我创建默认 HTML 元素,只能创建自定义组件,对吗?

我在 Aurelia Gitter 上问过这个问题,Erik Lieben 建议使用 @processContent(function) 装饰器,替换给定 function 和 return [=17= 中的内容] 让 Aurelia 处理它。

不幸的是,我不知道如何实际应用这些说明,我希望这里有一些替代方法或一些关于如何实际完成此操作的详细信息。


编辑

我创建了一个对应的 feature request。尽管已经提供了可能的解决方案,但我还是希望看到一些更简单的方法来解决这个问题 ;)

如果您想重复使用 HTML 个片段,请使用 compose。这样做 不会 创建新的自定义元素。它只是在每个组合元素的位置包含 HTML。因此,包含的 HTML 的视图模型与其组成的元素相同。

看看这个 GistRun:https://gist.run/?id=36cf2435d39910ff709de05e5e1bedaf

自定义-link.html

<template>
    <button if.bind="!isLinkBtn">
      <compose view="./custom-link-icon-and-text.html"></compose>
    </button>

    <a if.bind="isLinkBtn" href="#">
      <compose view="./custom-link-icon-and-text.html"></compose>
    </a>
</template>

自定义-link.js

import {bindable} from 'aurelia-framework';

export class CustomLink {
    @bindable() contentText;
    @bindable() icon;
    @bindable() isLinkBtn;
}

自定义-link-图标-text.html

<template>
    <span class="btn-icon">${icon}</span>
    <span class="btn-text">${contentText}</span>
</template>

consumer.html

<template>
  <require from="./custom-link"></require>
  <custom-link content-text="Here is a button"></custom-link>
  <custom-link is-link-btn.bind="true" content-text="Here is a link"></custom-link>
</template>

您可能希望将它们拆分为单独的元素,例如 <custom-button><custom-link>,而不是使用 is-link-btn 属性控制它们的显示。您可以使用相同的技术重用公共 HTML 部分,并使用装饰器组合重用公共代码。

查看此 GistRun:https://gist.run/?id=e9572ad27cb61f16c529fb9425107a10

回复您的"less verbose"评论

您可以将其压缩到一个文件中,避免 compose 使用上述 GistRuninlineView 装饰器中的技术:

查看此 GistRun:https://gist.run/?id=4e325771c63d752ef1712c6d949313ce

您只需要一个文件:

自定义-links.js

import {bindable, inlineView} from 'aurelia-framework';

function customLinkElement() {
    return function(target) {
        bindable('contentText')(target);
        bindable('icon')(target);
  }
}


const tagTypes = {button: 'button', link: 'a'};


@inlineView(viewHtml(tagTypes.button))
@customLinkElement()
export class CustomButton {

}


@inlineView(viewHtml(tagTypes.link))
@customLinkElement()
export class CustomLink {

}


function viewHtml(tagType) {
  let result = `
    <template>
        <${tagType}${tagType === tagTypes.link ? ' href="#"' : ''}>
            <span class="btn-icon">${icon}</span>
            <span class="btn-text">${contentText}</span>
        </${tagType}>
    </template>
    `;

  return result;
}

抱歉,我在看 gitter 的时候同时做了两件事,我显然不擅长:-)

你最后想完成的事情,这个也可以吗?

我不是专家,也不了解该领域的很多知识,但据我了解,这将实现您想要的。浏览器将查看 role 属性并将其作为 link 或按钮处理,并忽略实际元素类型本身/不会关心它是按钮还是锚点它会像定义在角色。

然后您可以使用 css.

将其设置为按钮或 link 标签的样式
<a role.bind="type"><span>x</span><span>y</span></a>

其中类型是 link 或按钮,请参见:https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_link_role