Модуль JavaScript для фильтрации данных и отобразить его в виде таблицы


Я хочу организовать некоторые JavaScript-код, используя шаблон модуля.

Сценарий выглядит следующим образом: имея следующие HTML-таблицы создаются динамически

<table class="table table-hover">
    <tbody>
        <tr>
            <td colspan="2" style="border-top: 0">2018-03-27</td>
        </tr>
        <tr>
            <td data-id="8" data-user="Yesenia" data-classroom="D101" data-date="2018-03-27" style="vertical-align: middle;">
                <a href="http://localhost:8080/ds/request/show/8">Yesenia en D101</a>
            </td>
            <td width="1">
                <form action="http://localhost:8080/ds/request/delete/8" method="POST" value="DELETE" onsubmit="if (!confirm('¿Estás seguro?')) return false;" style="margin: 0;">
                    <input name="_method" value="DELETE" type="hidden">
                    <button type="submit" class="btn btn-link btn-small">
                        <i class="icon-trash"></i>
                    </button>
                </form>
            </td>
        </tr>
        <tr>
            <td colspan="2">2018-03-26</td>
        </tr>
        <tr>
            <td data-id="1" data-user="user user" data-classroom="D101" data-date="2018-03-26" style="vertical-align: middle;">
                <a href="http://localhost:8080/ds/request/show/1">user user en D101</a>
            </td>
            <td width="1">
                <form action="http://localhost:8080/ds/request/delete/1" method="POST" value="DELETE" onsubmit="if (!confirm('¿Estás seguro?')) return false;" style="margin: 0;">
                    <input name="_method" value="DELETE" type="hidden">
                    <button type="submit" class="btn btn-link btn-small">
                        <i class="icon-trash"></i>
                    </button>
                </form>
            </td>
        </tr>
        <tr>
            <td data-id="2" data-user="user user" data-classroom="D102" data-date="2018-03-26" style="vertical-align: middle;">
                <a href="http://localhost:8080/ds/request/show/2">user user en D102</a>
            </td>
            <td width="1">
                <form action="http://localhost:8080/ds/request/delete/2" method="POST" value="DELETE" onsubmit="if (!confirm('¿Estás seguro?')) return false;" style="margin: 0;">
                    <input name="_method" value="DELETE" type="hidden">
                    <button type="submit" class="btn btn-link btn-small">
                        <i class="icon-trash"></i>
                    </button>
                </form>
            </td>
        </tr>
        <tr>
            <td data-id="7" data-user="Yesenia" data-classroom="D103" data-date="2018-03-26" style="vertical-align: middle;">
                <a href="http://localhost:8080/ds/request/show/7">Yesenia en D103</a>
            </td>
            <td width="1">
                <form action="http://localhost:8080/ds/request/delete/7" method="POST" value="DELETE" onsubmit="if (!confirm('¿Estás seguro?')) return false;" style="margin: 0;">
                    <input name="_method" value="DELETE" type="hidden">
                    <button type="submit" class="btn btn-link btn-small">
                        <i class="icon-trash"></i>
                    </button>
                </form>
            </td>
        </tr>
        <tr>
            <td data-id="9" data-user="Cesar" data-classroom="D101" data-date="2018-03-26" style="vertical-align: middle;">
                <a href="http://localhost:8080/ds/request/show/9">Cesar en D101</a>
            </td>
            <td width="1">
                <form action="http://localhost:8080/ds/request/delete/9" method="POST" value="DELETE" onsubmit="if (!confirm('¿Estás seguro?')) return false;" style="margin: 0;">
                    <input name="_method" value="DELETE" type="hidden">
                    <button type="submit" class="btn btn-link btn-small">
                        <i class="icon-trash"></i>
                    </button>
                </form>
            </td>
        </tr>
    </tbody>
</table>

А входной фильтр в таблице

<input id="filter" placeholder="Filtrar...">

Следующие JavaScript-логика реализована, что позволило отфильтровать таблицу

const dataset = Array.from(document.querySelectorAll('[data-id]')).map(td => Object.assign({}, td.dataset));
const trigger = document.querySelector('#filter');
const serverURL = window.serverURL;

function applyFilter(event) {
    const results = dataset.filter(data => data.user.toLowerCase().includes(event.target.value));

    render(results);
}

function render(dataset) {
    let date = null;
    const rows = [];

    dataset.forEach((data, index) => {
        if (data.date !== date) {
            rows.push(renderDateRow(data.date, index));
        }

        rows.push(renderDataRow(data));

        date = data.date;
    });

    document.querySelector('table tbody').innerHTML = rows.join('');
}

