RPeraltaJr
1/5/2020 - 12:39 AM

Search Results with Pagination

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Vue | Search Jobs</title>
    <!-- Compiled and minified CSS -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
</head>
<body>
    <div id="root">

        <div class="container my-4">
            <div class="row">
                <div class="col">
                    <input v-model="keyword" class="form-control" placeholder="Search by id or title">
                </div>
                <div class="col">
                    <select v-model="category" class="form-control">
                        <option value="">Search by category</option>
                        <option v-for="cat in categories" :value="cat">{{ cat }}</option>
                    </select>
                </div>
                <div class="col">
                    <select v-model="location" class="form-control">
                        <option value="">Search by location</option>
                        <option v-for="loc in locations" :value="loc">{{ loc }}</option>
                    </select>
                </div>
            </div>
        </div>
        <div class="container">
            <div class="row">
                <div class="col">

                    <!-- Count -->
                    <p>
                        Showing 
                        {{ filteredJobs.length == 0 ? '0' : (page * perPage) - perPage + 1 }}
                        to
                        {{ pages.length > 1 && (page * perPage) < filteredJobs.length ? (page * perPage) : filteredJobs.length }}
                        of {{ filteredJobs.length }} results
                        {{ jobs.length != filteredJobs.length ? '(filtered from ' + jobs.length + ' total entries)'  : '' }}
                    </p>

                    <!-- Table -->
                    <table class="table">
                        <tr>
                            <th>ID</th>
                            <th>Title</th>
                            <th>Category</th>
                            <th>Location</th>
                        </tr>
                        <!-- job in filteredJobs -->
                        <tr v-for="job in filteredByAll">
                            <td>
                                <div>{{ job.id }}</div>
                            </td>
                            <td>
                                <div>{{ job.title }}</div>
                            </td>
                            <td>
                                <div>{{ job.category }}</div>
                            </td>
                            <td>
                                <div>{{ job.location }}</div>
                            </td>
                        </tr>
                    </table>

                    <!-- Pagination -->
                    <nav aria-label="Pagination">
                        <ul class="pagination">
                            <!-- <li class="page-item">
                                <button class="page-link" aria-label="Previous" @click="page--" v-if="page != 1">
                                    <span aria-hidden="true">&laquo;</span>
                                    <span class="sr-only">Previous</span>
                                </button>
                            </li> -->

                            <!-- pages.slice(page-1, page+3) if encountering a large amount of pages -->
                            <li :class="{ 'page-item': true, 'active': pageNumber == page}" v-for="pageNumber in pages">
                                <button type="button" class="page-link" @click="page = pageNumber"> {{ pageNumber }} </button>
                            </li>

                            <!-- <li class="page-item">
                                <button class="page-link" aria-label="Next" @click="page++" v-if="page < pages.length">
                                    <span aria-hidden="true">&raquo;</span>
                                    <span class="sr-only">Next</span>
                                </button>
                            </li> -->
                        </ul>
                    </nav>

                </div>
            </div>
        </div>
    </div>

    <!-- Vue -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js"></script>
    <script src="assets/js/main.js"></script>
</body>
</html>
const vm = new Vue({
    el: '#root',
    data: {       
        jobs: [],
        categories: [],
        locations: [],
        keyword: '',
        category: '',
        location: '',

        page: 1,
        perPage: 10,
        pages: [],
        filteredJobs: [], // needed to create updated pagination when filters are active
    },
    methods: {
      setPages() {
        let numberOfPages = Math.ceil(this.filteredJobs.length / this.perPage);
        this.pages = []; // set array to empty to recalculate
        for(let index = 1; index <= numberOfPages; index++) {
          this.pages.push(index);
        }
      },
      paginate(jobs) {
        let page = this.page;
        let perPage = this.perPage;
        let from = (page * perPage) - perPage;
        let to = (page * perPage);
        return jobs.slice(from, to);
      }
    },
    computed: {
    	filteredByAll() {
        this.filteredJobs = getByLocation(getByCategory(getByKeyword(this.jobs, this.keyword), this.category), this.location);
        return this.paginate(this.filteredJobs)
      },
    },
    watch: {
      filteredByAll() {
        this.setPages();
      }
    },
    created() {
      fetch('https://my-json-server.typicode.com/RPeraltaJr/jobs-api/results')
        .then((response) => response.json())
        .then(data => {
          // add jobs to array
          this.jobs = data;

          // add job categories to array
          this.jobs.map((job) => {
            // add category if it doesn't exist in array
            if(this.categories.indexOf(job.category) === -1) {
              this.categories.push(job.category);
            }
          });

          // add job locations to array
          this.jobs.map((job) => {
            // add location if it doesn't exist in array
            if(this.locations.indexOf(job.location) === -1) {
              this.locations.push(job.location);
            }
          });
        });
    }
});

function getByKeyword(list, keyword) {
  const search = keyword.trim().toLowerCase()
  if (!search.length) return list
  let hasNumber = /\d/;
  return list.filter(item => item.title.toLowerCase().indexOf(search) > -1 || item.id == search)
}

function getByCategory(list, category) {
  if (!category) return list
  return list.filter(item => item.category === category)
}

function getByLocation(list, location) {
  if (!location) return list
  return list.filter(item => item.location === location)
}