Search code examples
javascriptattributespolymer

Bind dictionary of attributes on element definition


I am trying to bind a dictionary of attributes like {'id': 'myid', 'class': 'my-1 pr-md-2', ...} at the time I am defining the element. I do not want to set that attributes after the DOM is loaded by Javascript.

I am struggling with the correct form of doing this. I can not bind them one by one declaring the attribute name and value manually as they are user given parameters. I have think of appending them to the attributes property, but I do not know if it is advisable to do it this way.

This is my data structure:

<template is="dom-repeat" items="{{links}}" as="link">
    <a class="nav-item nav-link mr-md-2" on-tap="changePage">{{link.title}}</a>
</template>

and the attributes are saved in each link.attributes property. My solution would be something like this:

HTML

<template is="dom-repeat" items="{{links}}" as="link">
    <a attributes={{appendAttributes(link)}} class="nav-item nav-link mr-md-2" on-tap="changePage">{{link.title}}</a>
</template>

JS

appendAttributes: function(link){
    //Get current attributes of the element and append the ones in link.attributes
}

Is this the correct way to handle it?


Solution

  • As far as I know this is not possible with Polymer's templating system: there's no way to access the element to which a computed binding is applied to.

    This

    <a attributes={{appendAttributes(link)}}></a>
    

    can't work because the attributes property is read-only.

    I can not bind them one by one declaring the attribute name and value manually as they are user given parameters

    Actually if you know in advance what attributes/properties have to be set you can still set them dynamically:

    <a id=[[userGivenId]]
       class$=[[userGivenClass]]
       ...
    ></a>
    

    Anyway, there is a lit-html directive made by open-wc called spread which does just what you want. This would require rewriting your component using LitElement to something like this:

    import { LitElement, html, property, customElement } from 'lit-element';
    import { repeat } from 'lit-html/directives/repeat';
    import { spread } from '@open-wc/lit-helpers';
    
    @customElement('my-element')
    export class MyElement extends LitElement {
      @property() userGivenId;
      @property() links;
      // ...
    
      render() {
        return html`
    
          ${repeat(this.links, link => html`
    
            <a ...=${spread({
                 id: this.userGivenId,
                 '?my-boolean-attribute': true
                 '.myProperty': { foo: 'bar' },
                 '@my-event': () => console.log('my-event fired'),
               })}
               class="nav-item nav-link mr-md-2" 
               @click=${e => this.changePage(e)}
            >${link.title}</a>
    
          `)}
    
        `;
      }
    }
    

    With some limitations, PolymerElements and LitElements can coexist in the same project so converting a single component shouldn't cause any trouble.