Re: [w3c/webcomponents] [idea] Allow HTML Element attributes to accept any type of value, not just strings. (#519)

> Okay, how do you expect the syntax to be?

As far as HTML syntax, nothing new, that stays the same. For example:

```html
<some-element some-attribute="anything here is just a string still">
</some-element>
```

A Custom Element can define any string format they wish (by documenting it). The Custom Element's `attributeChangedCallback` can parse a string value and convert it into anything (an array, an Object, a number, anything, like currently). For example, suppose I want to store an Object, then I can document in my component's documentation that `some-attribute` can accept a string like this:

```html
<some-element some-attribute="0, 30, 0">
</some-element>
```

The `attributeChangedCallback` for `<some-element>` can look like this:

```js
class SomeElement extends HTMLElement {
    // ...
    
    attributeChangedCallback(name, oldValue, newValue) {
        if (name == 'some-attribute') {
            if (typeof newValue == 'string') {
                // if the attribute is a string of comma-separated numbers,
                // convert to an array (i.e. "deserialize").
                this.setAttribute('some-attribute', convertToArrayOfNumbers(newValue))
            }
            else if (newValue instanceof Array) {
                // ... work with an actual array ...
            }
        }
    }
}

function convertToArrayOfNumbers(string) {
    // ... possibly validate syntax here first ...
    return string.split(',')
}
```

As you can see from the example, when a `string` value is detected, the Custom Element deserializes it into an array of numbers. The advantage here is huge: it means any library like React can pass non-string values directly to leaf-most Custom Elements!

> How would one differentiate a string attribute from a number attribute?

I believe my example gives you an idea of that.

> And how would you pass more complex things like objects?

My example shows you can pass anything to `setAttribute`. That's the key. Note, HTML markup would still be string based, not change there, and would require deserialization. The benefits of non-string values would be apparent when using the JavaSript APIs directly (`setAttribute`, `getAttribute`). If we use `innerHTML` to set a component's content, then we're going to have string values and there's no way around that, there's no special syntax to denote JS objects. It is completely up to the Custom Element to handle that.

Furthermore, it is up to the Custom Element to supply ensure that the item stored in an attribute has a `toString` method. In my above example, `Array`s already have a `toString` method defined. For example, `[0,30,0].toString()` will result in `0,30,0`, which is the string format that the Custom Element expects. 

Here's another example that shows how we would convert the comma-separate-number-string into something more complex like a `Coordinates` object:

```js
class Coordinates {
    constructor(x, y, z) {
        this.x = x
        this.y = y
        this.z = z
    }
}

class SomeElement extends HTMLElement {
    // ...
    
    attributeChangedCallback(name, oldValue, newValue) {
        if (name == 'some-attribute') {
            if (typeof newValue == 'string') {
                // if the attribute is a string of comma-separated numbers,
                // convert to an array (i.e. "deserialize").
                this.setAttribute('some-attribute', convertToCoordinates(newValue))
            }
            else if (newValue instanceof Coordinates) {
                // ... work with an actual array ...
            }
        }
    }
}

function convertToCoordinates(string) {
    // ... possibly validate syntax here first ...
    const args = string.split(',')
    return new Coordinates(...args)
}
```

> I do not understand what’s that superior about attributes compared to properties.

Can you explain what you mean by "properties"? Maybe I don't understand? Do you mean regular JS properties? If so, plain JS properties aren't hooked into the HTML engine, that's the downside.

> How do you know it wouldn’t be difficult to make? Imagine all the code in browsers, libraries, and frameworks that expect attribute values to be strings.

With this change, built-in elements can continue to take/give string values, those parts won't break. However, and in my opinion, if the `style` attribute of built-in elements can be updated to accept (via `setAttribute`) an `Object` with [CSS typed-om](https://drafts.css-houdini.org/css-typed-om-1/), that'd be awesome because it would greatly increase performance. For backwards compatibility, the `getAttribute` method would still return a string. Recommended would be to read an accessor or property (like you mention) to get the Object with numbers in it, skipping the performance cost of the string conversion that will happen when using `getAttribute`. Etc.

As far as Custom Element, the API is v0. It can change. People using Custom Element right now can expect things to break for the better good of the web.

---
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/w3c/webcomponents/issues/519#issuecomment-225451885

Received on Sunday, 12 June 2016 18:15:59 UTC