Checking If a View Slot Is Defined In Aurelia

Last updated: July 16, 2017

Aurelia supports the <slot> element provided via the HTML Web Components specification, which allows you to define placeholders in your HTML that can be replaced.

A lot of examples around seem to wrap a slot with a DIV, perhaps a class. The issue with this approach is if you have a styled DIV wrapping a slot and that slot is empty, your DIV will still be affecting the space around it. This is where the ability to check if a user has provided a slot or not helps.

This post was prompted by a StackOverflow question I answered here. It dawned on me this isn’t a straightforward solution and other people might find it valuable.

Take the following fictional example of a modal custom element. We have a header, body and footer. The body contains a slot without a name, but the header and footer are named slots.

<template>
    <div class="modal__header">
      <h1>Header:</h1>
      <slot name="modalHeader"></slot>
    </div>
    
    <div class="modal__body">
      <h1>Body:</h1>
      <slot></slot>
    </div>
    
    <div class="modal__footer">
      <h1>Footer:</h1>
      <slot name="modalFooter"></slot>
    </div>
</template>

The issue here as I mentioned earlier is a) styles on wrapping DIV’s affecting the layout even if the slot is empty and b) if the element wrapping your slot contains other HTML with your slot, it will show up even if no slot content is provided (as can be seen above).

What we want to do is conditionally check if a slot has user provided content and show or hide it accordingly.

Accessing the controller instance

Aurelia creates an au property on every tracked Aurelia node in the page, containing its view factory information, bindings and more. By accessing the controller and then the view property of our controller, we can access our slots.

export class ModalCustomElement {
  static inject = [Element];
  
  constructor(element) {
    this.element = element;
  }
  
  attached() {
    this.$slots = this.element.au.controller.view.slots;
    console.log(this.$slots);
  }
}

Inside of the view-model for our fictional modal custom element, we inject the custom element instance and then we create a class property called $slots where we pass in the view controller slots property containing our slots.

In our example, the $slots property we created should contain three slots. Because the $slots property is an object which is keyed by slot name, in our view we can determine if the user has provided slot data by checking the children property.

This then gives us some HTML that looks like this:

<template>
    <div class="modal__header" show.bind="$slots.modalHeader.children.length">
      <h1>Header:</h1>
      <slot name="modalHeader"></slot>
    </div>
    
    <div class="modal__body">
      <h1>Body:</h1>
      <slot></slot>
    </div>
    
    <div class="modal__footer" show.bind="$slots.modalFooter.children.length">
      <h1>Footer:</h1>
      <slot name="modalFooter"></slot>
    </div>
</template>

For each slot, we check the children property which is an array. We know if the length is zero, the user hasn’t provided any data for this slot. Default data inside of a slot will not be counted in the children array, that lives elsewhere in the fallback slot property.

We are also accessing the slot via its name as it lives in the object. For the default slot with no provided name, it’s name internally is: __au-default-slot-key__ which can also be checked using a show.bind.

Why if.bind and not show.bind?

You’ll notice we are using show.bind and not if.bind above. You can’t actually use if.bind because if removes the slot from the controller and therefore, we can no longer check for it. When using show the element remains but is hidden and our slot still exists.

Working example

If the above went over your head, the example I posted in the SO question is here where you can see DIV’s being shown/hidden depending on whether or not the user provides slot content.

Purchase Aurelia for Real World Applications over at Leanpub now

 

Dwayne

 

2 thoughts on “Checking If a View Slot Is Defined In Aurelia

  1. This is great!

    Speaking of slots, would you explain how to use them dynamically — the docs say that you can’t use them w/binding, so how would I have a set of slottable bits and use them dynamically?

  2. Hey Dwayne, nice work resolving this issue.

    I’ just tried it with typescript, but the property au doesn’t exist in the element. Do you know how to make this work using typescript?

Leave a Reply

Your email address will not be published. Required fields are marked *