Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Change element type at runtime

Is it possible to dynamically define the type of an element inside a custom components template at runtime?

I'd like to avoid duplication of the inner contents of the button and a element in the following example:

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

Is it possible to write something like this:

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

I'm aware of dynamic composition with <compose view="${widget.type}-view.html"></compose> but as far as I know, this won't allow me to create default HTML elements but only custom components, correct?

I've asked this question on the Aurelia Gitter where Erik Lieben suggested to use a @processContent(function) decorator, replace the content within the given function and return true to let Aurelia process it.

Unfortunately I don't know how to actually apply those instructions and am hoping for some alternative approaches here or some details about how to actually accomplish this.


Edit

I've created a corresponding feature request. Even though possible solutions have been provided, I'd love to see some simpler way to solve this ;)

like image 519
suamikim Avatar asked Mar 18 '26 05:03

suamikim


1 Answers

When you want to reuse HTML snippets, use compose. Doing so does not create a new custom element. It simply includes the HTML at the location of each compose element. As such, the view-model for the included HTML is the same as for the element into which it is composed.

Take a look at this GistRun: https://gist.run/?id=36cf2435d39910ff709de05e5e1bedaf

custom-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>

custom-link.js

import {bindable} from 'aurelia-framework';

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

custom-link-icon-and-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>

You may want to split these into separate elements, like <custom-button> and <custom-link> instead of controlling their presentation using an is-link-btn attribute. You can use the same technique to reuse common HTML parts and composition with decorators to reuse the common code.

See this GistRun: https://gist.run/?id=e9572ad27cb61f16c529fb9425107a10

Response to your "less verbose" comment

You can get it down to one file and avoid compose using the techniques in the above GistRun and the inlineView decorator:

See this GistRun: https://gist.run/?id=4e325771c63d752ef1712c6d949313ce

All you would need is this one file:

custom-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;
}
like image 112
Jeff G Avatar answered Mar 23 '26 14:03

Jeff G



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!