Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

html-element-property-mixins

Package Overview
Dependencies
Maintainers
1
Versions
21
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

html-element-property-mixins

A collection of mixins extending HTMLElement with properties.

  • 0.10.6
  • npm
  • Socket score

Version published
Maintainers
1
Created
Source

# html-element-property-mixins

Installation

$ npm install html-element-property-mixins

Introduction

html-element-property-mixins is a collection of mixins extending HTMLElement with properties, powering custom elements.

  1. ObservedProperties enables observed properties (just like built-in observedAttributes).
  2. DOMProperties enables attribute to property synchonisation.
  3. ReflectedProperties enables property to attribute synchonisation.
  4. Properties combines all three above.

Furthermore, we created a bunch of addons:

  1. PropertiesChangedCallback Debounces / batches property changes for efficient DOM-rendering.
  2. PropertyChangedHandler enables change handlers methods for property changes.
  3. PropertiesChangedHandler enables change handlers methods for multiple property changes.

Mixins

ObservedProperties

import { ObservedProperties } from 'html-element-property-mixins';
Observing

By default, Custom Elements can observe attribute value changes whitelisted in the observedAttributes Array. ObservedProperties offers a similar solution for DOM properties using observedProperties. When a property has changed, propertyChangedCallback is called, passing the property name, the old value and the new value.'

class DemoElement extends ObservedProperties(HTMLElement) {

  static get observedProperties() {
    return ['firstName', 'lastName', 'age']
  }

  propertyChangedCallback(propName, oldValue, newValue) {
    console.info(`${propName} changed from ${oldValue} to ${newValue}`);
  }
  
}

If you like you can add your own getter / setter pairs:

static get observedProperties() {
  return ['initials']
}

get initials() {
  return this._initials;
}

set initials(val) {
  this._initials = val.toUpperCase();
}

constructor() {
  this.initials = 'a.b.c.';
}

propertyChangedCallback(propName, oldValue, newValue) {
  console.info(`${propName} changed to ${newValue}`); //initials changed to A.B.C;
}

Accessors don't require a getter / setter pair. Keep in mind though that by default, private property values are assigned using the following pattern: #${propName}.

static get observedProperties() {
  return ['firstName']
}

get firstName() {
  return this['#firstName'].toLowerCase()
}

DOMProperties

import { DOMProperties } from 'html-element-property-mixins';

Some native properties (e.g. input value) can be set using a DOM attribute. This mixin adds exactly this behavior: attribute to property sync:

class DemoElement extends DOMProperties(HTMLElement) {

  static get DOMProperties() {
    return ['firstname', 'lastname']
  }

}
<demo-element id="demo" firstname="Adewale" lastname="King"></demo-element>
<script>
  console.info(demo.firstname, demo.lastname); // Adewale, King
</script>

By default, attributes are lowercased property names (e.g. 'myPropName' becomes 'mypropname'). You can configure custom attribute mappings using 'propertyAttributeNames':

static get DOMProperties() {
  return ['myBestFriend']
}

static get propertyAttributeNames() {
  return {
    myBestFriend: 'my-best-friend',
  }
}
<demo-element id="demo" my-best-friend="Hellen"></demo-element>
Attribute Converters

Attribute values are always strings. If you wish to set attributes based on properties taht have a specific type, you can confifure converters using propertyFromAttributeConverters:

static get DOMProperties() {
  return ['married', 'friends']
}

static get propertyFromAttributeConverters() {
  return {
    married: function(value) {
      if(value === '') return true;
      return false;
    },
    friends: function(value) {
      if(!value) return null;
      return JSON.parse(value);
    }
  }
}
<demo-element id="demo" married friends='["Gabriella","Anik","Linda"]'></demo-element>
<script>
  console.info(demo.married, demo.friends); //true, ['Gabriella','Anik','Linda'];
</script>

html-element-property-mixins come with a set of attribute converters for boolean, string, number and object types:

  import { StringConverter, NumberConverter, BooleanConverter, ObjectConverter } from 'html-element-property-mixins/utils/attribute-converters';

  static get propertyFromAttributeConverters() {
    return {
      firstName: StringConverter.fromAttribute,
      age: NumberConverter.fromAttribute,
      married: BooleanConverter.fromAttribute,
      friends: ObjectConverter.fromAttribute,
    }
  }

ReflectedProperties

import { ReflectedProperties, ObservedProperties } from 'html-element-property-mixins';

This enables property to attribute sync. Using the 'reflectedProperties' object, one can map properties (keys) to attributes (values). The ObservedProperties mixin is required.

class DemoElement extends ReflectedProperties(ObservedProperties(HTMLElement)) {

  static get observedProperties() {
    return ['firstname', 'lastname', 'age']
  }

  static get reflectedProperties() {
    return ['firstname', 'lastname', 'age']
  }

  constructor() {
    this.firstname = 'Amira';
    this.firstname = 'Arif';
    this.age = 24;
  }

}

By default, attributes are lowercased property names (e.g. 'myPropName' becomes 'mypropname'). You can configure custom attribute mappings using 'propertyAttributeNames':

static get reflectedProperties() {
  return ['firstName']
}

static get propertyAttributeNames() {
  return {
    firstName: 'first-name',
  }
}
<demo-element first-name="Amira"></demo-element>
Attribute Converters

Attribute values are always strings. If you wish to set attributes based on properties taht have a specific type, you can confifure converters using propertyToAttributeConverters:

