Vue components all acting as one - vue.js

I have three components. All of them bind a value.
Below is the blade file with the three components
Blade File:
<div id="app">
<div class="row">
<div class="col-4">
<crud-component :type="'tower'"></crud-component>
</div>
<div class="col-4">
<crud-component :type="'country'"></crud-component>
</div>
<div class="col-4">
<crud-component :type="'department'"></crud-component>
</div>
</div>
</div>
All of these components are dynamic, they only rely on that binded value called :type
The Vue file:
<template>
<div>
<h3>{{capitalize(type)}} <button class="btn btn-primary btn-sm" #click="showAddModal()">Add</button></h3>
<datatable :columns="columns" :sortKey="sortKey" :sortOrders="sortOrders">
<tbody v-if="loading">
<tr>
<td colspan="11"><div class="lds-dual-ring mx-auto"></div></td>
</tr>
</tbody>
<tbody v-else>
<tr v-for="data in retData" :key="data.id">
<td> {{data.id}}</td>
<td> {{data.name}}</td>
<td>
<button class="btn btn-warning btn-sm" #click="showEditModal(data)"> Edit </button>
<button class="btn btn-danger btn-sm" #click="showDeleteModal(data)"> Delete </button>
</td>
</tr>
</tbody>
</datatable>
<!-- MODALS -->
<modal :name="type + '-add-modal'" height="auto">
<div class="row p-3">
<div class="col-12">
<h3>Add {{capitalize(type)}}</h3>
<hr>
<form>
<div class="form-group">
<label for="add-item">{{capitalize(type)}} Name</label>
<input v-validate="'required'" type="text" name="item-name" class="form-control" id="add-item-name" required>
<small class="form-text text-danger">{{errors.first('item-name')}}</small>
</div>
<button type="button" class="float-right btn btn-sm btn-primary" #click="store">Submit</button>
</form>
</div>
</div>
</modal>
<modal :name="type + '-edit-modal'" height="auto">
<div class="row p-3">
<div class="col-12">
<h3>Edit {{capitalize(type)}}</h3>
<hr>
<form>
<div class="form-group">
<label for="edit-item">{{capitalize(type)}} Name</label>
<input v-validate="'required'" name="item-name" type="text" class="form-control" id="edit-item-name" v-model="currentItem.name" required>
<small class="form-text text-danger">{{errors.first('item-name')}}</small>
</div>
<button type="button" class="float-right btn btn-sm btn-primary" #click="store">Submit</button>
</form>
</div>
</div>
</modal>
<modal :name="type + '-delete-modal'" height="auto">
<div class="row p-3">
<div class="col-12">
<h3>Delete {{capitalize(type)}}</h3>
<hr>
<p class="lead">Are you sure you want to delete <strong>{{currentItem.name}}</strong>?</p>
<button type="button" class="float-right btn btn-sm btn-primary" #click="destroy">Submit</button>
</div>
</div>
</modal>
</div>
</template>
<script>
import Datatable from './utilities/DatatableComponent.vue';
import Pagination from './utilities/PaginationComponent.vue';
export default {
components: {
'datatable': Datatable,
'pagination': Pagination,
},
props: ['type'],
mounted() {
this.get(this.type);
},
data() {
let sortOrders = {};
let columns = [
{label: 'ID', name:'id', isSortable: true},
{label: 'NAME', name:'name', isSortable: true},
{label: 'ACTIONS', name:'actions', isSortable: false}
];
columns.forEach(column => {
sortOrders[column.name] = -1;
});
return {
loading: true,
currentItem: '',
isUpdate: false,
sortKey: 'id',
columns: columns,
sortOrders: sortOrders,
tableData: {
draw: 0,
length: 10,
search: '',
column: 'id',
dir: 'desc',
},
pagination: {
lastPage: '',
currentPage: '',
total: '',
lastPageUrl: '',
prevPageUrl: '',
nextPageUrl: '',
from: '',
to: '',
},
retData: []
};
},
methods: {
showEditModal(item) {
this.currentItem = item;
this.$modal.show(this.type + '-edit-modal');
this.isUpdate = true;
},
showAddModal() {
this.$modal.show(this.type + '-add-modal');
},
store() {
this.$validator.validate().then(valid => {
if(!valid) {
return;
} else {
if(this.isUpdate == true) {
axios.post('/api/crud/store', {
type: this.type,
item: this.currentItem,
isUpdate: this.isUpdate
}).then(response => {
this.get(this.type);
this.$modal.hide(this.type + '-edit-modal');
this.isUpdate = false;
this.currentItem = '';
});
} else {
axios.post('/api/crud/store', {
type: this.type,
name: $('#add-item-name').val(),
isUpdate: this.isUpdate
}).then(response => {
this.get(this.type);
this.$modal.hide(this.type + '-add-modal');
});
}
}
});
},
showDeleteModal(item) {
this.currentItem = item;
this.$modal.show(this.type + '-delete-modal');
},
destroy() {
axios.delete('/api/crud/delete', {
data: {
type: this.type,
item: this.currentItem
}
}).then(response => {
this.$modal.hide(this.type + '-delete-modal')
this.currentItem = '';
this.get(this.type)
});
},
capitalize(s) {
if (typeof s !== 'string') return ''
return s.charAt(0).toUpperCase() + s.slice(1)
},
get(type) {
axios.interceptors.request.use(config => {
NProgress.start();
this.loading = true;
return config;
});
axios.interceptors.response.use(response => {
NProgress.done();
this.loading = false;
return response;
});
this.tableData.draw++;
axios.post('/api/crud', {
type: type,
tableData: this.tableData
}).then(response => {
let data = response.data;
if(this.tableData.draw == data.draw) {
this.retData = data.data.data
}
console.log('Done');
});
},
}
}
</script>
<style>
.lds-dual-ring {
display: block;
width: 64px;
height: 64px;
}
.lds-dual-ring:after {
content: " ";
display: block;
width: 46px;
height: 46px;
margin: 5px;
border-radius: 50%;
border: 5px solid #000;
border-color: #000 transparent #000 transparent;
animation: lds-dual-ring 1.2s linear infinite;
}
#keyframes lds-dual-ring {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>
The problem is, when I'm trying to update, fetch, or do anything with one component.
It runs all of the API calls to fetch their specific data.
I just want to have that one table load when it's fetching data.

