Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HTML First Interface - v4 #30

Merged
merged 21 commits into from
Sep 27, 2023
Merged

HTML First Interface - v4 #30

merged 21 commits into from
Sep 27, 2023

Conversation

JRJurman
Copy link
Member

@JRJurman JRJurman commented Sep 12, 2023

Summary

This version is available by pointing to tram-lite@beta.
https://unpkg.com/tram-lite@beta

This version focuses on an HTML first interface, allowing developers to build complex behaviors in web-components using special tl- attributes.

This is in-part inspired by the success of HTMX, and the attractiveness of the new template syntax found in Tram-Lite v3.

Example Component

Codepen: https://codepen.io/JRJurman/pen/YzdrKQR

<script src="https://unpkg.com/tram-lite@beta"></script>

<template tl-definition>
  <ex-progressbar value="3" max="10">
    <div>
      <input type="number" tl-controlled tl-hostattr="value" tl-trigger="input" />
      <input type="number" tl-controlled tl-hostattr="max" tl-trigger="input" />
    </div>
    <progress value="${'value'}" max="${'max'}"></progress>
    <p>${'warning'}</p>
    <script tl-effect tl-dependencies="value max">
      const value = parseInt(this.getAttribute('value'));
      const max = parseInt(this.getAttribute('max'));
      if (value > max) {
        this.setAttribute('warning', `WARNING: ${value} is greater than ${max}`);
      } else {
        this.removeAttribute('warning');
      }
    </script>
  </ex-progressbar>
</template>

<ex-progressbar></ex-progressbar>

Notable things to highlight

<template tl-definition>

The tl-definition indicates a template tag that has a list of component definitions to process.

This is the HTML interface for TramLite.define

Rather than using a custom built-in (which isn't universally supported), we now watch for a tl-definition attribute using a mutation observer, regardless of platform. Eventually, when a universal solution is implemented here for adding behavior for existing tags (potentially has?) then we can look into migrating to that!

<input tl-controlled>

The tl-controlled attribute sets up the 2-way data binding that was common for inputs (and related components). The value for these elements will be bound to an attribute on the host component (and vice-versa).

This is the HTML interface for TramLite.updateRootAttr.

There are three additional attributes that work with tl-controlled:

  • tl-trigger - the event you want to update the host attribute on. E.g. input, keyup, etc... Defaults to change.
  • tl-hostattr - the attribute name for the host component. Defaults to value
  • tl-targetattr - the attribute on this input to copy over. Defaults to value, but can be checked for checkboxes, or anything else.

In the above example, we have the two inputs:

<input type="number" tl-controlled tl-hostattr="value" tl-trigger="input" />
<input type="number" tl-controlled tl-hostattr="max" tl-trigger="input" />

This sets up two controlled inputs. Both update on input events. The first one has a default tl-hostattr and tl-targetattr of value, so the host element will have an attribute value bound to this input. The second input has a tl-hostattr="max", which means that updates to this input will set the max attribute on the host web-component.

Note: while we use tl-controlled here with input elements, other elements can use this attribute (e.g. select).

<script tl-effect>

The tl-effect attribute is a special binding for script-tags that allows them to reference their host component. This is similar to the previous implementation of script tags in Tram-Lite definitions (but now should work in any web-component!).

This is the HTML interface for TramLite.addAttributeListener.

Notably, we've removed support for external script tags (using the src attribute). Tram-Lite support here was previously unclear, and it's not obvious how we might support these in the ways we support inline javascript.

Scripts with the tl-effect also support two additional attributes:

  • tl-dependencies - a list of attributes that will cause the inline script to rerun
  • tl-hold - an attribute that indicates that the component scripts should not trigger. If this attribute is present, we won't run the script. By default, Tram-Lite sets the value here to "component-mount" and will remove the attribute when the component has mounted. We may support other options in the future (like construction or DOMContentLoaded)

In the above example, we have the following script tag

<script tl-effect tl-dependencies="value max">
  const value = parseInt(this.getAttribute('value'));
  const max = parseInt(this.getAttribute('max'));
  if (value > max) {
    this.setAttribute('warning', `WARNING: ${value} is greater than ${max}`);
  } else {
    this.removeAttribute('warning');
  }
</script>

This script will re-trigger whenever value or max is updated.

API Changes

Removed html and svg

While these methods were useful in some cases, they didn't directly help with building web-components or enhancing web-component behavior. Their implementation often had unpredictable side-effects (as an artifact of setting innerHTML).

There is a use-case here that offering this makes us more inline with tools like JSX, which offer a more elegant way to build elements (and namely lists of elements) in javascript, but we should seek to offer an HTML interface first, rather than an exclusive JS implementation.

Moved functions into static methods

Rather than having top-level functions that are defined on the window object, we now expose all the Javascript methods from the TramLite class. They are...

  • TramLite.define
  • TramLite.updateRootAttr
  • TramLite.addAttributeListener

Now that the library is more focused on an HTML first interface, these functions don't need to be as readily available.

There is also a utility function, TramLite.appendShadowRootProcessor, which is used to bind the functionality for tl-effect and tl-controlled. Developers could potentially use this for upgrading other kinds of components with their own attributes, in the same way we process our own attributes.

Checklist

  • PR Summary
  • PR Annotations
  • Tests
  • API Docs
  • Building Component Guide
  • Validate with 7GUIs
  • Update Guiding Principles
  • Version Bump
  • Migration Guide (see Migration guide from v3 to v4 #31)

@JRJurman JRJurman changed the title New Component Effect, deprecate script behavior in Component Definitions - v4 HTML First Interface - v4 Sep 15, 2023
@JRJurman JRJurman marked this pull request as ready for review September 17, 2023 19:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

1 participant