as-child

Merges a container element's output attributes onto a child element

Introduction

The as-child Tag Helper allows the attributes and behavior of its container element to be applied to the first child element. When the as-child attribute is used, the container element itself is not rendered to the output. Instead, the as-child Tag Helper merges all the container's output attributes onto the child element, effectively substituting the container with its child in the final HTML.

This is useful where you want to wrap a child element in DuneUI Tag Helper to apply specific behavior, but doing so may break your HTML layout or cause other unwanted HTML output.

Example use case

Let's take the following example of the Collapsible Tag Helper.

<dui-collapsible>
    <dui-collapsible-trigger>
        Expand
    </dui-collapsible-trigger>
    <dui-collapsible-content>
        This is the content
    </dui-collapsible-content>
</dui-collapsible>

This example produces the following HTML output, which works as expected. Take note of the button element generated by the dui-collapsible-trigger Tag Helper.

<div
  x-data="collapsible(false)"
  x-bind="root"
  data-slot="collapsible"
  data-state="open"
>
  <button x-bind="trigger">Expand</button>
  <div x-bind="content">This is the content</div>
</div>

The x-* attributes in the generated HTML above are used by Alpine.js to control the collapsible behaviour.

Now, let's say that instead of text, we want to display a button with an icon as the trigger.

<dui-collapsible>
    <dui-collapsible-trigger>
        <dui-button variant="Ghost" size="Icon">
            <dui-icon name="chevrons-up-down" />
        </dui-button>
    </dui-collapsible-trigger>
    <dui-collapsible-content>
        This is the content
    </dui-collapsible-content>
</dui-collapsible>

This time the generated HTML breaks completely and fails to work correctly. The reason for this is that we are trying to render a button inside another button and since this is not allowed, the ASP.NET Core Tag Helpers will try and fix this incorrect HTML, generating two sibling button elements.

The first button contains the x-bind attribute which governs the Alpine.js behavior and is generated by the dui-collapsible-trigger Tag Helper. The second button is our button with the icon.

<div
  x-data="collapsible(false)"
  x-bind="root"
  data-slot="collapsible"
  data-state="open"
>
  <button x-bind="trigger"></button>
  <button data-slot="button" class="...">
    <svg ...>...</svg>
  </button>
  <div x-bind="content">This is the content</div>
</div>

What we really want is to generate only the button with the icon, but have the x-bind attribute applied to it.

This is where the as-child Tag Helper comes in. As you can see in the example below, we have the same Razor code as the previous example, but we applied the as-child Tag Helper to dui-collapsible-trigger.

<dui-collapsible>
    <dui-collapsible-trigger as-child>
        <dui-button variant="Ghost" size="Icon">
            <dui-icon name="chevrons-up-down" />
        </dui-button>
    </dui-collapsible-trigger>
    <dui-collapsible-content>
        This is the content
    </dui-collapsible-content>
</dui-collapsible>

This time, a single button element was generated and the behavior from the dui-collapsible-trigger (i.e. the x-bind attribute) was applied to the button with the icon.

<div
  x-data="collapsible(false)"
  x-bind="root"
  data-slot="collapsible"
  data-state="open"
>
  <button data-slot="button" class="..." x-bind="trigger">
    <svg ...>...</svg>
  </button>
  <div x-bind="content">This is the content</div>
</div>

Additional notes

Use with caution

Since the as-child Tag Helper parses the container element's full HTML content at runtime to merge attributes and behavior, there is a small performance penalty you pay for using it. We recommend using it only when necessary.

Can use on any HTML element

Although as-child is meant for scenarios described above, it can be used on any element.

<div name="edit1" as-child>
    <input type="text">
</div>

The code above, will generate the following HTML.

<input type="text" name="edit1" />

It does not really make sense to use as-child in a situation like the one above, but it is merely to demonstrate that you can use it on any HTML element. Also, since there is a performance penalty you pay for using it, we definitely do not recommend use cases like this.

Applies only to first child

as-child applies the attributes of the containing element only to the first child element. Take the following Razor code with a container element with multiple child elements.

<div name="edit1" as-child="true">
    <input type="tel">
    <input type="email">
</div>

This will apply the name attribute from the container element only to the first child.

<input type="tel" name="edit1" />

<input type="email" />

Also, when the container element contains no children, the container element will still be rendered. For example the following Razor code:

<div name="edit1" as-child="true"></div>

Will generate the following HTML output:

<div name="edit1"></div>