As mentioned in my comment above, you are adding interceptors to your Axios instance every time any of your components calls its get method.
The interceptor code runs on every request / response made through Axios, no matter where it comes from. This is why all your components appear to be loading when a request is made.
I suggest you remove the interceptors and change your code to
get(type) {
NProgress.start()
this.loading = true
this.tableData.draw++
axios.post('/api/crud', {
type: type,
tableData: this.tableData
}).then(response => {
NProgress.done()
this.loading = false
let data = response.data;
if(this.tableData.draw == data.draw) {
this.retData = data.data.data
}
})
}

Data needs to be a function.
data () {
return {
...
}
}
You can't simply have a bunch of variables and also a function like you have. You need to refactor your component.

Related

Vue is not update DOM when object property changes in array of objects

I have a const array of objects which set to the data property on created(), I am trying to update the object properties as the user entered the form, the Console print shows the value updated, but the DOM is changing. DOM is updated when the key value is changed too but I do not want to change the key value
Data Array:
const invoices = [
{
number: "BBRFL",
client: "Ellison Daugherty",
amount: 8800,
status: "Paid",
},
{
number: "TYUBK",
client: "Myrna Vinson",
amount: 4097,
status: "Paid",
},
{
number: "IRUBR",
client: "Mcmillan Warner",
amount: 6466,
status: "Draft",
},
];
Here is the app.
const app = new Vue({
el: "#app",
components: {
"create-invoice": CreateInvoice,
"invoice-item": InvoiceItem,
},
data() {
return {
invoices: invoices,
status: null,
};
},
created: function () {
const invoices = localStorage.getItem("invoices");
if (invoices) {
this.invoices = JSON.parse(invoices);
}
},
computed: {
count: function () {
return this.filteredInvoices.length;
},
filteredInvoices: function () {
if (this.status) {
return this.invoices.filter((invoice) => invoice.status == this.status);
}
return this.invoices;
},
},
methods: {
saveInvoice(invoice) {
this.invoices.push(invoice);
},
invoiceUpdate(invoice) {
const index = this.invoices.findIndex((i) => i.number === invoice.number);
// this.invoices.splice(index, 1, invoice);
Vue.set(this.invoices[index], "client", invoice.client);
Vue.set(this.invoices[index], "amount", invoice.amount);
Vue.set(this.invoices[index], "status", invoice.status);
},
invoiceDelete(number) {
const index = this.invoices.findIndex((i) => i.number === number);
this.invoices.splice(index, 1);
},
},
watch: {
invoices: {
deep: true,
handler: function (invoices) {
localStorage.setItem("invoices", JSON.stringify(invoices));
},
},
},
template: `
<div>
<div class="row">
<div class="col-md-4 text-left">
<h1>Invoices</h1>
<p class="text-dark">There are {{count}} Total Invoices</p>
</div>
<div class="col-md-4 text-start" style="margin-top: 10px">
<label for="status">Filter</label>
<select name="filter" id="status" class="form-control" v-model="status">
<option value="Draft">Draft</option>
<option value="Paid">Paid</option>
<option value="Pending">Pending</option>
</select>
</div>
<div class="col-md-4 text-right" style="margin-top: 10px">
<button class="btn btn-primary mt-4" id="createInvoice">
New Invoice
</button>
</div>
</div>
<div class="col-md-12">
<div class="row mt-2 mb-2">
<div
class="invoice col-md-12"
>
<invoice-item
v-for="(n, index) in filteredInvoices"
:key="n.number"
:initial-client="n.client"
:initial-number="n.number"
:initial-amount="n.amount"
:initial-status="n.status"
#update-invoice="invoiceUpdate"
#delete-invoice="invoiceDelete"
></invoice-item>
</div>
<div class="text-center" v-if="filteredInvoices.length === 0"><p>No invoice found</p></div>
</div>
</div>
<create-invoice #saveInvoice="saveInvoice" ></create-invoice>
</div>
</div>
`,
});
I had tried, this.$set, Vue.set, Direct assigning to property, assingment using splice function, but none of working. It only works with the change of value of key in for loop. Which I do not want to update.
Jsfiddle
Any Help? Thanks in advance.

