Skip to content

Commit

Permalink
Bind this to custom attribute converter methods
Browse files Browse the repository at this point in the history
  • Loading branch information
nebarf committed Jul 7, 2022
1 parent c93a8ee commit b015dcf
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 3 deletions.
5 changes: 5 additions & 0 deletions .changeset/old-tables-own.md
@@ -0,0 +1,5 @@
---
'@lit/reactive-element': patch
---

Bind this to custom attribute converter methods
10 changes: 7 additions & 3 deletions packages/reactive-element/src/reactive-element.ts
Expand Up @@ -1069,9 +1069,11 @@ export abstract class ReactiveElement
this.constructor as typeof ReactiveElement
).__attributeNameForProperty(name, options);
if (attr !== undefined && options.reflect === true) {
const converter = options.converter;
const toAttribute =
(options.converter as ComplexAttributeConverter)?.toAttribute ??
defaultConverter.toAttribute;
(converter as ComplexAttributeConverter)?.toAttribute?.bind(
converter
) ?? defaultConverter.toAttribute;
const attrValue = toAttribute!(value, options.type);
if (
DEV_MODE &&
Expand Down Expand Up @@ -1119,7 +1121,9 @@ export abstract class ReactiveElement
const options = ctor.getPropertyOptions(propName);
const converter = options.converter;
const fromAttribute =
(converter as ComplexAttributeConverter)?.fromAttribute ??
(converter as ComplexAttributeConverter)?.fromAttribute?.bind(
converter
) ??
(typeof converter === 'function'
? (converter as (value: string | null, type?: unknown) => unknown)
: null) ??
Expand Down
66 changes: 66 additions & 0 deletions packages/reactive-element/src/test/reactive-element_test.ts
Expand Up @@ -302,6 +302,72 @@ suite('ReactiveElement', () => {
assert.equal(el.getAttribute('foo'), 'toAttribute: FooType');
});

test('property option `converter` can use a class instance', async () => {
class IntegerAttributeConverter
implements ComplexAttributeConverter<Number>
{
private _defaultValue: Number;

constructor(defaultValue: Number) {
this._defaultValue = defaultValue;
}

toAttribute(value: Number, _type?: unknown): unknown {
return `${value}`;
}

fromAttribute(value: string | null, _type?: unknown): Number {
if (!value) {
return this._defaultValue;
}

const parsedValue = Number.parseInt(value, 10);
if (isNaN(parsedValue)) {
return this._defaultValue;
}
return parsedValue;
}
}

const defaultIntAttrConverterVal = 1;

class E extends ReactiveElement {
static override get properties() {
return {
num: {
type: Number,
converter: new IntegerAttributeConverter(
defaultIntAttrConverterVal
),
reflect: true,
},
};
}

num?: number;
}

customElements.define(generateElementName(), E);
const el = new E();
container.appendChild(el);
await el.updateComplete;

assert.equal(el.getAttribute('num'), null);
assert.equal(el.num, undefined);

el.setAttribute('num', 'notANumber');
await el.updateComplete;
assert.equal(el.num, defaultIntAttrConverterVal);

el.num = 10;
await el.updateComplete;
assert.equal(el.getAttribute('num'), '10');

el.setAttribute('num', '5');
await el.updateComplete;
assert.equal(el.num, 5);
});

test('property/attribute values when attributes removed', async () => {
class E extends ReactiveElement {
static override get properties() {
Expand Down

0 comments on commit b015dcf

Please sign in to comment.