vendredi 4 décembre 2020

Litelement - Table sorting renders wrong values

I am trying to sort an HTML table based on it's values in my Lit element. However, I'm running into a problem with my component. Here is an overview of my table; enter image description here

The problem

In this application, you need to be able to sort on every table header. However, items which are considered 'done' need to move to the bottom of the table. My problem arises whenever I mark an item as done. In the following example I will mark the top todo (task: 123) as done. The expected behaviour is that the todo is moved to the bottom of the table with it's checkbox enabled. This is not however what is the outcome at the moment.

enter image description here

As you can see, the todo item with task 123 is moved to the bottom. However, the todo with task 456 also gets it's checkbox marked. This is not desired behaviour and I don't know what's causing it. You can also see that the colors are not correct (this is some styling to show you that a changed todo is being saved, yellow = saving, green = saved and = error).

Things I have tried

Since I don't know what is exactly causing this issue I don't know what I should do. I gave all my inputs/rows/td's id's to make sure nothing gets mixed up, but that doesn't seem to work.

Code

import { LitElement, html, css } from 'lit-element';

class TableList extends LitElement {
    static get properties() {
        return {
            data: {
                type: Array
            },
            primaryKey: {
                type: String
            },
            defaultSortKey: {
                type: String
            }
        };
    }

    set data(value) {
        let oldValue = this._data;
        this._data = [ ... value];
        this.sortByHeader();
        this.requestUpdate('data', oldValue);
    }

    get data() {
        return this._data;
    }
    
    async edit(entry, key, event) {
        this.shadowRoot.getElementById('entry' + entry[this.primaryKey].value).classList.remove('saved');
        this.shadowRoot.getElementById('entry' + entry[this.primaryKey].value).classList.remove('error');
        this.shadowRoot.getElementById('entry' + entry[this.primaryKey].value).classList.add('saving');

        if (entry[key].type === "checkbox") {
            entry[key].value = event.target.checked;
        } else {
            entry[key].value = event.target.value;
        }

        if (await update(entry)) {
            this.shadowRoot.getElementById('entry' + entry[this.primaryKey].value).classList.remove('saving');
            this.shadowRoot.getElementById('entry' + entry[this.primaryKey].value).classList.add('saved');

            setTimeout(() => {
                this.shadowRoot.getElementById('entry' + entry[this.primaryKey].value).classList.remove('saved');
            }, 1000);
        } else {
            this.shadowRoot.getElementById('entry' + entry[this.primaryKey].value).classList.remove('saving');
            this.shadowRoot.getElementById('entry' + entry[this.primaryKey].value).classList.add('error');

            setTimeout(() => {
                this.shadowRoot.getElementById('entry' + entry[this.primaryKey].value).classList.remove('error');
            }, 5000);
        }
    }

    sortByHeader(key) {       
        if (key === undefined) {
            key = this.defaultSortKey;
        }

        let oldValue = this.data;
        this._data = [ ... this.data.sort((a, b) => {
            return a[this.defaultSortKey].value - b[this.defaultSortKey].value 
                        || a[key].value - b[key].value;
        })];

        this.requestUpdate('data', oldValue);
    }

    renderHeaders() {
        let keys = Object.keys(this.data[0]);

        return keys.map(key => html`
            ${this.data[0][key].visible ?
                html`
                    <th id="${'header' + key}" @click="${() => this.sortByHeader(key)}">
                        ${key}
                    </th>
                `: ''
            }
        `)
    }

    renderRows() {
        return this.data.map(entry => html`
        <tr id="${'entry' + entry[this.primaryKey].value}">
            ${Object.keys(entry).map(key => html`
                ${entry[key].visible && !entry[key].editable ?
                    html`<td>${entry[key].value}</td>`
                    : ``
                }
                ${entry[key].visible && entry[key].editable ?
                    html`<td id="${'td' + key + entry[this.primaryKey].value}">
                            <input
                                id="${'input' + key + entry[this.primaryKey].value}"
                                name="${'input' + key + entry[this.primaryKey].value}"
                                type="${entry[key].type}"
                                ?checked="${entry[key].value}"
                                value="${entry[key].value}"
                                @change="${(event) => {
                                    this.edit(entry, key, event)
                                }}"
                            />
                        </td>`
                    : ``
                }
            `)}
        </tr>
        `)
    }

    render() {
        return html`
            <table id="table-list">
                <thead>
                    <tr>
                        ${this.renderHeaders()}
                    </tr>
                </thead>
                <tbody>
                    ${this.renderRows()}
                </tbody>
            </table>
      `;
    }

    static get styles() {
        return css`
            table {
                width: 100%;
                border-collapse: collapse;
                font-family: Arial, Helvetica, sans-serif;
            }

            th {
                padding-top: 12px;
                padding-bottom: 12px;
                text-align: center;
                background-color: #4CAF50;
                color: white;
            }

            tr {
                text-align: right;
                -moz-transition: all .2s ease-in;
                -o-transition: all .2s ease-in;
                -webkit-transition: all .2s ease-in;
                transition: all .2s ease-in;
                background: white; 
                padding: 20px;
            }

            .disabled {
                color: lightgrey;
            }

            .saving {
                background: yellow;
            }

            .saved {
                background: lightgreen;
            }

            .error {
                background: red;
            }

            .sort:after {
                content: ' ↓';
            }
        `;
    }
}

export default TableList;



Aucun commentaire:

Enregistrer un commentaire