Реализовать возможность фильтровать по оставшимся выпадающим спискам.
Причем фильтры по спискам должны объединятся.
Как-то так должно работать:
Продолжим работу с данными. Сегодня у нас будет функция filter
Суть функции filter заключается в том чтобы взять и отфильтровать данные, то есть взять список и оставить в нем подходящие данные, убрать не подходящие.
Чтобы не ломать код, которые был сделан в прошлом задании, я создам новую функцию в main.js и назову ее processFilter
, запихаю в нее стягивание списка данных, вот так:
чтобы не захламлять console.log
, убери все вызовы console.log
из функции process
То есть при запуске приложения должно быть так
Как и функции forEach и map, функция filter работает по такому же принципе. Передаем ей анонимную функцию и чего-то происходит.
У forEach
в анонимной функции может быть что угодно, как правило это какая-та операция. И функция не должна ничего возвращать (хотя в принципе может, но это ни на что влиять не будет)
Например:
data.forEach(item => {
console.log(item);
})
У map
анонимная функция обязательно должна вернуть какое-то новое значение, а результат выполнения функции обязательно кладут в переменную. Например:
let newData = data.forEach(item => {
return item['ключик'];
})
Новый список который создает функция map
всегда содержит столько же значений сколько и исходный список, можно вспомнить такую картинку
тут в консоли два раза выводятся списки длины 38. В первом случае – исходный, а во втором – список значений по ключу на каждый элемент из исходного списка.
Так вот, функция filter, берет исходный список и оставляет в нем только элементы, удовлетворяющие некоторому условию.
У filter анонимная функция должна вернуть логическое значение (ложь либо истину).
Если значения анонимной функции получается истинное, то элемент попадает в новый список, а если ложное – то не попадает.
Давай чтобы было понятнее, сначала рассмотрим на простом примере. Ну типа вот таком
let filteredData = [1,2,3,4].filter(item => {
return item > 2
})
console.log(filteredData);
что же вернет эта вундервафля:
как видишь, он возвращает элементы, которые большие двух, что собственно в return item > 2
и написано.
Мы можем сделать хитрое условие какое-нибудь
let filteredData = [1,2,3,4].filter(item => {
return item > 1 && item < 4
})
и тут работает. Потрясающе! =О
Кстати смотрим, так как тут у нас анонимная функция, которая просто возвращает значение, это значит, что этот код можно писать короче, вот так:
let filteredData = [1,2,3,4].filter(item => item > 1 && item < 4)
жутковато-то получилось, но в жизни так часто пишут =)
И так вернемся к нашему списку:
[
...
{
"ID": "1385327090",
"Отметь, в какой мере ты удовлетворен курсом?": "В основном",
"Насколько курс был интересен?": "Интересный",
"Насколько курс был полезен?": "Полезный",
"Насколько материал курса был понятен?": "В основном",
"Насколько доволен форматом обучения?": "В основном",
"Оцени работу преподавателя по шкале от 1-10 / 10 – высоко оцениваю работу преподавателя. 1 – совсем не понравилась работа преподавателя": "8"
},
...
]
я хочу запросить список отзывов, в которых отметили для ключа "Насколько курс был полезен?"
значение "Полезный"
Как же это сделать? А нет ничего проще, просто пишем:
let filteredData = data.filter(item => item["Насколько курс был полезен?"] == "Полезный")
console.log(filteredData);
проверяем как работает условие
красота! =)
Попробуй поиграться с другими ключами =О
А если чувствуешь себя уверено, то идем дальше
Давай теперь попробуем выводить список на страницу.
Создадим функцию и назовем ее fillList
, загоним в нее стандартный код скачивания списка
async function fillList() {
let r = await fetch("/data.json");
let data = await r.json();
console.log(data);
}
в сумме должно так выглядеть
теперь в файлике index.html
, создадим таблицу под элементы списка. Я буду использовать табличку https://getbootstrap.com/docs/5.3/components/table/
<div class="container mt-2">
<table class="table" id="elements-container">
<thead>
<tr>
<th>ID</th>
<th>Полезность</th>
<th>Довольность</th>
<th>Удовлетворенность</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
вот так получится
идем обратно в main.js
так как я буду добавлять строки в тело таблицы, мне надо по id
таблицы вытащить это самое tbody. Для этого надо написать вот так:
let container = document.querySelector("#elements-container > tbody");
console.log(container)
А теперь надо его заполнить. Заполняем, как и выпадающий список в прошлом задании через forEach.
Мы воспользуемся классным методом insertAdjacentHTML
, который позволяет передать текст разметки, а он его возьмет и автоматом вставит в контейнер. Получается такое:
data.forEach(item => {
container.insertAdjacentHTML("beforeend", `
<tr>
<td>${item['ID']}</td>
<td>${item['Насколько курс был полезен?']}</td>
<td>${item['Насколько доволен форматом обучения?']}</td>
<td>${item['Отметь, в какой мере ты удовлетворен курсом?']}</td>
</tr>
`);
})
попробуем теперь пофильтровать выводимые данные.
У нас сейчас выводится весь список, но было бы прикольно если бы мы могли вверху выбрать в выпадающем списке значение, и он бы оставил только те строки, которые соответствуют выбранному вверху значению.
Например, оставить только те строки полезность которых была “Полезной”
Попробуем сначала просто вывести отфильтрованный список. Сделаем это прямо в методе fillList
проверяем
а что если я хочу брать значение с формы. Тогда надо написать так:
проверим как работает:
чего-то не работает… Почему так?
Дело в том, что наша функция fillList
срабатывает ровно один раз при запуске приложения. А вот когда мы переключаем значение в списке, то ничего не происходит.
Мы можем добавить обработчик события изменения значения в списке. Для этого добавим функцию onSelectPoleznostChanged
теперь пойдем в index.html и привяжем эту функцию в качестве обработчика события смены значения в списке. Делается это так:
давай теперь добавим какой-нибудь вывод в консоль в функцию onSelectPoleznostChanged
async function onSelectPoleznostChanged() {
console.log("Поменялся")
}
проверим как работает:
а теперь попробуем взять значение из списка
async function onSelectPoleznostChanged() {
let selectPoleznost = document.querySelector("#selectPoleznost")
console.log(selectPoleznost.value)
}
тестируем:
красота! =)
А попробуем-ка вызывать метод fillList() внутри этой функции,
async function onSelectPoleznostChanged() {
let selectPoleznost = document.querySelector("#selectPoleznost")
console.log(selectPoleznost.value)
fillList()
}
проверяем:
ну что-то происходит…
Если присмотреться, то наш список который выводится на странице – растет.
из-за чего это происходит?
Дело в том, что мы в методе fillList всегда добавляем элементы в наш список, и никогда не удаляем
давай добавим перед вызовом forEach()
строчку, где будем чистить список:
проверим что получилось:
отлично, все работает! =)
Возможность отфильтровать – это конечно здорово. Но что, если я хочу возможность сбросить фильтр?
Для выпадающего списка это обычно решается путем добавления пункта типа “не важно” или “все”
Идем в index.html
и добавляем там:
мы добавили один пункт меню вручную, остальные у нас добавляются, как ты помнишь, динамически
единственная проблема сейчас, этот фильтр не работает:
в принципе это логично, ведь мы фильтруем вот так
let filteredData = data.filter(item => item['Насколько курс был полезен?'] == selectPoleznost.value)
когда мы выбрали значение не важно
, он начинает оставлять те записи у которых в отзыве по ключу item['Насколько курс был полезен?']
стоит значение не важно
. А таких нет…
Значит надо просто значения не важно обрабатывать отдельно. С точки зрения логического выражения это можно записать так:
let filteredData = data.filter(item => {
// чтобы не получалось сильно длинных строчек, я перепишу анонимную функцию через return
return selectPoleznost.value == 'не важно' || item['Насколько курс был полезен?'] == selectPoleznost.value
})
супер =)
вот такой итоговый код получается
Теперь можно и задание поделать =О
Реализовать возможность фильтровать по оставшимся выпадающим спискам.
Причем фильтры по спискам должны объединятся.
Как-то так должно работать: