[w3c/webcomponents] Consider functional mixin pattern for form-associated custom elements (#812)

Rather than incorporating support for forms into all custom elements, I was wondering whether it’s worth exploring a functional mixin pattern for defining form support. This would keep form features from affecting components that don’t need those features.

In this approach, form participation would be defined through a mixin function that can be applied a base class (either `HTMLElement`, or a class deriving from it) to return a new form-capable class. JavaScript pseudo-code for such a mixin would look like:

```js
function FormAssociatedMixin(Base) {
  return FormAssociated extends Base {
    // … Form-related methods/properties defined here, become part of resulting class …
    static get formAssociated() { return true; }
  };
}
```

Authors creating custom elements with forms in mind would then apply this `FormAssociatedMixin` to the base class they extend. The sample custom element from the [Form Participation Explainer](https://docs.google.com/document/d/1JO8puctCSpW-ZYGU8lF-h4FWRIDQNDVexzHoOQ2iQmY/edit?pli=1#) might look like:

```js
class MyControl extends FormAssociatedMixin(HTMLElement) {
  formAssociatedCallback() { … }
  formDisabledCallback() { … }
  formStateRestoreCallback(state, mode) { … }
}

customElements.define('my-control', MyControl);
```

If someone wants form support, they extend `FormAssociatedMixin(HTMLElement)`; if they don’t intend their component to be used with forms, they extend `HTMLElement` as they do now.

Among other things, this could keep `ElementInternals` clean for components that don’t support forms. The form-related properties and methods currently planned for `ElementInternals` could become private properties and methods:

```js
function FormAssociatedMixin(Base) {
  return FormAssociated extends Base {
    static get formAssociated() { return true; }
    #setFormValue(value, state) { … }
    #checkValidity() { … }
    #reportValidity() { … }
    …
  };
}
```

And then invoked directly on `this`, rather than on `ElementInternals`:

```js
class MyControl extends FormAssociatedMixin(HTMLElement) {
  …
  formResetCallback() {
    this.value = '';
    this.#setFormValue('');
  }
  …
}
```

I make this suggestion only because the Elix project I lead has gotten so much mileage out of the extensive set of functional [mixins](https://component.kitchen/elix/mixins); we’ve defined this way. After several years of creating components, I can say that it’s really nice to keep optional behavior out of base classes, and only bring them into play when needed. I think form participation is a good example of such optional behavior, so perhaps a mixin approach would work well for it.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/w3c/webcomponents/issues/812

Received on Tuesday, 7 May 2019 21:23:01 UTC