Customizing elements

You can extensively customize Elix elements for the context of your application. There are several techniques for doing this:

Slots

Most Elix elements accept content via their default slot. Some complex elements have multiple slots. E.g., Carousel defines slots inside its left and right arrow buttons, allowing you to customize the arrow icon or other content shown inside those buttons.

Updates

When the state of an Elix element changes, it uses a mixin called RenderUpdatesMixin to determine what updates should be applied to the element host and the elements in its shadow tree. The mixin accomplishes this by asking the element for the current value of its updates property.

This system allows for the appearance and behavior of an element to be collectively defined by the element class, its base classes, and any mixins applied to it. You can tap into this system by subclassing an Elix element and defining an updates property that sets styles and other properties on the element or its subelements.

Element roles

Complex Elix elements like Carousel and Tabs have templates with various key subelements. Such complex elements expose properties you can set to determine what standard or custom HTML elements are used to fill key roles in the template.

E.g., Carousel has a key element role called the "stage", which shows a single selected item (usually an image) at a large size. Carousel also defined another role called the "proxy" for the element that will be used to represent a specific item; by default, the proxy is a small PageDot.

You can override these roles on an element-by-element basis by setting role properties on the element in markup or before the component is attached to the DOM. Element roles are filled by supplying a descriptor that is either:

  • A component class (FooElement) that will be instantiated to fill the role.
  • A string representing the tag name ("foo-element") that will be instantiated to fill the role.
  • An HTMLTemplateElement that will be cloned to the fill the role.

Example: if you have a defined your own custom elements for a carousel's arrow buttons and page dots, you could use those with a specific Elix Carousel. You could instantiate the Carousel in markup, then fill the role by setting properties for the element's arrowButtonRole and proxyRole. In the case of markup, this can be done by identifying the tag name for the desired custom elements:

<script type="module" src="node_modules/elix/src/Carousel.js"></script>
<script type="module" src="MyArrowButton.js"></script>
<script type="module" src="MyPageDot.js"></script>

<body>
  <elix-carousel arrow-button-role="my-arrow-button" proxy-role="my-page-dot">
    <img src="image1.jpg">
    <img src="image2.jpg">
    <img src="image3.jpg">
  </elix-carousel>
</body>

By specifying what standard or custom element should be used for that key subelements, you can provide arbitrary customizations of the Carousel's appearance and behavior.

You can also define roles on a class basis. Suppose you want to create a custom carousel that always uses your custom page dot to represent the items in the carousel. You can do this in the constructor by setting the symbols.roles property:

import Carousel from 'elix/src/Carousel.js';
import MyArrowButton from 'MyArrowButton.js';
import MyPageDot from 'MyPageDot.js';

class MyCarousel extends Carousel {
  constructor() {
    super();
    this[symbols.roles] = Object.assign({}, this[symbols.roles], {
      arrowButton: MyArrowButton,
      proxy: MyPageDot
    });
  }
}

The result has all the functionality of the base Carousel, with the customized appearance of the arrows and page dots:

Mountain lake Terraced farm Winter trees Forest river Red panda
Demo: Carousel with custom arrow buttons and page dots

Template patching

Many Elix elements are specializations of other types of elements. Often such relationships are expressed in a class hierarchy. For example, a DropdownList is a specialized type of MenuButton, so DropdownList is defined as a subclass as MenuButton. MenuButton is in turn is a special type of PopupSource, and again the former is defined as a subclass of the latter.

Such specialized classes often need to add additional elements to the template defined by their parent classes. A common Elix pattern is to have a component define its template by obtaining a template from its parent class, then patching the result to define its own template. This is often done using helper functions in the template module.

Reusing mixins

Sometimes you want to create a component that's substantially similar to an existing Elix element, but which is different enough that the techniques above are insufficient. In such cases, you may still be able to reuse much of the code for the Elix element in question by creating your own component from the same set of mixins.

The vast majority of the behavior for all Elix elements is defined by mixins. The documentation for each element will indicate what mixins it uses; inspecting the source code is obviously helpful as well. Having identified that set of mixins, you can apply that same set of mixins to a base class like HTMLElement or ReactiveElement to create a fundamentally new component that nevertheless reuses a considerable degree of code. This allows you to both create components more quickly, and at a higher degree of quality.