hotwired / stimulus

A modest JavaScript framework for the HTML you already have
https://stimulus.hotwired.dev/
MIT License
12.67k stars 420 forks source link

Data API: set data value is typecasted upon render #63

Closed geoffdavis92 closed 6 years ago

geoffdavis92 commented 6 years ago

Given the following HTML and controller:

<!-- index.html -->
<main data-controller="test" data-action="click->test#logstate"></main>
// test_controller.js
export default class Test extends Controller {
  initialize() {
    this.data.set('state', { foo: 'bar' });
  }
  logstate() {
    console.log(this.data.get('state'));
  }
}

The output HTML upon Stimulus render would give me a console log of [object Object], and a data attribute data-test-state="[object Object]".

I understand the Data API isn't fully documented, but is there a way to set an Object type as a data value without using a third-party state management library? As I understand it the Data API takes the given arguments and assigns a data-* attribute to the controller, calculating the attribute name with the first argument and assigning the stringified value to that attribute's value.

One approach I could see around the issue of trying to assign an Object is to do something like the following:

// Object
Object.entries({foo: 'bar', baz: 'buzz'})
  .map(([key,value]) => {
    this.data.set(key,value)
  })

// Array
this.data.set('list',['foo','bar','baz','buzz'].toString())

…which would give us:

<main data-controller="test" data-test-foo="bar" data-test-baz="buzz" data-test-list="foo,bar,baz,buzz"></main>

Now the values of the data attributes are available, even if the dev now has to parse objects/arrays back into their respective structures, and strings/numbers are still able to be retrieved or properly typecasted.

However, this doesn't seem ideal and if there is a built-in feature to save Object-typed data to a controller I'd be interested to check that out.

Thanks!

savroff commented 6 years ago

Hi @geoffdavis92 As you can see here: https://github.com/stimulusjs/stimulus/pull/40

Stimulus like to keep Stimulus’ DataSet interface string-only to match the DOM

geoffdavis92 commented 6 years ago

@savroff thanks for the link! That does make sense, I just didn't know if I was missing something undocumented yet.

Are you taking PRs for doc changes? I'd love to help out

savroff commented 6 years ago

Yes, I will add docs update this weekend.

@geoffdavis92 but if you have time to do it before :) your welcome!

javan commented 6 years ago

As noted in https://github.com/stimulusjs/stimulus/pull/40#issuecomment-355645996, we'd like to keep the data API string-based for now. I recommend writing your own accessor methods that build on top when you need more:

export default class extends Controller {
  initialize() {
    this.state = { foo: "bar" }
    console.log(this.state.foo) // "bar"
  }

  get state() {
    return JSON.parse(this.data.get("state"))
  }

  set state(value) {
    this.data.set("state", JSON.stringify(value))
  }
}