Back to Lit

07

packages/lit-dev-content/site/tutorials/content/wc-to-lit/07.md

latest4.2 KB
Original Source

Styling Buttons With Attributes

Now all that's missing is the button functionality. This component should allow the user to provide a single up or down vote rating and give visual feedback to the user. In this step you'll do some setup work:

  • Add a new vote property and attribute, like the rating property you added previously.
  • Add new styles for the vote-up and vote-down buttons based on the current value of the vote attribute.

Then in the following step you'll add the event listeners to handle button clicks and set the vote attribute.

Start by adding the following lines:

index.html

css
<template>
  <style>
    ...

    :host([vote=up]) .thumb_up {
      fill: green;
    }
    :host([vote=down]) .thumb_down {
      fill: red;
    }
  </style>
</template>

In shadow DOM the :host selector refers to the node or custom element that the shadow root is attached to.

In this case, if the vote attribute is "up" (e.g. <rating-element vote="up">) it will turn the thumb-up button green.

If vote is "down" (e.g. <rating-element vote="down">), then it will turn the thumb-down button red.

Now, implement the logic for this by creating a reflecting property / attribute for vote similar to how you implemented rating. Start with the property setter and getter:

{% switchable-sample %}

ts
export class RatingElement extends HTMLElement {
  private _rating = 0;
  private _vote: 'up'|'down'|null = null;

  ...

  set vote(newValue) {
    const oldValue = this._vote;
    if (newValue === oldValue) {
      return;
    }

    if (newValue === 'up') {
      if (oldValue === 'down') {
        this.rating += 2;
      } else {
        this.rating += 1;
      }
    } else if (newValue === 'down') {
      if (oldValue === 'up') {
        this.rating -= 2;
      } else {
        this.rating -= 1;
      }
    }

    this._vote = newValue;
    this.setAttribute('vote', newValue!);
  }

  get vote() {
    return this._vote;
  }
}
js
export class RatingElement extends HTMLElement {
  _vote = null;

  ...

  set vote(newValue) {
    const oldValue = this._vote;
    if (newValue === oldValue) {
      return;
    }

    if (newValue === 'up') {
      if (oldValue === 'down') {
        this.rating += 2;
      } else {
        this.rating += 1;
      }
    } else if (newValue === 'down') {
      if (oldValue === 'up') {
        this.rating -= 2;
      } else {
        this.rating -= 1;
      }
    }

    this._vote = newValue;
    this.setAttribute('vote', newValue);
  }

  get vote() {
    return this._vote;
  }
}

{% endswitchable-sample %}

Initialize the _vote instance property with null as a class-member property, and in the setter, check if the new value is different. If so, adjust the rating accordingly and, importantly, reflect the vote attribute back to the host with this.setAttribute.

{% aside "warn" %}

It is not recommended to manipulate rating inside the vote setter in this way.

This is not the most efficient way to update rating, but it's the most convenient way for this tutorial.

{% endaside %}

Handle "vote" Attribute Changes

Next, set up the attribute binding for vote:

{% switchable-sample %}

ts
static get observedAttributes() {
  return ['rating', 'vote'];
}

attributeChangedCallback(attributeName: string, _oldValue: string, newValue: string) {
  if (attributeName === 'rating') {
    const newRating = Number(newValue);

    this.rating = newRating;
  } else if (attributeName === 'vote') {
    this.vote = newValue as 'up'|'down';
  }
}
js
static get observedAttributes() {
  return ['rating', 'vote'];
}

attributeChangedCallback(attributeName, _oldValue, newValue) {
  if (attributeName === 'rating') {
    const newRating = Number(newValue);

    this.rating = newRating;
  } else if (attributeName === 'vote') {
    this.vote = newValue;
  }
}

{% endswitchable-sample %}

This is the same process you went through with the rating attribute binding:

  • Add 'vote' to the observedAttributes.
  • Set the vote property in the attributeChangedCallback.

Check that this is working by setting the vote attribute to "up" in the browser dev tools console with $0.setAttribute('vote', 'up').