Lit-html: Support for attributes spread directive

Created on 9 Aug 2018  路  9Comments  路  Source: Polymer/lit-html

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?

Most helpful comment

You probably want to do special handing of booleans:

image

const addAttributes = (attrs) =>
        directive((part) => {
          for (const attr in attrs) {
            const value = attrs[attr];
            part.element.setAttribute(attr, typeof value === 'boolean' ? '' : value);
          }
        });

All 9 comments

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:

image

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:

image

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!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

pietmichal picture pietmichal  路  4Comments

justinfagnani picture justinfagnani  路  3Comments

abdonrd picture abdonrd  路  4Comments

AndyOGo picture AndyOGo  路  3Comments

depeele picture depeele  路  3Comments