I would like to have an ability to add additional attributes in a component subclasses, ex
class Component {
myAttrs() {
return {
'one': 1
};
}
render() {
return html`<div ${this.myAttrs()} />`;
}
}
class SubComponent extends Component {
myAttrs() {
attrs = super();
attrs['two'] = 2;
return attrs;
}
}
The sampe doesn't works, since html template can't accepts dictionaries as a tag content.
Is there any way to dynamically construct a template with attributes from a dictionary?
You probably want something similar to classString here:
https://github.com/Polymer/lit-element/pull/132/files#diff-2f36ff503b102a822acb76a03ce0bb3fR21
No that won't work, but you could create a directive that would create the attributes
like
html`div ${addAttributes(this.myAttrs())}`
Something like (Pseudo code):
export const addAttributes =
(attrs: string[]): DirectiveFn<NodePart> =>
directive((part: NodePart): void => {
for (const attr in attrs) {
part.setAttributes(attr, attrs[attr]);
}
});
Thanks for your help!
Yep, the thing I asked could be implemented as a directive that works like react spread attribute
I could works but seems no directives supported inside tag declaration
I've got
lit-html.js:252 Uncaught TypeError: Cannot read property '1' of null
at new Template (lit-html.js:252)
while (count-- > 0) {
// Get the template literal section leading up to the first
// expression in this attribute
const stringForPart = result.strings[partIndex];
// Find the attribute name
const attributeNameInPart = lastAttributeNameRegex.exec(stringForPart)[1]; // <==
So i'd used a fake property name.
function addAttributes(attrs) {
return directive((part) => {
for (const attr in attrs) {
if (attrs.hasOwnProperty(attr)) {
part.element.setAttribute(attr, attrs[attr]);
}
}
});
}
render() {
return html`<input id="${this.props.id}" __nope="${addAttributes(this.myAttrs())}">`
}
I've got it working:

Here is the directive:
const addAttributes =
(attrs) =>
directive((part) => {
for (const attr in attrs) {
part.element.setAttribute(attr, attrs[attr]);
}
});
And I use it as:
render() {
return html`<div dummy=${addAttributes(this.myAttrs())}>hi!</div>`;
}
Reading your response now, I also had to use a dummy attribute
Maybe this is something you want to allow @justinfagnani ? or is that too hard to implement?
You probably want to do special handing of booleans:

const addAttributes = (attrs) =>
directive((part) => {
for (const attr in attrs) {
const value = attrs[attr];
part.element.setAttribute(attr, typeof value === 'boolean' ? '' : value);
}
});
You have to consider that when the element re-renders with a different list of attributes, you probably want the directive to handle removal of unset attributes as well.
Hm, after @ruphin comment, implementing such directive looks against lit-html philosophy for me. No js template literals and native values separation involved.
I'll try to avoid such situations with attributes.
Anyway, thanks everyone for your help!
Most helpful comment
You probably want to do special handing of booleans: