K
K
KraKenRa12022-01-21 22:01:31
Vue.js
KraKenRa1, 2022-01-21 22:01:31

How to combine sorting and filtering?

I would like to understand how to do a search for a table to which a filter has already been applied. The list can be removed, because the search will still go through the table. I could not find such an answer on the Internet, or it is written in such a way that it will be understandable only to those who have been studying vue js for more than 1 week.

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
</head>

<body>
    <style>
        a:hover {
            cursor: pointer;
        }
        /* ul{
            display: none;
        } */
    </style>

    <div id="app">

        <p><input type="text" v-model="company" placeholder="Поиск..." /></p>

        <ul>
            <li v-for="phone in filteredList">
                <p>{{ phone.title }} - {{ phone.company }}</p>
            </li>
        </ul>

        <table>
            <tr>
                <td><a @click="sortParam='title'">Модель</a></td>
                <td><a @click="sortParam='company'">Компания</a></td>
                <td><a @click="sortParam='price'">Цена</a></td>
            </tr>
            <tr v-for="phone in sortedList">
                <td>{{phone.title}}</td>
                <td>{{phone.company}}</td>
                <td>{{phone.price}}</td>
            </tr>
        </table>

    </div>

    <script src="https://unpkg.com/[email protected]"></script>
    <script>
        Vue.createApp({
            data() {
                return {
                    company: '',
                    sortParam: "",
                    phones: [
                        { title: "iPhone 12", company: "Apple", price: 65000 },
                        { title: "Galaxy S20", company: "Samsung", price: 63000 },
                        { title: "Galaxy A10", company: "Samsung", price: 38000 },
                        { title: "iPhone 10", company: "Apple", price: 45000 },
                        { title: "Xiaomi Redmi 8", company: "Xiaomi", price: 42000 },
                    ],
                };
            },
            computed: {
                sortedList() {
                    switch (this.sortParam) {
                        case "title":
                            return this.phones.sort(sortByTitle);
                        case "company":
                            return this.phones.sort(sortByCompany);
                        case "price":
                            return this.phones.sort(sortByPrice);
                        default:
                            return this.phones;
                    }
                },
                filteredList() {
                    let comp = this.company;
                    return this.phones.filter(function (elem) {
                        if (comp === '') return true;
                        else return elem.company.indexOf(comp) > -1;
                    })
                },
            },
        }).mount("#app");
        const sortByTitle = (d1, d2) =>
            d1.title.toLowerCase() > d2.title.toLowerCase() ? 1 : -1;
        const sortByCompany = (d1, d2) =>
            d1.company.toLowerCase() > d2.company.toLowerCase() ? 1 : -1;
        const sortByPrice = (d1, d2) => (d1.price > d2.price ? 1 : -1);
    </script>

</body>

</html>

Answer the question

In order to leave comments, you need to log in

1 answer(s)
G
GrayHorse, 2022-01-23
@KraKenRa1

For fun.
Composition API [RU] + Script Setup [RU] .
Actually, demo: https://sfc.vuejs.org/#
Code:

<script setup>
  import {ref, computed, watchEffect} from "vue";

  const phones = ref([
    { title: "iPhone 12", company: "Apple", price: 65000 },
    { title: "Galaxy S20", company: "Samsung", price: 63000 },
    { title: "Galaxy A10", company: "Samsung", price: 38000 },
    { title: "iPhone 10", company: "Apple", price: 45000 },
    { title: "Xiaomi Redmi 8", company: "Xiaomi", price: 42000 },
  ]);


  const companySearch = ref("");
  const filteredList = computed(() => {
    if (!companySearch.value) {
      return phones.value;
    }
    const search = companySearch.value.trim().toLowerCase();
    return phones.value.filter(elem => elem.company.toLowerCase().startsWith(search)); // .includes(search)
  });


  const sortParam = ref("");
  
  const sortByTitle   = (p1, p2) => p1.title.localeCompare(p2.title, undefined, {sensitivity: "base"});
  const sortByCompany = (p1, p2) => p1.company.localeCompare(p2.company, undefined, {sensitivity: "base"});
  const sortByPrice   = (p1, p2) => p1.price - p2.price;

  const sortedList = ref([]);
  watchEffect(() => {
    const array = filteredList.value; //const array = [...filteredList.value];
    if (sortParam.value === "title") {
      sortedList.value = array.sort(sortByTitle);
    } else if (sortParam.value === "company") {
      sortedList.value = array.sort(sortByCompany);
    } else if (sortParam.value === "price") {
      sortedList.value = array.sort(sortByPrice);
    } else {
      sortedList.value = array;
    }
  });
</script>

Version with inverting sorting on the second click (link to demo in the comment):
<script setup>
  import {ref, computed} from "vue";

  const phones = ref([
    { title: "iPhone 12", company: "Apple", price: 65000 },
    { title: "Galaxy S20", company: "Samsung", price: 63000 },
    { title: "Galaxy A10", company: "Samsung", price: 38000 },
    { title: "iPhone 10", company: "Apple", price: 45000 },
    { title: "Xiaomi Redmi 8", company: "Xiaomi", price: 42000 },
  ]);


  const companySearch = ref("");
  const filteredList = computed(() => {
    if (!companySearch.value) {
      return phones.value;
    }
    const search = companySearch.value.trim().toLowerCase();
    return phones.value.filter(elem => elem.company.toLowerCase().startsWith(search)); // .includes(search)
  });

  /** @type {import("vue").Ref<("title"|"company"|"price")>} */
  const orderBy = ref("title");
  const orders = ref({ // if `true` — an order is reversed
    title: false,
    company: false,
    price: false,
  });
  const isOrderReversed = computed(() => orders.value[orderBy.value]);
  function toggleOrder() {
    orders.value[orderBy.value] = !orders.value[orderBy.value];
  }
  /** @param {"title"|"company"|"price"} value */
  function setOrderBy(value) {
    if (orderBy.value === value) {
      toggleOrder();
    }
    orderBy.value = value;
  };

  const {compare} = new Intl.Collator(undefined, {sensitivity: "base"});
  function comparator(pre, cur) {
    const k = isOrderReversed.value ? -1 : 1;    
    if (orderBy.value === "title") {
      return compare(pre.title, cur.title) * k;
    } else if (orderBy.value === "company") {
      return compare(pre.company, cur.company) * k;
    } else if (orderBy.value === "price") {
      return (pre.price - cur.price) * k;
    }
    return 0;
  }
  const sortedList = computed(() => { 
    return filteredList.value.sort(comparator);
  });
</script>

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question