Netflix / x-element

A dead simple starting point for custom elements.
Apache License 2.0
28 stars 12 forks source link

Consider support for custom “serialize” / “deserialize” logic in property block. #149

Open theengineear opened 1 year ago

theengineear commented 1 year ago

Currently, deserialization (i.e., syncing from an attribute string to a typed property) will only work on “serializable types” — declared as the types Boolean, String, and Number. Other types will simply not be synced. Similar story for serialization (aka reflection, this is when a typed property is serialized to a string and set as an attribute).

While real-world use-cases for more complex deserialization / serialization are few and far between — we may choose to be less restrictive about this functionality in the future.

Proposal

Expose new serialize and deserialize options in the property block:

// Toy example.
class MyElement extends XElement {
  static get properties() {
    return {
      child: {
        type: HTMLElement,
        // Allow deserialization of stringified values via document.create
        deserialize: value => {
          try {
            return document.create(value);
          } catch (error) {
            return null;
          }
        },
        serialize: value => value?.localName,
        reflect: true,
      },
    };
  }
}

Rationale

Allow deserialization of compound types

Perhaps there are reasons that you cannot use property binding for certain things. Rather than impede developer flows, we could allow authors to get around this road bump if they really want.

class MyElement extends XElement {
  static get properties() {
    return {
      object: {
        type: Object,
        // Allow deserialization of stringified values via JSON.parse.
        deserialize: value => JSON.parse(value),
      },
    };
  }
}

Change default serialization / deserialization behavior

We recently shipped a change to address #147 which deserializes the empty string to NaN when syncing to Number-type properties from attributes. This behavior is likely the “right” default, but authors may want to override this and we shouldn’t get in their way. E.g., they may want 0 instead of NaN:

class MyElement extends XElement {
  static get properties() {
    return {
      number: {
        type: Number,
        // Rather than deserialize `'' >> NaN`, deserialize as `'' >> 0`.
        deserialize: value => Number(value),
      }
    };
  }
}