vaadin-grid sorting with templates vs renderers

Hi

I noticed that when i use templates and sort a column and then change the items, the sorting is still in effect

But when I use headerRenderers and renderers for the cell content, and then change the items, the sorting is reset

Any way of preventing this?

I am using the javascript version of vaadin-grid

thnx

Hi, could you please provide a sample code that you are using to create vaadin-grid with headerRenderer?

This is the code with renderers.

I had the same setup with templates, and that worked as expected. I can give you that as well if you want.)

I refactored it because you mentioned that lit-element does not go well with templates ( here https://vaadin.com/forum/thread/18239314/button-inside-a-vaadin-grid-column-template)


import '@vaadin/vaadin-grid';
import '@vaadin/vaadin-grid/vaadin-grid-sort-column.js';
import { registerStyles } from '@vaadin/vaadin-themable-mixin/register-styles.js';
import { styles, vaadinGridStyles } from './styles.js';

registerStyles('vaadin-grid', vaadinGridStyles);
export class TableComponent extends LitElement {
  static get properties() {
    return {
      items: {
        type: Object,
        value: {},
      },
      autoHeight: {
        type: Boolean,
      },
      _selectedItems: {
        type: Array,
        value: [],
      },
    };
  }

  static get styles() {
    return styles;
  }

  constructor() {
    super();
    this.items = {
      headers: [],
      list: [],
    };
  }

  connectedCallback() {
    super.connectedCallback();
  }

  updated() {
    const grid = this.shadowRoot.querySelector('vaadin-grid');
    if (this.autoHeight) grid.setAttribute('height-by-rows', 'true');
    this._addHeaderRenderers();
    this._addContentRenderers();
  }

  _addContentRenderers() {
    const grid = this.shadowRoot.querySelector('vaadin-grid');
    this.items.headers.map((header, index) => {
      const col = grid.querySelector(`#column${index}`);
      col.renderer = (root, column, rowData) => {
        const cellData = rowData.item[header.key]
;
        root.innerHTML = `<div class="circle"><span class="char">${cellData}</span></div>`;
      };
    });
  }

  _addHeaderRenderers() {
    const grid = this.shadowRoot.querySelector('vaadin-grid');
    this.items.headers.map((header, index) => {
      const col = grid.querySelector(`#column${index}`);
      col.autoWidth = true;
      col.headerRenderer = root => {
        root.innerHTML = `<vaadin-grid-sorter path="${header.key}">${header.name}</vaadin-grid-sorter>`;
      };
    });
  }

  _handleActiveItemChange(e) {
    if (this.selectable) {
      const item = e.detail.value;
      if (item) {
        this._selectedItems = [item]
; // single select for now
      }
    }
  }

  render() {
    return html`
      <vaadin-grid
        .items=${this._filterItems(this.items.list)}
        .selectedItems=${this._selectedItems}
        @active-item-changed=${this._handleActiveItemChange}
        theme="wrap-cell-content"
      >
        ${html`
          ${this.items.headers.map(
            (header, index) => html`
              <vaadin-grid-column id="column${index}"></vaadin-grid-column>
            `,
          )}
        `}
      </vaadin-grid>
    `;
  }
}

Thanks for the example.

When you change items, the function this.items.headers.map is called again, which most likely results in re-creating DOM nodes. This might be ok for small components, but in case of grid columns it’s not recommended.

Can you try repeat directive instead of this.items.headers.map? Alternatively, guard directive might work as well, so that it will only re-render in case items.headers are updated, but not when items.list is updated.

Thank you for the suggestions.

But the repeat directive did not fix the problem

And with the guard directive the headers were not rendered at all

The root.innerHTML = .. in the renderer resets the sorter whenever it gets invoked. You could try to skip replacing the inner HTML if the root already has content

col.headerRenderer = root => {
  if (root.firstElementChild) {
    root.innerHTML = ..;
  }	
};

That is a good suggestion, even for performance issues

But it did not solved the problem.