function renderDateRow(date, index) {
    return `<tr>
        <td colspan="2" ${index === 0 ? 'style="border-top: 0"' : ''}>${date}</td>
    </tr>`;
}

function renderDataRow(data) {
    return `<tr>
        <td
            data-id="${data.id}"
            data-user="${data.user}"
            data-classroom="${data.classroom}"
            data-date="${data.date}"
            style="vertical-align: middle;">
            <a href="${serverURL}/request/show/${data.id}">${data.user} en ${data.classroom}</a>
        </td>
        <td width="1">
            <form
                action="${serverURL}/request/delete/${data.id}"
                method="POST"
                value="DELETE"
                onSubmit="if (!confirm('¿Estás seguro?')) return false;"
                style="margin: 0;">
                <input type="hidden" name="_method" value="DELETE">
                <button type="submit" class="btn btn-link btn-small">
                    <i class="icon-trash"></i>
                </button>
            </form>
        </td>
    </tr>`;
}

trigger.addEventListener('keyup', applyFilter);

Я использую шаблон модуля для того, чтобы организовать код, окончательная реализация выглядит следующим образом:

const FilterModule = (() => {
    const dataset = Array.from(document.querySelectorAll('[data-id]')).map(td => Object.assign({}, td.dataset));

    function loadServerURL() {
        return window.serverURL;
    }

    function applyFilter(event) {
        const results = dataset.filter(data => data.user.toLowerCase().includes(event.target.value));

        render(results);
    }

    function loadTrigger() {
        document.querySelector('#filter').addEventListener('keyup', applyFilter);
    }

    function render(dataset) {
        let date = null;
        const rows = [];

        dataset.forEach((data, index) => {
            if (data.date !== date) {
                rows.push(renderDateRow(data.date, index));
            }

            rows.push(renderDataRow(data));

            date = data.date;
        });

        document.querySelector('table tbody').innerHTML = rows.join('');
    }

    function renderDateRow(date, index) {
        return `<tr>
            <td colspan="2" ${index === 0 ? 'style="border-top: 0"' : ''}>${date}</td>
        </tr>`;
    }

    function renderDataRow(data) {
        const serverURL = loadServerURL();

        return `<tr>
            <td
                data-id="${data.id}"
                data-user="${data.user}"
                data-classroom="${data.classroom}"
                data-date="${data.date}"
                style="vertical-align: middle;">
                <a href="${serverURL}/request/show/${data.id}">${data.user} en ${data.classroom}</a>
            </td>
            <td width="1">
                <form
                    action="${serverURL}/request/delete/${data.id}"
                    method="POST"
                    value="DELETE"
                    onSubmit="if (!confirm('¿Estás seguro?')) return false;"
                    style="margin: 0;">
                    <input type="hidden" name="_method" value="DELETE">
                    <button type="submit" class="btn btn-link btn-small">
                        <i class="icon-trash"></i>
                    </button>
                </form>
            </td>
        </tr>`;
    }

    function init() {
        loadTrigger();
    }

    return {
        init
    };
})();

FilterModule.init();

Я благодарен Вам за любые замечания для того, чтобы лучше организовать код



Комментарии
1 ответ

Ваш модуль очень хорошо, и большая часть кода-это действительно умно.

У меня есть пару предложений для вас, чтобы помочь вам увеличивать ваш дизайн.

Муфта

Самый главный вопрос я вижу в вашем коде заключается в том, что ваш модуль жестко связан с таблицей, что ты делаешь и как ты свяжешь данные.

Что вы имеете воздействие в модуле, если вы собираетесь изменить сервировки стола или загрузки данных.

Вот в том и дело, что пострадавшие являются:

const dataset = Array.from(document.querySelectorAll('[data-id]')).map(td => Object.assign({}, td.dataset));

Как вы видите, не только вы должны исправить селектор данных, но даже то, как вы найти в DOM-элемент и конечно же DOM-элемент...