static get reflectedProperties() {
  return ['married', 'friends']
}

static get propertyToAttributeConverters() {
  return {
    married: function(value) {
      if(value === '') return true;
      return false;
    },
    friends: function(value) {
      if(!value) return null;
      return JSON.parse(value);
    }
  }
}

<demo-element id="demo" married friends='["Gabriella","Anik","Linda"]'></demo-element>
<script>
  console.info(demo.married, demo.friends); //true, ['Gabriella','Anik','Linda'];
</script>

html-element-property-mixins come with a set of attribute converters for boolean, string, number and object types. Attributes are set based on the return value of these functions: when false or undefined, removeAttribute is called. Otherwise, setAttribute is called using the return value.

import { StringConverter, NumberConverter, BooleanConverter, ObjectConverter } from 'html-element-property-mixins/utils/attribute-converters';

static get reflectedProperties() {
  return ['firstName', 'age', 'married', 'friends']
}

static get propertyToAttributeConverters() {
  return {
    firstName: StringConverter.toAttribute,
    age: NumberConverter.toAttribute,
    married: BooleanConverter.toAttribute,
    friends: ObjectConverter.toAttribute,
  }
}

NOTE: ObservedProperties is required for ReflectedProperties.

Properties

import { Properties } from 'html-element-property-mixins';

This wraps all property mixins into a single properties configuration object.

class DemoElement extends Properties(HTMLElement) {

  static get properties() {
    return {
      firstName: {
        observe: true, //add to `observedProperties` array
        DOM: true, //add to `DOMProperties` array
        reflect: true, //add to `reflectedProperties` array
        attributeName: 'first-name', //map to custom attribute name,
        toAttributeConverter: StringConverter.toAttribute, //run when converting to attribute
        fromAttributeConverter: StringConverter.fromAttribute //run when converting from attribute
      }
    }
  }

}

If you use the PropertyChangedHandler addon, you can add 'changedHandler' to your config:

class DemoElement extends PropertyChangedHandler(Properties(HTMLElement)) {

  static get properties() {
    return {
      age: {
        observe: true,
        changedHandler: '_firstNameChanged',
      }
    }
  }

  _firstNameChanged(oldValue, newValue) {
    //custom handler here!
  }

}

Addons

PropertiesChangedCallback

import { ObservedProperties } from 'html-element-property-mixins';
import { PropertiesChangedCallback } from 'html-element-property-mixins/src/addons';

When declaring observed properties using the observedProperties array, property changes are fired each time a a property changes using the propertyChangedCallback. For efficiency reasons (e.g. when rendering DOM), the propertiesChangedCallback (plural!) can be used. This callback is debounced by cancel / requestAnimationFrame on every property change. In the following example, render is invoked only once:

import { PropertiesChangedCallback } from 'html-element-property-mixins/src/addons';
import { ObservedProperties } from 'html-element-property-mixins';

class DemoElement extends PropertiesChangedCallback(ObservedProperties(HTMLElement)) {

  constructor() {
    super();
    this._renderCount = 0;
  }

  static get observedProperties() {
    return ['firstName', 'lastName', 'age'];
  }

  propertiesChangedCallback(propNames, oldValues, newValues) {
    this._renderCount++;
    this.render();
  }

  render() {
    this.innerHTML = `
      Hello, ${this.firstName} ${this.lastName} (${this.age} years).<br>
      Render Count = ${this._renderCount}. 
    `
  }

  constructor() {
    super();
    this.firstName = 'Amina';
    this.lastName = 'Hamzaoui';
    this.age = 24;
  }

}

PropertyChangedHandler

import { ObservedProperties } from 'html-element-property-mixins';
import { PropertyChangedHandler } from 'html-element-property-mixins/src/addons';

Value changes to properties whitelisted in the observedProperties array are always notified using propertyChangedCallback. PropertyChangedHandler provides for custom callbacks for property changes:

class DemoElement extends PropertyChangedHandler(ObservedProperties((HTMLElement)) {
  static get observedProperties() {
    return ['firstName']
  }

  static get propertyChangedHandlers() {
    return {
      firstName: function(newValue, oldValue) {
        console.info('firstName changed!', newValue, oldValue);  
      }
    }
  }
}

Alternatively, callbacks can be passed as string references:

static get propertyChangedHandlers() {
  return { firstName: '_firstNameChanged' }
}

_firstNameChanged(newValue, oldValue) {
  console.info('firstName changed!', newValue, oldValue);
}

Note: PropertyChangedHandler should always be used in conjunction with ObservedProperties.

PropertiesChangedHandler

import { ObservedProperties } from 'html-element-property-mixins';
import { PropertiesChangedHandler } from 'html-element-property-mixins/src/addons';

Its plural companion propertiesChangedHandlers can be used to invoke a function when one of many properties have changed. Key / value pairs are now swapped. A key refers to the handler function, the value holds an array of the observed properties.

class DemoElement extends PropertiesChangedHandler(ObservedProperties((HTMLElement)) {
  static get observedProperties() {
    return ['firstName', 'lastName']
  }

  static get propertiesChangedHandlers() {
    return {
      _nameChanged: ['firstName', 'lastName']
    }
  }

  _nameChanged(propNames, newValues, oldValues) {
    console.info(newValues.firstName, newValues.lastName);
  }

}

Note: PropertiesChangedHandler should always be used in conjunction with ObservedProperties.

FAQs

Package last updated on 05 Apr 2020

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc