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

Feature request - classes style list of strings or numbers as a Value Type #651

Open
lb- opened this issue Feb 2, 2023 · 0 comments
Open

Comments

@lb-
Copy link
Contributor

lb- commented Feb 2, 2023

Summary

Working with HTML attributes as a list of space separated strings is very intuitive, and used for classes, aria references and more.

It would be great if there was the ability to work with space separate strings more easily with Stimulus, instead of needing parse/split the strings manually whenever used. This convention is also used when wanting to apply multiple actions on the data-action attribute.

Simple use case - a field that cleans its own input value based on some predetermined filters.

<input type="text" data-controller="clean" data-clean-options-value="trim upper snake" data-action="clean#filter" />

Current usage

Currently if we want an Array we either need to use a string and split it manually, which is not a huge problem but prone to error (handling of trim, additional whitespace, parsing edge cases).

Alternatively we need to use the JSON syntax which is good for automated output but less easy to read visually and manually write out in template code.

<div data-controller="example" data-example-clear-value='["x","y","z"]' data-example-intervals-value="300 500 950">Content</div>
export default class extends Controller {
  static values = {
    clear: Array,
    intervals: String
  }

  constructor() {
    console.assert(this.clearValue, ['x', 'y', 'z'])
    console.assert(this.intervalsValue.split('').map(Number), [300, 500, 950])
  }
}

Approach Idea 1 - DOMTokenList usage (strings only)

This is very explicit and adheres to the rules and the methods that already come with the classes list type interface from the DOM. However, to create a new DOMTokenList instance you need a DOM element first, there is no exposed constructor, this also assumes that Stimulus will only ever be declared in a browser environment.

Usage

<input type="text" data-controller="clean" data-clean-options-value="trim slugify" data-action="clean#filter"/>
export default class extends Controller {
  static values = {
    options: DOMTokenList
  }

  constructor() {
    console.assert([...this.optionsValue], ['trim', 'slugify'])
    console.assert(this.optionsValue.value, 'trim slugify')
    console.assert(this.optionsValue.contains('slugify'), true)
  }
}

Decoding & Encoding

// this is not ideal as it creates an element that will never be written to the DOM but will work
const decoded = ((val) => {
  const el = document.createElement('template');
  el.setAttribute('class', val);
  return el.classList;
})(str);
const encoded = val.value;

Approach Idea 2 - Set usage (strings only)

This leverages the idea of using Set as the value type, while it stretches the usage a bit it could be a nice way to keep things simple. It is not 'smart' about the content of the set, just that it will be unique and will treat whitespace as the splitting mechanism.

This has the benefit of not polluting the Array usage with more confusion and enforces this as a use case for unique things that are set in the data attribute value.

Usage

<div data-controller="example" data-example-clear-value="query page 500">Content</div>
export default class extends Controller {
  static values = {
    clear: Set
  }

  constructor() {
    console.assert(this.clearValue, ['query', 'page', '500'])
  }
}

Decoding & Encoding

// a bit noisy, could be simpler, goal here is to nicely clear out any inner whitespace to avoid empty entries
const decoded = Set(str.trim().split(' ').map(_ => _.trim()).filter(Boolean));
const encoded = [...arr].join(' ');

Approach Idea 3 - [Number] / [String] usage (Array with prescribed types)

Usage

In this scenario we want to support a progressively delayed async request (or something of that nature) and also easily support multiple options for a set of clearing params values.

This backwards compatible approach allows for strings to be nicely parsed by Stimulus and worked with as if they were an array but written and presented in the URL as a string. This is very similar to how the Stimulus classes works now (except that the 'value' will be the first value of the parsed array, this approach would also be suitable but could risk confusion and more surface area for the ...Value getter/setters.

<div data-controller="example" data-example-clear-value="x y z" data-intervals-value="300 500 950">Content</div>
export default class extends Controller {
  static values = {
    clear: [String],
    intervals: [Number],
  }

  constructor() {
    console.assert(this.clearValue, ['x', 'y', 'z'])
    console.assert(this.intervalsValue, [300, 500, 950])

    this.clearValue = ['beta'];
    console.assert(this.clearValue, ['a'])
    this.clearValue = 'beta'; // not sure on what this should do if incorrectly used - maybe spread to an array?
    console.assert(this.clearValue, ['b', 'e', 't' ',a'])
  }
}

Encoding & Decoding

// clearing extra whitespace could also be a regex
const decoded = str.trim().split(' ').map(_ => _.trim()).filter(Boolean);
// if [Number]
const decoded = str.trim().split(' ').map(_ => _.trim()).filter(Boolean).map(Number);
const encoded = [...arr].join(' ');
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

1 participant