VUEX Filtered computed property does not update at state change

I am using vuex , and with getters Iam filtering a array of data in the store.
In a parent component I am fetching the array and send it to a child with props.
The child component resieve filtered array with getters and save it in computed property.
But when I make changes by calling actions, store is updated but filtered array stayed the same.
When I send to the child component original unfiltered array it's okey.
It the vue dev tool I see correct updated getters.
Some of the code is below.
STORE
const getDefaultState = () => {
return {
activities: [],
error: null,
isActivitiesLoading: false,
isActivityUpdating: false,
}
}
const mutations = {
[FETCHING_ACTIVITIES](state) {
state.isActivitiesLoading = true;
state.error = null;
},
[FETCHING_ACTIVITIES_SUCCESS](state, activities) {
state.error = null;
state.isActivitiesLoading = false;
state.activities = activities
},
[FETCHING_ACTIVITIES_ERROR](state, error) {
state.error = error;
state.isActivitiesLoading = false
},
[UPDATING_ACTIVITY](state) {
state.isActivityUpdating = true;
state.error = null;
},
[UPDATING_ACTIVITY_SUCCESS](state, activity) {
state.error = null;
state.isActivityUpdating = false;
const index = state.activities.findIndex(a => a.id === activity.id)
state.activities[index] = activity;
},
[UPDATING_ACTIVITY_ERROR](state, error) {
state.error = error;
state.isActivityUpdating = false
},
}
const actions = {
async fetchActivities({ commit }) {
commit(FETCHING_ACTIVITIES);
try {
const response = await ActivitiesApi.fetchActivities();
const activities = response.data.data;
commit(FETCHING_ACTIVITIES_SUCCESS, activities);
return response.data.data;
} catch (error) {
commit(FETCHING_ACTIVITIES_ERROR, error);
return null;
}
},
async updateActivity({ commit }, payload) {
commit(UPDATING_ACTIVITY);
try {
const response = await ActivitiesApi.updateActivity(payload);
const activity = response.data.data;
commit(UPDATING_ACTIVITY_SUCCESS, activity);
return response.data.data;
} catch (error) {
commit(UPDATING_ACTIVITY_ERROR, error);
return null;
}
},
};
const getters = {
getActivities(state) {
return state.activities;
},
getRunningActivities(state) {
let today = new Date();
const activities = state.activities;
const filteredActivities = activities.filter(function(activity) {
let activityDate = new Date(activity.start_date)
return activityDate <= today
});
return filteredActivities;
},
};
export default {
namespaced: true,
state: getDefaultState(),
getters,
actions,
mutations,
}
PARENT COMPONENT
<template>
<div class="container">
<h3>Running Activities</h3>
<ActivitiesComponent
:initialActivitiesFromStore="runningActivities"
/>
</div>
</template>
import ActivitiesComponent from "../components/Activities";
export default {
components: {
ActivitiesComponent
},
mounted() {
this.$store.dispatch('activities/fetchActivities').then(
() => {
if (this.hasError) {
console.log(this.error)
} else {
}
}
);
},
computed: {
activitiesFromStore() {
return this.$store.getters['activities/getActivities'];
},
runningActivities() {
return this.$store.getters['activities/getRunningActivities']
},
},
}
</script>
CHILD COMPONENT
<template>
<div class="container">
<div v-if="isActivitiesLoading" class="spinner-border spinner"></div>
<div class="row">
<div class="col">
<table class="table">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Activities</th>
<th scope="col">Period</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
<tr v-for="(activity, activityId) in $v.activities.$each.$iter" :key="activityId">
<th scope="row">{{ parseInt(activityId) + 1 }}</th>
<td>
<input type="text" class="form-control" v-model="activity.name.$model">
<div class="alert alert-danger" v-if="!activity.name.required">Print Name</div>
<div v-if="activitiesFromStore[activityId].is_paused" class="alert alert-warning">
Activity is paused
</div>
</td>
<td>
<input type="text" class="form-control" v-model="activity.activity_period.$model">
<div class="alert alert-danger" v-if="!activity.activity_period.required">Print period</div>
<div class="alert alert-danger" v-if="!activity.activity_period.integer || !activity.activity_period.minValue">Period > 0</div>
</td>
<td class="d-flex border-0">
<button #click="activity.$model.is_paused = ! activity.$model.is_paused" class="btn btn-light mr-1" v-bind:class="{ active: !activity.$model.is_paused }">
<span v-if="activity.$model.is_paused">Убрать с паузы</span>
<span v-else>Make pause</span>
</button>
<button #click="updateActivity(activity.$model)" :disabled="
isActivityUpdating || !activitiesChanged(activityId) || !activity.name.required || !activity.activity_period.required || !activity.activity_period.integer || !activity.activity_period.minValue
" type="button" class="btn btn-success mr-1">
<span v-if="isActivityUpdating && activityActed.id == activity.$model.id" class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
Change
</button>
<button #click="deleteActivity(activity.$model)" type="button" class="btn btn-danger">
<span v-if="isActivityDeleting && activityActed.id == activity.$model.id" class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
Delete
</button>
</td>
</tr>
</tbody>
</table>
<div class="collapse" id="collapseExample">
<div class="form-group row">
<div class="col-4">
<label for="newPassword-input">Name</label>
<input v-model="activityToAdd.name" class="form-control">
<div v-if="$v.activityToAdd.period.$dirty && !$v.activityToAdd.name.required" class="alert alert-danger">Print name</div>
</div>
<div class="col-4">
<label for="newPassword-input">Period</label>
<input v-model="activityToAdd.period" class="form-control">
<div class="alert alert-danger" v-if="$v.activityToAdd.period.$dirty && !$v.activityToAdd.period.required">Print period</div>
<div class="alert alert-danger" v-if="(!$v.activityToAdd.period.integer || !$v.activityToAdd.period.minValue)">period > 0</div>
</div>
</div>
<button #click="addActivity" :disabled="!$v.activityToAdd.name.required || !$v.activityToAdd.period.required || !$v.activityToAdd.period.integer || !$v.activityToAdd.period.minValue" type="button" class="btn btn-primary">
<span v-if="isActivityAdding" class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
add
</button>
</div>
</div>
</div>
</div>
</template>
<script>
import {required, minValue, integer} from "vuelidate/lib/validators"
export default {
props: ['initialActivitiesFromStore'],
data() {
return {
activityActed: null,
justEdited: false,
justAdded: false,
justDeleted: false,
activityToAdd:{
name: '',
period: '',
isPaused: ''
}
}
},
computed: {
activitiesFromStore() {
return this.initialActivitiesFromStore
},
activities() {
return JSON.parse(JSON.stringify(this.initialActivitiesFromStore));
},
},
methods: {
activitiesChanged(id) {
if(this.activitiesFromStore[id] && this.activities[id].name == this.activitiesFromStore[id].name && this.activities[id].activity_period == this.activitiesFromStore[id].activity_period && this.activities[id].is_paused == this.activitiesFromStore[id].is_paused)
return false;
else
return true
},
updateActivity(activity){
this.activityActed = activity;
this.$store.dispatch('activities/updateActivity', activity).then(
() => {
if (this.hasError) {
console.log(this.error)
} else {
this.justEdited = true;
// still the same
console.log(this.$store.getters['activities/getRunningActivities']);
}
}
);
},
},
validations: {
activities: {
$each: {
name: {
required,
},
activity_period: {
required,
integer,
minValue: minValue(0)
},
is_paused: {
required,
},
}
},
}
}
</script>
The problem was that I did not follow the vue specification about modification of an array. I used vm.items[indexOfItem] = newValue which is not reactive.

