<!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">«</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">»</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)
}