Search code examples
typescriptvaadinlit-element

How can you change state in a web component in a way that can be interpreted by history using vaadin-router?


I have a fairly simple LitElement custom component that I want to have an "editing" state for. Since I have all the information from the server already, I don't want to load a new component just for the purpose of showing an editing screen or reload the current component, but I do want to make it so that a user pressing the back button is brought out of the editing state. I'm not sure how to do both.

Simple example:

@customElement('my-element')
class MyElement extends LitElement {

  @property({ attribute: false }) private _editing = false;
  @property({ attribute: false }) private _data!: Data;

  public connectedCallback() {
    super.connectedCallback();
    // Loading stuff..
  }

  public render() {
    return this._editing
      ? html`
          <editing-control .data=${this._data}></editing-control>
        `
      : html`
          <details-control .data=${this._data}></details-control>
          <button @click=${this._onEditClick}>Edit</button>
        `;
  }

  private _onEditClick(e: Event) {
    this._editing = true;
  }
}

Note I'm using Vaadin router for routing and the following approaches have problems

  • Having the edit button add #editing to the url (either by having it be a link or changing the location with Route.go or window.location.href) causes the router to reconnect the component, triggering a re-load of the data and reset of state
  • Attempting to listen to 'popstate', I get the notification after vaadin-router, so it changes to the previous location before I can modify its behavior
  • using onBeforeLeave, I can't distinguish between an attempt to go back versus a legitimate request to go do a different page from the menu

Solution

  • I suppose you have something like this in your routes configuration

    {
      path: '/foo',
      component: 'my-foo-component'
    }
    

    You should modify it and use action instead of component, in the action you need to return the same instance of the component

    my fooElement = document.createElement('my-foo-component');
    ....
    
    {
      path: '/foo',
      action: () => fooElement;
    }