Skip to content

Custom properties

George Treviranus edited this page Mar 25, 2022 · 10 revisions

As mentioned in the properties & state article, you can still use vanilla properties in the constructor/class. The only difference is such properties will not affect render state in any way, nor will they inherently work with attributes. As you might expect, setting a property using the static properties method will do nothing if there's an existing accessor.

So what does that mean? Let's look at this example:

constructor() {
  super()
  this._isOpen = false
}

// Accessors:

set isOpen(value) {
  if (value === this.isOpen) return
  this._isOpen = value
}

get isOpen() {
  return this._isOpen
}

Here, we have the _isOpen property, which is paired with get/set accessors. In this scenario, you control the outcome of your property. It's effectively invisible to Bulba and all its features.

All of the upgrade features are available to you, too:

import { validateType, ... } from "@bulba/element"
attributeChangedCallback(name, oldVal, newVal) {
  // Important!
  super.attributeChangedCallback(name, oldVal, newVal)

  if (name === "is-open" && oldVal !== newVal) {
    this.isOpen = newVal

    if (typeof this.onAttributeChange === "function") {
      this.onAttributeChange("is-open", oldValue, value)
    }
  }
}

set isOpen(value) {
  if (value === this.isOpen) return

  // Validate the type is correct
  validateType(this, "isOpen", value, "boolean")

  // Store the old value before we set the new one
  const oldValue = this.isOpen
  this._isOpen = value

  this.onPropertyChange("isOpen", oldValue, value)

  // Optionally reflect the property
  this.setAttribute("is-open", String(value))

  // Update your view with the new state
  // This automatically triggers `onUpdate`
  // This is async!
  this.requestRender()
}

You should use this strategy sparingly, especially if you have a mixture of upgraded properties and custom properties.