Moving from Polymer to Lit

Does anyone have a link to a good tutorial for migrating custom cards from Polymer to Lit?

Otherwise I’m not sure I can maintain the custom cards I’ve provided to the community for several years now.

I barely had the knowledge to maintain and extend the existing cards. I definitely don’t have the knowledge to migrate them over. Especially needing to do it in less than a month.

It would be a shame to lose those plugins. I know a lot of people use them.

Any help would be appreciated.

3 Likes

Maybe ask in the frontend dev Discord channel.

The best starting documentation would be Custom Cards | Home Assistant Developer Docs

To start, you’ll change what the class extends from Polymer > LitElement
assuming your doing

import {
  LitElement,
  html,
  css,
} from "https://unpkg.com/[email protected]/lit-element.js?module";

Next instead of

static get template() {
	return Polymer.html
}

you’d do

// Instead of hass="[[hass]]" etc. you would do .hass=${this.hass} and so forth (normal attributes don't have the period(.) at the start
// For events from the element instead of on-click="_handleClick" do @click=${this._handleClick}
render() {
    return html`
      ...
    `;
}

All styles from your template function would go in

// Dont add style tags
static get styles() {
    return css`
      ...
    `;
}

That should get you started but if you want to observe changes etc you can check the life cycle of LitElement in their docs or use the getters and setters

set hass( obj ) {
    ...
}

Just be careful as there is a caveat in using the getters and setters, if you don’t store the setter then you won’t be able to access it which the document above doesn’t elaborate on with the set hass method. So to be able to handle changes to the hass object and still access it in your class without rewriting a bunch of code do the following.

set hass( obj ) {
    this._hass = obj; // Don't set it to the same name or you'll cause an infinite loop
    // Add code here that handles a change in the hass object
}
get hass() {
    return this._hass; // Wherever your code calls this.hass it will return the stored value of _hass
}
3 Likes

finally a certain way of finding the cards that use that… looking for Polymer only returned a lot of false positives.

in my config, only lovelace-text-input-row uses it, and it is a very small card. however, I can not edit it to work with your suggestions above. Would you be willing to use this as an example of how to do so?

class TextInputRow extends Polymer.Element {

  static get template() {
    return Polymer.html`
      <paper-input
          label="[[label]]"
          value="[[value]]"
          minlength="[[minlength]]"
          maxlength="[[maxlength]]"
          autoValidate="[[pattern]]"
          pattern="[[pattern]]"
          type="[[mode]]"
          on-change="valueChanged"
          id="textinput"
          placeholder=""
        ></paper-input>
    `;
  }

  ready() {
    super.ready();
    this.$.textinput.addEventListener('click', ev => ev.stopPropagation());
  }

  setConfig(config) {
    this._config = config;
  }

  valueChanged(ev) {
    const newValue = this.$.textinput.value;
    const param = {
      entity_id: this._config.entity,
      value: newValue,
    };
    this._hass.callService('input_text', 'set_value', param);
  }

  computeObjectId(entityId) {
    return entityId.substr(entityId.indexOf(".") + 1);
  }

  computeStateName(stateObj){
    return stateObj.attributes.friendly_name === undefined 
    ? this.computeObjectId(stateObj.entity_id).replace(/_/g, " ") 
    : stateObj.attributes.friendly_name || "";
  }

  set hass(hass) {
    this._hass = hass;
    this.stateObj = hass.states[this._config.entity];
    if(this.stateObj) {
      this.value = this.stateObj.state;
      this.minlength = this.stateObj.attributes.min;
      this.maxlength = this.stateObj.attributes.max;
      this.pattern = this.stateObj.attributes.pattern;
      this.mode = this.minlength = this.stateObj.attributes.mode;
      this.label = this._config.name ? this._config.name : this.computeStateName(this.stateObj);
    }
  }
}

customElements.define('text-input-row', TextInputRow);

would be much appreciated!

Try to follow the “guide” above first then I will look at what you’re having issues with. Atm nothing apart from the set hass from my guide was followed. This will make it easier for me to help otherwise I would literally be rewriting the code above.

I will also point out that the ready method doesn’t need to exist as you can do @click=${ev => ev.stopPropagation()} on the paper-input itself

sure will do. btw, I didn’t change anything at all yet, this is the original code of that particular resource.

there might be an even better way, as this cards result can also be achieved by:

            card_mod:
              style:
                hui-generic-entity-row $: |
                  state-badge {
                    display: none;
                  }

to make a core input_text full width…

sorry, I should have checked that before I posted here. Yet I will try and make it work anyhow.
appreciated!

1 Like

Thanks for the info.

I kind of already had a start and got some of what you noted.

I’ll try a bit more.

I’m sure I’ll have questions so thank you in advance.

I’ll give it a shot.

this is what I made for now:

and it looks like:

seems ok, apart from the red styling and bigger font… not sure why that is.
If you or anyone else could have a look, that would be appreciated, thx!

edit

red is showing because of the auto-validate, and we can take that out.
More serious issue: the state wont get updated upon clicking enter…

is this a matter of the old paper elements?

all good now

1 Like