How to make the fields independent?

I'm trying to make the fields/buttons independent of one another. Just like in the case of Adding more people. Example image
No matter how many fields I add, they'll not be linked to each other. I can remove/edit one of them and it'll only affect that field.
but I'm not sure how can I achieve similar behavior If I need to repeat same fields. If I click on Add Other City, a new city block would be created but with the previous data. Example image.
HTML:
<div id="app">
<div v-model="cities" class="city">
<form action="" v-for="(city, index) in cities">
<div class="column" v-for="(profile, index) in profiles">
<input type='text' v-model="profile.name" placeholder="Name">
<input type='text' v-model="profile.address" placeholder="Address">
<button type="button" #click="removeProfile">-</button>
</div>
<center><button type="button" #click="addProfile">Add More People</button></center>
<br><br><br>
</form>
<center>
<button type="button" #click="addCity">Add Other City</button>
<button type="button" #click="removeCity">Remove City</button>
</center>
</div>
</div>
JS:
new Vue({
el: '#app',
data: {
cities: [
{ name: '' }
],
profiles: [
{ name: '', address: '' }
],
},
methods: {
addProfile() {
this.profiles.push({
name: '',
address: ''
})
},
removeProfile(index) {
this.profiles.splice(index, 1);
},
addCity() {
this.cities.push({
// WHAT TO DO HERE?
})
},
removeCity(index) {
this.cities.splice(index, 1);
},
}
})
Here's a link to Jsfiddle: https://jsfiddle.net/91m8cf5q/4/
I first tried to do this.profiles.push({'name': '', 'address': ''}) inside this.cities.push({}) in addCity() but It's not possible (gives error).
What should I do to make them separate so that when I click Add Other City, new blank field would appear and removing fields from that city should not remove fields from the previous cities.
You should have profiles assigned to individual city
new Vue({
el: '#app',
data: {
cities: [],
},
mounted(){
this.cities.push({
name: '',
profiles: [
{
name: '',
address: '' }
]
});
},
methods: {
addProfile(index) {
this.cities[index].profiles.push({
name: '',
address: ''
})
},
removeProfile(index) {
this.profiles.splice(index, 1);
},
addCity() {
this.cities.push({
name: '',
profiles: [
{
name: '',
address: '' }
]
})
},
removeCity(index) {
this.cities.splice(index, 1);
},
}
})
#app form {
margin: 5px;
width: 260px;
border: 1px solid green;
padding-top: 20px;
}
.column {
padding: 5px;
}
.city {
width: 280px;
border: 1px solid red;
margin: 10px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div class="city">
<form action="" v-for="(city, index) in cities">
<div class="column" v-for="(profile, index) in city.profiles">
<input type='text' v-model="profile.name" placeholder="Name">
<input type='text' v-model="profile.address" placeholder="Address">
<button type="button" #click="removeProfile">-</button>
</div>
<center><button type="button" #click="addProfile(index)">Add More People</button></center>
<br><br><br>
</form>
<center>
<button type="button" #click="addCity">Add Other City</button>
<button type="button" #click="removeCity">Remove City</button>
</center>
</div>
</div>
Profiles should be part of cities, like this
new Vue({
el: '#app',
data: {
cities: [{
name: '',
profiles: [
{ name: '', address: '' }
]}
]
},
methods: {
addProfile(city) {
city.profiles.push({
name: '',
address: ''
})
},
addCity() {
this.cities.push(
{ name: '',
profiles: [
{ name: '', address: '' }
]}
)
},
removeCity(index) {
this.cities.splice(index, 1);
},
}
})
HTML:
<div id="app">
<div class="city">
<form v-for="city in cities">
<div class="column" v-for="profile in city.profiles">
<input type='text' v-model="profile.name" placeholder="Name">
<input type='text' v-model="profile.address" placeholder="Address">
<button type="button" #click="removeProfile">-</button>
</div>
<center>
<button type="button" #click="addProfile(city)">Add More People</button>
</center>
<br><br><br>
</form>
<center>
<button type="button" #click="addCity">Add Other City</button>
<button type="button" #click="removeCity">Remove City</button>
</center>
</div>
<div>

Multi selected Filter in Vue.js

I have a selection of checkbox groups (hotel type and locations) and I want to filter the results based on what has been selected. How can I add my selectedLocations and types to the filteredHotels() method and get a filtered result. Sydney Hotels, Sydney Backpackers, Sydney or Melbourne hotels or all Hotels if only hotels is selected.
HTML
<div>
<div class="row pt-5">
<div class="col">
<h5>Locations</h5>
<label v-for="(value, key) in locations">
{{value}}
<input type="checkbox" :value="value" v-model="selectedLocations">
</label>
{{selectedLocations}}
</div>
</div>
<div class="row pt-5">
<div class="col">
<h5>Type</h5>
<label v-for="(value, key) in types">
{{value}}
<input type="checkbox" :value="value" v-model="selectedTypes">
</label>
{{selectedTypes}}
</div>
</div>
<transition-group class="row" style="margin-top:30px;" name="list" tag="div" mode="out-in">
<div class="col-sm-4 pb-3 list-item" v-for="(hotel, index) in filteredHotels" :key="index">
<div class="sau-card">
<i class="fal fa-server fa-3x"></i>
<h2>{{hotel.name}}</h2>
<p>{{hotel.type}}</p>
<p>{{hotel.location}}</p>
</div>
</div>
</transition-group>
</div>
Data
data() {
return {
locations:['Sydney','Melbourne'],
types:['backpackers','hotel','resort'],
selectedLocations:[],
selectedTypes:[],
hotels:[
{name:'a Hotel',location:'Sydney', type:'backpackers'},
{name:'b Hotel',location:'Sydney', type:'hotel'},
{name:'c Hotel',location:'Sydney', type:'resort'},
{name:'d Hotel',location:'Melbourne',type:'hotel'},
{name:'e Hotel',location:'Melbourne', type:'resort'},
{name:'f Hotel',location:'Melbourne', type:'hotel'},
]
}
},
Computed
computed:{
filteredHotels(){
if(this.selectedLocations.length){
return this.hotels.filter(j => this.selectedLocations.includes(j.location))
}
else if(this.selectedTypes.length){
return this.hotels.filter(j => this.selectedTypes.includes(j.type))
}
else{
return this.hotels;
}
}
}
Fiddle
Pass your data via binding this or using ()=>, e.g:
filteredHotels(){
return this.hotels.filter(function (el) {
return this.selectedLocations.includes(el.location)
|| this.selectedTypes.includes(el.type)
}.bind(this));
}
Fiddle
Here is a working example:
I've updated the condition under computed property as below: Include only location which is being selected. Instead of just returning the current selected location.
computed:{
filteredHotels(){
console.log(this.selectedLocations);
if(!this.selectedLocations.length) {
return this.hotels;
} else {
return this.hotels.filter(j => this.selectedLocations.includes(j.location))
}
}
}
new Vue({
el: "#app",
data: {
locations: ['Sydney', 'Melbourne'],
types: ['backpackers', 'hotel', 'resort'],
selectedLocations: [],
selectedTypes: [],
filtersAppied: [],
hotels: [{
name: 'a Hotel',
location: 'Sydney',
type: 'backpackers'
},
{
name: 'b Hotel',
location: 'Sydney',
type: 'hotel'
},
{
name: 'c Hotel',
location: 'Sydney',
type: 'resort'
},
{
name: 'd Hotel',
location: 'Melbourne',
type: 'hotel'
},
{
name: 'e Hotel',
location: 'Melbourne',
type: 'resort'
},
{
name: 'f Hotel',
location: 'Melbourne',
type: 'hotel'
},
]
},
methods: {
setActive(element) {
if (this.filtersAppied.indexOf(element) > -1) {
this.filtersAppied.pop(element)
} else {
this.filtersAppied.push(element)
}
},
isActive: function (menuItem) {
return this.filtersAppied.indexOf(menuItem) > -1
},
},
computed: {
filterApplied: function() {
if (this.filtersAppied.indexOf(element) > -1) {
this.filtersAppied.pop(element)
} else {
this.filtersAppied.push(element)
}
},
filteredHotels: function() {
return this.hotels.filter(hotel => {
return this.filtersAppied.every(applyFilter => {
if (hotel.location.includes(applyFilter)) {
return hotel.location.includes(applyFilter);
}
if (hotel.type.includes(applyFilter)) {
return hotel.type.includes(applyFilter);
}
});
});
}
}
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
li {
margin: 8px 0;
}
h2 {
font-weight: bold;
margin-bottom: 15px;
}
del {
color: rgba(0, 0, 0, 0.3);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div>
<div class="row pt-5">
<div class="col">
{{filteredHotels}}
<h5>Locations</h5>
<label v-for="(value, key) in locations">
{{value}}
<input type="checkbox" :value="value" v-model="selectedLocations" :checked="isActive(value)" #click="setActive(value)">
</label> {{selectedLocations}}
</div>
</div>
<div class="row pt-5">
<div class="col">
<h5>Type</h5>
<label v-for="(value, key) in types">
{{value}}
<input type="checkbox" :value="value" v-model="selectedTypes"
:checked="isActive(value)"#click="setActive(value)">
</label> {{selectedTypes}}
</div>
</div>
<div class="row">
<div class="col-sm-3 pb-4" v-for="hotel in filteredHotels">
<div class="card text-center">
<h1>{{hotel.name}}</h1>
<p>{{hotel.location}}</p>
</div>
</div>
</div>
</div>
</div>
Hope this helps!

Pagination. How to make moving between pages by clicking on numerals

Tell me how to make it so that when you click on a button from a cycle with page numbers, this particular page opens. Switching along the arrows works for me, but I cannot understand how to switch between pages. I take data from Api. Total posts 98. It is possible to add your posts. On one page only 10 posts are shown.
My html:
<div id="app">
<div class="smallfon">
<div class="blocktwitter"><img src="src/assets/twitter.png" class="twitter"/></div>
<div class="addTextPost">Add a post</div>
<input type="text" v-model="createTitle" class="created"/>
<input type="text" v-model="createBody" class="created"/>
<div><button #click="addPost()" class="addPost">AddPost</button></div>
<div class="post1">
<div class="yourPosts">Your Posts</div>
<ul>
<li v-for="(post, index) of paginatedData" class="post">
<p><span class="boldText">Title:</span> {{ post.title }}</p>
<p><span class="boldText">Content:</span> {{ post.body }}</p>
<button #click="deleteData(index, post.id)" class="buttonDelete">Delete</button>
<button #click="visiblePostID = post.id" class="buttonChange">Change</button>
<div v-if="visiblePostID === post.id" class="modalWindow">
<div><input v-model="post.title" class="changePost"><input v-model="post.body" class="changePost"></div>
<button type="button" #click="changePost(post.id, post.title, post.body)" class="apply">To apply</button>
</div>
</li>
</ul>
<button type="button" #click="page -=1" v-if="page > 0" class="prev"><<</button>
<button class="item"
v-for="n in evenPosts"
:key="n.id"
v-bind:class="{'selected': current === n.id}">{{ n }} </button>
<button type="button" #click="page +=1" class="next" v-if="page < evenPosts-1">>></button>
</div>
</div>
</div>
My js:
export default {
el: "#app",
data () {
return {
current: null,
page: 0,
posts: [],
createTitle: '',
createBody: '',
visiblePostID: '',
}
},
watch: {
counter: function(newValue, oldValue) {
this.getData()
}
},
created(){
this.getData()
},
computed: {
evenPosts: function(posts){
return Math.ceil(this.posts.length/10);
},
paginatedData() {
const start = this.page * 10;
const end = start + 10;
return this.posts.slice(start, end);
}
},
methods: {
setCurrent: function(id) {
this.current = id;
},
getData() {
axios.get(`https://jsonplaceholder.typicode.com/posts`).then(response => {
this.posts = response.data
})
},
deleteData(index, id) {
axios.delete('http://jsonplaceholder.typicode.com/posts/' + id)
.then(response => {
console.log('delete')
this.posts.splice(index, 1);
})
.catch(function(error) {
console.log(error)
})
},
addPost() {
axios.post('http://jsonplaceholder.typicode.com/posts/', {
title: this.createTitle,
body: this.createBody
}).then((response) => {
this.posts.unshift(response.data)
})
},
changePost(id, title, body) {
axios.put('http://jsonplaceholder.typicode.com/posts/' + id, {
title: title,
body: body
})
},
}
}
Screenshot of application
add click event #click="page=n" in button
<button #click="page=n" class="item"
v-for="n in evenPosts"
:key="n.id"
v-bind:class="{'selected': current === n.id}">{{ n }} </button>
Codepen : https://codepen.io/anon/pen/bZOROO