Изменить тип элемента во время выполнения

Можно ли динамически определять тип элемента внутри шаблона пользовательских компонентов во время выполнения?

Я хотел бы избежать дублирования внутреннего содержимого button а также a элемент в следующем примере:

<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, где Эрик Либен предложил использовать @processContent(function) декоратор, заменить содержимое в рамках данного function и вернуться true позволить Аурелии обработать это.

К сожалению, я не знаю, как на самом деле применять эти инструкции, и я надеюсь на некоторые альтернативные подходы здесь или некоторые детали о том, как на самом деле это сделать.


редактировать

Я создал соответствующий запрос функции. Несмотря на то, что были предоставлены возможные решения, я хотел бы увидеть более простой способ решить эту проблему;)

2 ответа

Решение

Если вы хотите повторно использовать фрагменты HTML, используйте compose. Это не создает новый пользовательский элемент. Он просто включает HTML-код в месте расположения каждого элемента compose. Таким образом, модель представления для включенного 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;
}

заказ канального значок-и-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

Ответ на ваш "менее многословный" комментарий

Вы можете записать его в один файл и избежать compose используя методы в вышеупомянутом GistRun и inlineView декоратор:

Смотрите этот 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;
}

Извините, я делал 2 вещи одновременно, глядя на gitter, что я не очень хорошо, по-видимому:-)

Для вещи, которую вы хотели достичь в конце концов, это могло бы сработать?

Я не являюсь экспертом в этой области или обладаю большими знаниями в этой области, но, насколько я понимаю, это поможет вам достичь желаемого. Браузер будет смотреть на атрибут role и обрабатывать его как ссылку или кнопку, игнорируя сам фактический тип элемента / ему будет все равно, будет ли он кнопкой или привязкой, он будет действовать так, как если бы он имел тип, определенный в роли.

Затем вы можете стилизовать его как кнопку или тег ссылки с помощью CSS.

<a role.bind="type"><span>x</span><span>y</span></a>

где типом является либо ссылка, либо кнопка, смотрите это: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_link_role

Другие вопросы по тегам