function renderDateRow(date, index) {...

И

function renderDataRow(data) {...

Те, кто работают, нужно точно знать, как вывести строки в таблице, так что если вам нужен стол без фильтра, вы должны дублировать этот код.
И если вы должны изменить макет таблицы вы должны изменить этот код либо.
Это вопрос Для ремонт модуля.

Как преодолеть

Теперь я предлагаю какое-то решение, что сцепление.

Вы должны изменить ваш метод init() функция, чтобы принять некоторые зависимости, такие как:

function init(loadData, tableRenderer)

Как метод loadData() и tableRenderer() должна быть функцией вы собираетесь ввести в свой код.

Этот метод loadData() прост, он просто возвращает список записей.

tableRenderer(сведения) должны просто принять новый рекорд набора данных: один фильтрованный.

Не нужных функций

Привязку события тривиально в вашем случае, поэтому вы должны избавиться от loadTrigger() функция, и код непосредственно в функцию init:

function init(loadData, tableRenderer, filterSelector) {
const filterElement = document.querySelector(filterSelector || '#filter');

filterElement.addEventListener('keyup', applyFilter);
}

Вы можете даже иметь преимущество вводят функции имеют функцию applyFilter():

function makeFilterFrom(dataset, renderer) {
return function (event) {
renderer(applyFilter(event, dataset));
}
}

И так линия связывать стали:

...
filterElement.addEventListener('keyup', makeFilterFrom(loadData(), tableRenderer));
...

Очистка

Как ваш модуль зарегистрировать прослушиватель событий, вы должны иметь функцию, которая избавиться от, что для того, чтобы избежать утечек памяти.

const listeners = [];

...
const handler = makeFilterFrom(loadData(), tableRenderer);
filterElement.addEventListener('keyup', handler);
listeners.push({element: filterElement, event: 'keyup', fn: handler});

...
function cleanup() {
while(listeners.length) {
let listener = listener.pop();
listener.element.removeEventListener(listener.event, listener.fn);
}
}

И тогда вы должны предоставлять эту функцию, чтобы позволить пользователю модуль использовать его.

Именования

В FilterModule не хорошее имя, как вы используете модуль , что является избыточным.

Лучшее название можно папку contentfilter или TableFilter.

Называя вещи всегда Херд, но попробовать лучше имя всегда стоит.

Вот переписанный модуль:

// That should be a constant, you don't need a function
const serverURL = window.serverURL;

function loadData() {
const dataTable = document.querySelectorAll('[data-id]');
return Array.from(dataTable).map(td => Object.assign({}, td.dataset));
}

function render(dataset) {
let date = null;
const rows = [];

dataset.forEach((data, index) => {
if (data.date !== date) {
rows.push(renderDateRow(data.date, index));
}

rows.push(renderDataRow(data));

date = data.date;
});

document.querySelector('table tbody').innerHTML = rows.join('');
}

function renderDateRow(date, index) {
return `<tr>
<td colspan="2" ${index === 0 ? 'style="border-top: 0"' : ''}>${date}</td>
</tr>`;
}

function renderDataRow(data) {
return `<tr>
<td
data-id="${data.id}"
data-user="${data.user}"
data-classroom="${data.classroom}"
data-date="${data.date}"
style="vertical-align: middle;">
<a href="${serverURL}/request/show/${data.id}">${data.user} en ${data.classroom}</a>
</td>
<td width="1">
<form
action="${serverURL}/request/delete/${data.id}"
method="POST"
value="DELETE"
onSubmit="if (!confirm('¿Estás seguro?')) return false;"
style="margin: 0;">
<input type="hidden" name="_method" value="DELETE">
<button type="submit" class="btn btn-link btn-small">
<i class="icon-trash"></i>
</button>
</form>
</td>
</tr>`;
}

// Here starts your actual module, the previous code is just
// from other modules
const ContentFilter = (() => {
const listeners = [];

function applyFilter(filterValue, dataset) {
return dataset.filter(record =>
record.user.toLowerCase().includes(filterValue));
}

function makeFilterFrom(dataset, renderer) {
return function (event) {
renderer(applyFilter(event.target.value, dataset));
}
}

// An utility to forge the object
function listenerFrom(element, event, fn) {
return {element: element, event: event, fn: fn};
}

function init(loadData, tableRenderer, filterSelector) {
const filterElement = document.querySelector(filterSelector || '#filter');
const handler = makeFilterFrom(loadData(), tableRenderer);
listeners.push(listenerFrom(filterElement, 'keyup', handler));
filterElement.addEventListener('keyup', handler);
}

function cleanup () {
while(listeners.length) {
let listener = listener.pop();
listener.element.removeEventListener(listener.event, listener.fn);
}
}

return {
init,
cleanup
};
})();

ContentFilter.init(loadData, render, '');

// Rememeber to handle the cleanup on page unload or if you
// redraw all the piece of page where you have the filter
// module.

2
ответ дан 27 марта 2018 в 10:03 Источник Поделиться