Cannot read property 'reduce' of undefined in vue js - laravel

I am having problems with vue.js when I want to get data to edit and I got the following error:
cannot read property 'reduce' of undefined"
But when I use console.log, the data are correct. I try more time to change this.$set(this.$data, "form", res.returnsale) of fetchRuturn on my code and then I got this error.
<template>
<v-app>
<v-card class="mx-5 my-5">
<div class="teal darken-1">
<v-card-title class="white--text">Edit​ Return Sale</v-card-title>
</div>
<v-divider></v-divider>
<div class="px-5">
<p class="caption font-italic pt-5">The field labels marked with * are required input fields.</p>
<v-row>
<v-col md="6" cols="12">
<label for="reference_no" class="font-weight-bold">Reference No</label>
<v-text-field solo outlined dense v-model="form.reference_no"></v-text-field>
</v-col>
<v-col md="6" cols="12">
<label class="font-weight-bold">Location*</label>
<v-autocomplete item-value="address" item-text="address" solo outlined dense label="Business Location"
return-object v-model="form.location" :items="locations"></v-autocomplete>
</v-col>
<v-col md="6" cols="12">
<label class="font-weight-bold">Supplier</label>
<v-autocomplete :items="suppliers" item-text="name" item-value="name" solo outlined dense return-object
v-model="form.supplier" label="Select Supplier"></v-autocomplete>
</v-col>
<v-col md="6" cols="12">
<label class="font-weight-bold">Account</label>
<v-autocomplete :items="accounts" item-text="name" item-value="name" solo outlined dense return-object
v-model="form.account" label="Select Account"></v-autocomplete>
</v-col>
<v-col cols="12">
<label class="font-weight-bold">Select Product</label>
<div>
<v-autocomplete dense solo item-text="name" item-value="name" return-object :items="products"
#input="addTocart"></v-autocomplete>
</div>
</v-col>
</v-row>
<div>
<label class="font-weight-bold mb-3">Product Table</label>
<table class="tableReturn">
<thead>
<tr class="tableReturn--header">
<td>Name</td>
<td>Code</td>
<td>Quantity</td>
<td>Unit Price</td>
<td>Discount</td>
<td>Total</td>
<td>Actions</td>
</tr>
</thead>
<tbody>
<tr class="tableReturn--td" v-for="(item, index) in form.items" :key="index">
<td>{{item.name}}</td>
<td>{{item.code}}</td>
<td>
<input type="number" class="table-quantity" v-model="form.items[index].quantity" />
</td>
<td>
<input type="number" class="table-quantity" v-model="form.items[index].unit_price"
placeholder="0.00" />
</td>
<td>
<input type="number" class="table-quantity" v-model="form.items[index].discount" placeholder="0.00" />
</td>
<td>USD {{ discountedPrice(item) | formatMoney }}</td>
<td>
<v-btn small color="red" outlined #click="removeItem(index)">
<v-icon>mdi-delete</v-icon>
</v-btn>
</td>
</tr>
<tr>
<td class="py-5" colspan="2">Total</td>
<td colspan="3">{{ Qty }}</td>
<td>USD {{ Total | formatMoney }}</td>
</tr>
</tbody>
</table>
</div>
<v-row>
<v-col md="6" cols="12">
<div class="d-flex flex-column mb-5">
<label for="" class="font-weight-bold">Return Note</label>
<textarea cols="30" rows="5" class="textarea" v-model="form.return_des"></textarea>
</div>
</v-col>
<v-col md="6" cols="12">
<div class="d-flex flex-column mb-5">
<label for="" class="font-weight-bold">Staff Note</label>
<textarea cols="30" rows="5" class="textarea" v-model="form.staff_des"></textarea>
</div>
</v-col>
</v-row>
</div>
<v-btn #click.prevent="updateReturn" class="blue mx-5 darken-2 mb-5 grey--text text--lighten-4">
<v-icon>mdi-check</v-icon>Update
</v-btn>
</v-card>
</v-app>
</template>
<script>
import Vue from "vue";
let numeral = require('numeral');
Vue.filter('formatMoney', function (value) {
return numeral(value).format('00,00.00')
});
export default {
name: "editReturn",
created() {
this.fetchReturn();
this.fetchLocation();
this.fetchSupplier();
this.fetchAccount();
this.fetchProduct();
},
data() {
return {
form: {
items: []
},
suppliers: [],
locations: [],
products: [],
returnsale: [],
accounts: []
};
},
computed: {
Qty() {
return this.form.items.reduce((total, item) => {
return total + Number(item.quantity);
}, 0);
},
Total() {
return this.form.items.reduce((total, item) => {
let subtotal = (item.unit_price - (item.unit_price * item.discount) / 100) *
item.quantity
return total + subtotal;
}, 0);
}
},
methods: {
discountedPrice(product) {
return (
(product.unit_price -
(product.unit_price * product.discount) / 100) *
product.quantity
);
},
fetchLocation() {
this.$axios.$get(`api/location`)
.then(res => {
// this.locations = res.locations.data;
this.$set(this.$data, "locations", res.locations.data);
console.log(res);
})
.catch(err => {
console.log(err.response);
});
},
fetchSupplier() {
this.$axios.$get(`api/supplier`)
.then(res => {
this.$set(this.$data, "suppliers", res.suppliers.data);
console.log(res);
})
.catch(err => {
console.log(err.response);
});
},
fetchAccount() {
this.$axios.$get(`api/account`)
.then(res => {
this.$set(this.$data, "accounts", res.accounts);
console.log(res)
})
.catch(err => {
console.log(err.response);
});
},
fetchProduct() {
this.$axios
.$get(`/api/product`)
.then(res => {
this.$set(this.$data, "products", res.products.data);
console.log(res);
})
.catch(err => {
console.log(err);
});
},
fetchReturn() {
this.$axios
.$get(`api/return-sale/` + this.$route.params.id)
.then(res => {
this.$set(this.$data, "form", res.returnsale);
console.log(res);
for (let i in this.form.items) {
Vue.set(this.form.items[i], 'quantity', this.form.items[i].pivot.quantity);
Vue.set(this.form.items[i], 'unit_price', this.form.items[i].pivot.unit_price);
Vue.set(this.form.items[i], 'discount', this.form.items[i].pivot.discount);
}
})
.catch(err => {
console.log(res.response);
});
},
updateReturn() {
this.$axios
.$patch(`api/return-sale/` + this.form.id, {
location: this.form.location,
products: this.form.products,
supplier: this.form.supplier,
account: this.form.account,
return_des: this.form.return_des,
staff_des: this.form.staff_des,
})
.then(res => {
this.returnsale = res.data;
// this.$set(this.$data, "returnsale", res.data);
this.$set(this.$data, "returnsale", res.returnsale);
this.$router.push(`/return/return-sale/view`);
console.log(res);
})
.catch(err => {
console.log(err.response);
});
},
addTocart(item) {
if (this.form.items.includes(item)) {
alert("already there");
} else {
this.form.items.push(item);
}
Vue.set(item, 'quantity', 1);
Vue.set(item, 'discount', 1);
},
removeItem(index) {
this.form.items.splice(index, 1);
}
}
};
</script>
<style lang="scss">
.textarea {
border: 1px solid rgba(0, 0, 0, 0.125);
outline: 1px solid #461577;
}
.tableReturn {
width: 100%;
margin-top: 10px;
border-collapse: collapse;
&--header {
font-weight: 500;
text-align: left;
border-bottom: 1px solid rgba(0, 0, 0, 0.125);
}
&--td {
border-bottom: 1px solid rgba(0, 0, 0, 0.125);
}
}
.table-quantity {
border: 1px solid rgba(0, 0, 0, 0.125);
padding: 5px 10px 5px 10px;
margin-top: 5px;
margin-bottom: 5px;
}
</style>
enter image description here

Try this
i have added one check if (this.form.hasOwnProperty("items"))
if form have value then only it will excute
<template>
<v-app>
<v-card class="mx-5 my-5">
<div class="teal darken-1">
<v-card-title class="white--text">Edit​ Return Sale</v-card-title>
</div>
<v-divider></v-divider>
<div class="px-5">
<p
class="caption font-italic pt-5"
>The field labels marked with * are required input fields.</p>
<v-row>
<v-col md="6" cols="12">
<label for="reference_no" class="font-weight-bold">Reference No</label>
<v-text-field solo outlined dense v-model="form.reference_no"></v-text-field>
</v-col>
<v-col md="6" cols="12">
<label class="font-weight-bold">Location*</label>
<v-autocomplete
item-value="address"
item-text="address"
solo
outlined
dense
label="Business Location"
return-object
v-model="form.location"
:items="locations"
></v-autocomplete>
</v-col>
<v-col md="6" cols="12">
<label class="font-weight-bold">Supplier</label>
<v-autocomplete
:items="suppliers"
item-text="name"
item-value="name"
solo
outlined
dense
return-object
v-model="form.supplier"
label="Select Supplier"
></v-autocomplete>
</v-col>
<v-col md="6" cols="12">
<label class="font-weight-bold">Account</label>
<v-autocomplete
:items="accounts"
item-text="name"
item-value="name"
solo
outlined
dense
return-object
v-model="form.account"
label="Select Account"
></v-autocomplete>
</v-col>
<v-col cols="12">
<label class="font-weight-bold">Select Product</label>
<div>
<v-autocomplete
dense
solo
item-text="name"
item-value="name"
return-object
:items="products"
#input="addTocart"
></v-autocomplete>
</div>
</v-col>
</v-row>
<div>
<label class="font-weight-bold mb-3">Product Table</label>
<table class="tableReturn">
<thead>
<tr class="tableReturn--header">
<td>Name</td>
<td>Code</td>
<td>Quantity</td>
<td>Unit Price</td>
<td>Discount</td>
<td>Total</td>
<td>Actions</td>
</tr>
</thead>
<tbody>
<tr class="tableReturn--td" v-for="(item, index) in form.items" :key="index">
<td>{{item.name}}</td>
<td>{{item.code}}</td>
<td>
<input type="number" class="table-quantity" v-model="form.items[index].quantity" />
</td>
<td>
<input
type="number"
class="table-quantity"
v-model="form.items[index].unit_price"
placeholder="0.00"
/>
</td>
<td>
<input
type="number"
class="table-quantity"
v-model="form.items[index].discount"
placeholder="0.00"
/>
</td>
<td>USD {{ discountedPrice(item) | formatMoney }}</td>
<td>
<v-btn small color="red" outlined #click="removeItem(index)">
<v-icon>mdi-delete</v-icon>
</v-btn>
</td>
</tr>
<tr>
<td class="py-5" colspan="2">Total</td>
<td colspan="3">{{ Qty }}</td>
<td>USD {{ Total | formatMoney }}</td>
</tr>
</tbody>
</table>
</div>
<v-row>
<v-col md="6" cols="12">
<div class="d-flex flex-column mb-5">
<label for class="font-weight-bold">Return Note</label>
<textarea cols="30" rows="5" class="textarea" v-model="form.return_des"></textarea>
</div>
</v-col>
<v-col md="6" cols="12">
<div class="d-flex flex-column mb-5">
<label for class="font-weight-bold">Staff Note</label>
<textarea cols="30" rows="5" class="textarea" v-model="form.staff_des"></textarea>
</div>
</v-col>
</v-row>
</div>
<v-btn
#click.prevent="updateReturn"
class="blue mx-5 darken-2 mb-5 grey--text text--lighten-4"
>
<v-icon>mdi-check</v-icon>Update
</v-btn>
</v-card>
</v-app>
</template>
<script>
import Vue from "vue";
let numeral = require("numeral");
Vue.filter("formatMoney", function(value) {
return numeral(value).format("00,00.00");
});
export default {
name: "editReturn",
created() {
this.fetchReturn();
this.fetchLocation();
this.fetchSupplier();
this.fetchAccount();
this.fetchProduct();
},
data() {
return {
form: {
items: []
},
suppliers: [],
locations: [],
products: [],
returnsale: [],
accounts: []
};
},
computed: {
Qty() {
if (this.form.hasOwnProperty("items")) {
return this.form.items.reduce((total, item) => {
return total + Number(item.quantity);
}, 0);
}
},
Total() {
if (this.form.hasOwnProperty("items")) {
return this.form.items.reduce((total, item) => {
let subtotal =
(item.unit_price - (item.unit_price * item.discount) / 100) *
item.quantity;
return total + subtotal;
}, 0);
}
}
},
methods: {
discountedPrice(product) {
return (
(product.unit_price - (product.unit_price * product.discount) / 100) *
product.quantity
);
},
fetchLocation() {
this.$axios
.$get(`api/location`)
.then(res => {
// this.locations = res.locations.data;
this.$set(this.$data, "locations", res.locations.data);
console.log(res);
})
.catch(err => {
console.log(err.response);
});
},
fetchSupplier() {
this.$axios
.$get(`api/supplier`)
.then(res => {
this.$set(this.$data, "suppliers", res.suppliers.data);
console.log(res);
})
.catch(err => {
console.log(err.response);
});
},
fetchAccount() {
this.$axios
.$get(`api/account`)
.then(res => {
this.$set(this.$data, "accounts", res.accounts);
console.log(res);
})
.catch(err => {
console.log(err.response);
});
},
fetchProduct() {
this.$axios
.$get(`/api/product`)
.then(res => {
this.$set(this.$data, "products", res.products.data);
console.log(res);
})
.catch(err => {
console.log(err);
});
},
fetchReturn() {
this.$axios
.$get(`api/return-sale/` + this.$route.params.id)
.then(res => {
this.$set(this.$data, "form", res.returnsale);
console.log(res);
for (let i in this.form.items) {
Vue.set(
this.form.items[i],
"quantity",
this.form.items[i].pivot.quantity
);
Vue.set(
this.form.items[i],
"unit_price",
this.form.items[i].pivot.unit_price
);
Vue.set(
this.form.items[i],
"discount",
this.form.items[i].pivot.discount
);
}
})
.catch(err => {
console.log(res.response);
});
},
updateReturn() {
this.$axios
.$patch(`api/return-sale/` + this.form.id, {
location: this.form.location,
products: this.form.products,
supplier: this.form.supplier,
account: this.form.account,
return_des: this.form.return_des,
staff_des: this.form.staff_des
})
.then(res => {
this.returnsale = res.data;
// this.$set(this.$data, "returnsale", res.data);
this.$set(this.$data, "returnsale", res.returnsale);
this.$router.push(`/return/return-sale/view`);
console.log(res);
})
.catch(err => {
console.log(err.response);
});
},
addTocart(item) {
if (this.form.items.includes(item)) {
alert("already there");
} else {
this.form.items.push(item);
}
Vue.set(item, "quantity", 1);
Vue.set(item, "discount", 1);
},
removeItem(index) {
this.form.items.splice(index, 1);
}
}
};
</script>
<style lang="scss">
.textarea {
border: 1px solid rgba(0, 0, 0, 0.125);
outline: 1px solid #461577;
}
.tableReturn {
width: 100%;
margin-top: 10px;
border-collapse: collapse;
&--header {
font-weight: 500;
text-align: left;
border-bottom: 1px solid rgba(0, 0, 0, 0.125);
}
&--td {
border-bottom: 1px solid rgba(0, 0, 0, 0.125);
}
}
.table-quantity {
border: 1px solid rgba(0, 0, 0, 0.125);
padding: 5px 10px 5px 10px;
margin-top: 5px;
margin-bottom: 5px;
}
</style>

Related

show editable field when empty

I have this problem: I have a component with which I create editable fields.
If the field is already populated, everything works fine. I see the database value, I can click on it and change it.
If, on the other hand, in the database that value is empty, it is "hidden" in the view. In this way it is not possible to click on it to insert a value in the editable field.
I tried to get around the obstacle by inserting a: placeholder = "placeholder" but I don't even see that.
How can I do?
This is my visualization file:
<div class="py-0 sm:grid sm:grid-cols-10 sm:gap-4 my-2">
<dt class="text-md leading-6 font-medium text-gray-900 sm:col-span-2 self-center">
{{ $trans('labels.description') }}
</dt>
</div>
<div class="py-1 sm:grid sm:grid-cols-10 sm:gap-4">
<dd class="text-sm leading-5 text-gray-600 sm:mt-0 sm:col-span-10 self-center">
<v-input-text-editable
v-model="task.description"
#input="updateTask()"
:placeholder = "placeholder"
/>
</dd>
</div>
props: {
users: {
type: Array,
default: []
},
description: {
type: String,
default: null
}
},
data() {
return {
task: new Form({
description: this.description
})
}
},
this is my component:
<template>
<div class="block w-full">
<div v-if="!editable" class="cursor-pointer" #click="enableEditMode()">
{{ valueAfterEdit }}
</div>
<div v-else class="mt-1 flex rounded-md shadow-sm" v-click-outside="handleClickOutside">
<div class="relative flex-grow focus-within:z-10">
<input #keyup.enter="commitChanges()" class="form-input block w-full rounded-none rounded-l-md transition ease-in-out duration-150 sm:text-sm sm:leading-5" v-model="valueAfterEdit" ref="input"/>
</div>
<span class="btn-group">
<button #click="discardChanges()" type="button" class="btn btn-white rounded-l-none border-l-0">
<svg class="h-4 w-4 text-gray-400" fill="currentColor" viewBox="0 0 20 20">
<path d="M10 8.586L2.929 1.515 1.515 2.929 8.586 10l-7.071 7.071 1.414 1.414L10 11.414l7.071 7.071 1.414-1.414L11.414 10l7.071-7.071-1.414-1.414L10 8.586z"/>
</svg>
</button>
<button #click="commitChanges()" type="button" class="btn btn-white">
<svg class="h-4 w-4 text-gray-400" fill="currentColor" viewBox="0 0 20 20">
<path d="M0 11l2-2 5 5L18 3l2 2L7 18z"/>
</svg>
</button>
</span>
</div>
</div>
</template>
<script>
export default {
props: {
value: {
type: String,
default: null
},
allowEmpty: {
type: Boolean,
default: false
}
},
data() {
return {
editable: false,
valueBeforeEdit: this.value,
valueAfterEdit: this.value
}
},
watch: {
value(val) {
this.valueBeforeEdit = val;
}
},
methods: {
handleClickOutside() {
this.disableEditMode();
this.commitChanges();
},
enableEditMode() {
this.editable = true;
this.$emit('edit-enabled');
this.$nextTick(() => {
this.$refs.input.focus();
});
},
disableEditMode() {
this.editable = false;
this.$emit('edit-disabled');
},
commitChanges() {
if (!this.allowEmpty && this.valueAfterEdit !== '.') {
this.$emit('input', this.valueAfterEdit);
this.disableEditMode();
}
},
discardChanges() {
this.valueAfterEdit = this.valueBeforeEdit;
this.disableEditMode();
}
}
}
</script>

I there any way to filter the data in table when clicked one part of section pie chart? laravel vue

i had implement the click event for the chart and it can filter the data in the table. But now i face the problem of the data does not return all the data in the table when click outside the chart are.How can make it return all the data when click outside the area chart? Thank you. Any help will be appreciated.
<script>
import VueApexCharts from "vue-apexcharts";
import faker from "faker";
export default {
components: {
VueApexCharts,
},
data() {
return {
Lists: [],
selectedData:{},
search1:'',
clicked:'',
clicked1:'',
isLoading1:true,
currentPage: 1,
series: [300, 200, 49, 100,290, 228, 119, 55],
chartOptions: {
colors: ['#7961F9', '#FF9F43', '#196EB0', '#2EAB56','#df87f2','#057FF2', '#14DA6E','#FF5500'],
legend: {
fontSize: "14px",
position: "top",
},
dataLabels: {
enabled: true,
minAngleToShowLabel: 0,
distributed: false,
style: {
colors: ['#111'],
},
background: {
enabled: true,
foreColor: '#fff',
borderWidth: 0,
}
},
chart: {
width: 500,
type: 'pie',
events: {
legendClick: (chartContext, seriesIndex,w, config) => {
this.clicked = w.config.labels[seriesIndex];
console.log(this.clicked);
console.log(seriesIndex);
},
dataPointSelection: (event,chartContext,config) => {
this.clicked1 = config.w.config.labels[config.dataPointIndex];
console.log(this.clicked1);
},
},
},
labels: ['Private', 'Local','Dental', 'Government','Cyber Security', 'Health', 'Foreign','Medical'],
responsive: [
{
breakpoint: 480,
options: {
legend: {
position: "bottom",
fontSize: "12px",
},
},
},
],
},
}
},
created() {
this.getData();
this.getData1();
},
computed:{
filterLists(){
let list = this.Lists;
if(this.search1 !=''){
list = list.filter((tr)=>{
return tr.agency.toUpperCase().includes(this.search1.toUpperCase())
});
}
if (this.clicked !=''&& this.clicked){
list = list.filter((tr)=>{
return tr.projectCategory.toUpperCase().includes(this.clicked.toUpperCase())
});
}
if (this.clicked1 !=''&& this.clicked1){
list = list.filter((tr)=>{
return tr.projectCategory.toUpperCase().includes(this.clicked1.toUpperCase())
});
}
return list;
},
},
methods: {
next(page) {},
getData1() {
this.isLoading1 = true;
for (let i = 0; i < this.randInt(8, 4); i++) {
let index = Math.floor(Math.random() * 2);
let projectCategory = this.rotate([
'Private', 'Local','Dental', 'Government','Cyber Security', 'Health', 'Foreign','Medical'
]);
this.Lists.push({
projectCategory:projectCategory,
project_name: faker.company.catchPhrase(),
agency: faker.company.companyName(),
logo: faker.image.abstract(),
});
}
this.maxPage = 2;
this.isLoading1 = false;
},
next1(page) {
if (page == -2) {
this.currentPage = 1;
} else if (page == -3) {
this.currentPage = this.maxPage;
} else {
if (
this.currentPage + page < 1 ||
this.currentPage + page > this.maxPage
) {
return;
}
this.currentPage += page;
}
this.showLoader("#card-list");
this.Lists = [];
this.isLoading1 = true;
setTimeout(() => {
this.closeLoader("#card-list");
this.getData1();
}, 1500);
},
},
};
</script>
<style>
#card > header{
padding: 1.5rem 2rem;
background-color: #2E3839;
}
#card{
--tw-bg-opacity: 1;
background-color: rgba(249, 250, 251, var(--tw-bg-opacity));
}
.con-img.vs-avatar--con-img img {
object-fit: cover !important;
}
.apexcharts-toolbar {
position:absolute;
margin-right:12px;
}
vs-button.btn:hover{
background-color: rgba(255,255,255,0);
cursor: pointer;
}
</style>
<template>
<div class="mb-base">
<div class="vx-row mb-base">
<div class="vx-col 2/3 w-full mb-base">
<vs-card
id="card"
class="vs-con-loading__container h-full"
>
<template slot="header">
<div class="flex">
<div>
<img
src=""
alt="Info"
class="h-12 inline-block mr-4 object-scale-down"
/>
</div>
<div class="flex flex-col justify-center w-full text-start">
<h3 class="text-white">Source of Fund</h3>
<span class="text-sm text-white">Based on Total Billed (Yearly)</span>
</div>
<div>
</div>
</div>
</template>
<div class="flex flex-wrap mt-2">
<div class="lg:w-1/3 w-full">
<vue-apex-charts
type="donut"
:options="chartOptions"
:series="series"
width="100%"
class="items-center justify-center flex mt-16 content-center"
/>
</div>
<div class="lg:w-2/3 w-full lg:pl-6 pl-0 mt-6">
<div class="flex justify-end items-end">
<vx-input-group class="mb-base lg:w-1/2 w-full">
<template slot="append">
<div class="append-text btn-addon">
<vs-button color="#A9A9A9"><i class="fas fa-search"></i></vs-button>
</div>
</template>
<vs-input
v-model="search1"
placeholder="Project Code or name"
/>
</vx-input-group>
</div>
<div id="card-list">
<vs-list v-if="!isLoading1">
<vs-list-item
v-for="(tr, index) in filtertLists"
:key="index"
class="hover:shadow cursor-pointer text-base mb-4"
>
<template slot="title">
<div
class="flex flex-col ml-2 cursor-pointer"
>
<div class="font-bold">{{ tr.project_name }}</div>
<div>{{ tr.agency }}</div>
</div>
</template>
<template slot="avatar">
<vs-avatar :src="tr.logo"></vs-avatar>
</template>
{{ tr.projectCategory }}
</vs-list-item>
<div v-if="!filterLists.length" class="flex">
<div class="items-center justify-center text-lg font-bold">No record...</div>
</div>
</vs-list>
<div v-else class="flex">
<div class="items-center justify-center">Fetching data...</div>
</div>
</div>
<div class="flex justify-end gap-4">
<div class="flex items-center justify-center text-sm">
Page {{ currentPage }} of
{{ maxPage }}
</div>
<div>
<vs-button
type="filled"
color=" rgba(243, 244, 246)"
class="w-10 mr-2 rounded-md bg-gray-400 text-black btn hover:text-black"
#click="next1(-1)"
>
<i class="fas fa-chevron-left"></i>
</vs-button>
<vs-button
type="filled"
color=" rgba(243, 244, 246)"
class="w-10 mr-2 rounded-md bg-gray-400 text-black btn"
#click="next1(1)"
>
<i class="fas fa-chevron-right"></i>
</vs-button>
</div>
</div>
</div>
</div>
</vs-card>
</div>
</div>
</div>
</template>
I am working with laravel vue pie chart. is there any way to filter the data in the table when click the element of pie chart. For example, when clicked the section pie chart, the table will be filter and display the the data in the table under that section..Any help will be appreciated
It is not possible to point directly to a solution as you have given so little detail. I will still try to explain the logic. For this process, you will get an input from the screen working with Vue.js and you will manipulate a data displayed on Vue.js.
So first; you need to know which part of your pie chart clicked on. I assume the pie chart you are using on your project have some events which triggered when you interact with charts. You will listen that event and catch the value of clicked item.
Now you have the value of clicked item and you need to filter your results by that.
To accomplish that you can use Vue.js Computed Properties and Watchers :https://v2.vuejs.org/v2/guide/computed.html
Lets say you have your data on your Vue.js application:
data () {
return {
clickedItem: null,
itemsOnTable: [ ... ]
}
}
You have all your table content in itemsOnTable and selected item's data in clickedItem
You can use computed to filter your data:
data () {
return {
clickedItem: null,
itemsOnTable: [ ... ]
}
},
computed: {
// filter itemsOnTable if clickedItem have any value
filteredItems: function () {
if(this.clickedItem==null) return this.itemsOnTable;
return this.itemsOnTable.filter(item => item.column = this.clickedItem);
}
}
Now in your Vue.js component you can directly use filteredItems for your table elements v-for
<table>
<tr v-for="items in filteredItems">
<td>{{ item.column }}</td>
<!-- other columns -->
</tr>
</table>
This examples explains basics of interactions and computed properties and aims to help you to understand basics.

VueJS - Add lazy loading to component v-for

I want to increase the loading times on my website. I currently have a list of all my projects and all the projects load on the first pageload. I want to implement lazy loading to those components but I can't seem to find a way to do it.
I have a component called project-card:
<project-card v-bind:project="project" v-bind:key="project.id" v-if="projects" v-for="project in filteredProjects"></project-card>
The template file:
<template>
<div class="project-card rounded dark:bg-gray-800 bg-white overflow-hidden shadow-lg rounded-lg flex flex-col relative">
<img class="w-full h-64 object-cover" :src="'/uploads/' + project.image" alt="">
<div class="px-6 py-4 h-full flex flex-col">
<div class="project-info block min-h-8">
<div class="w-full">
<p class="tracking-widest text-xs title-font font-medium dark:text-white text-gray-500 mb-1">{{ project.language }}</p>
</div>
</div>
<div class="project-language flex-1">
<div class="w-5/6 float-left">
<p class="font-bold dark:text-white gilroy text-xl">{{ project.name }}</p>
</div>
</div>
<div class="project-description flex-2 space-y-4 py-3 h-full">
<p class="dark:text-white ">{{ project.description | str_limit(128) }}</p>
</div>
<div class="read-more mt-auto">
<div class="flex items-center flex-wrap ">
<button type="button" #click="openModal" class="font-bold text-sm text-indigo-600 hover:text-indigo-500 transition duration-150 ease-in-out hover:text-indigo-900 read-more-button flex items-center focus:outline-none">
Lees meer <span class="read-more-arrow ml-2">→</span>
</button>
<span class="text-gray-600 mr-3 inline-flex items-center lg:ml-auto md:ml-0 ml-auto leading-none text-sm pr-3 py-1 border-r-2 border-gray-300">
</span>
<span class="text-gray-600 inline-flex items-center leading-none text-sm">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" class="w-4 h-4 mr-1">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z" />
</svg>
{{ project.stargazers_count }}
</span>
</div>
<project-card-modal :project="project" ref="projectModal"></project-card-modal>
</div>
</div>
</div>
</template>
<script>
import projectCardModalComponent from "./projectCardModalComponent";
export default {
components: {
projectCardModalComponent
},
methods: {
openModal() {
this.$refs.projectModal.show = true;
}
},
props: ['project']
}
</script>
Currently It is looking something like this, so it gives an idea of how it is supposed to turn out. But I want to implement lazy loading to this component. How can I achieve that?
APP.JS
import Vue from "vue";
require('./bootstrap');
window.Vue = require('vue');
Vue.component('project-card', require('./components/projectCardComponent.vue').default);
Vue.component('project-card-modal', require('./components/projectCardModalComponent.vue').default);
Vue.component('contact-form', require('./components/contactFormComponent.vue').default);
Vue.component('contact-form-flash', require('./components/contactFormFlashComponent.vue').default);
Vue.component('project-language-filter-button', require('./components/projectLanguageButtonFilterComponent.vue').default);
Vue.component('project-language-filter-dropdown', require('./components/projectLanguageDropdownFilterComponent.vue').default);
Vue.component('education', require('./components/educationComponent.vue').default);
Vue.component('work-experience', require('./components/workExperienceComponent.vue').default);
Vue.component('sidebar', require('./components/dashboard/dashboardSidebarComponent.vue').default);
Vue.component('projects', require('./components/dashboard/dashboardProjectsComponent.vue').default);
import MugenScroll from 'vue-mugen-scroll'
const app = new Vue({
el: '#app',
data: {
showModal: false,
projects: [],
filteredProjects: [],
languages: [],
languageFilterKey: 'all',
workExperiences: [],
educations: [],
search: '',
pageSizeBegin: 0,
pageSizeEnd: null,
projectsLoading: false,
},
mounted: function() {
this.getProjects();
this.getWorkExperiences();
this.getAllEducations();
this.getProjectLanguages();
},
created() {
this.getFilteredProjects();
},
methods: {
getProjects: function() {
axios.get('/api/get/projects')
.then(response => {
this.filteredProjects = this.projects = response.data
}).catch(err => {
console.log(err)
});
},
getFilteredProjects: function() {
for(let i = 0; i < 6; i++) {
let count = this.filteredProjects.length + i
}
},
getProjectLanguages: function() {
axios.get('/api/get/project/languages')
.then(response => {
this.languages = response.data
}).catch(err => {
console.log(err)
});
},
selectedLanguage: function() {
if(this.languageFilterKey !== null) {
this.languages.forEach((item) => {
item.active = item.language === this.languageFilterKey;
});
} else {
this.languageFilterKey = null
}
},
filterProjectsByLanguage () {
if(this.languageFilterKey === 'all') {
this.filteredProjects = this.projects;
} else {
this.filteredProjects = this.projects.filter((project) => {
return this.languageFilterKey === project.language
});
}
},
getWorkExperiences: function() {
axios.get('/api/get/workexperiences')
.then(response => {
this.workExperiences = response.data
}).catch(err => {
console.log(err)
});
},
getAllEducations: function() {
axios.get('/api/get/educations')
.then(response => {
this.educations = response.data
}).catch(err => {
console.log(err)
});
},
amountOnChange(event) {
if(!event.target.value) {
this.pageSizeEnd = null;
} else {
this.pageSizeEnd = event.target.value;
}
}
},
computed: {
filteredList() {
if(!this.pageSizeEnd) {
return this.projects.filter((project) => {
return project.name.toLowerCase().includes(this.search.toLowerCase()) || project.language.toLowerCase().includes(this.search.toLowerCase())
})
}
return this.projects.filter((project) => {
return project.name.toLowerCase().includes(this.search.toLowerCase()) || project.language.toLowerCase().includes(this.search.toLowerCase())
}).slice(this.pageSizeBegin, this.pageSizeEnd)
},
}
})
Vue.filter('str_limit', function (value, size) {
if (!value) return '';
value = value.toString();
if (value.length <= size) {
return value;
}
return value.substr(0, size) + '...';
});
I think what you want is actually infinite scrolling.
A lot of libs are doing that, my favorite being vue-mugen-scroll.
Take a look at their demo, I think it's close to your use case.
var vm = new Vue({
el: '#vue-instance',
data: {
posts: [],
loading: false
},
created() {
this.getPosts()
},
methods: {
getPosts() {
for (var i = 0; i < 16; i++) {
var count = this.posts.length + i
this.posts.push({
title: 'title ' + count
})
}
}
}
});
<script src="https://unpkg.com/vue-mugen-scroll#0.2.5/dist/vue-mugen-scroll.min.js"></script>
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="vue-instance" class="container">
<div class="row">
<div class="col-sm-6" v-for="(post, index) in posts">
<div class="card m-4" style="width: 18rem;">
<img class="card-img-top" src="https://via.placeholder.com/350x150">
<div class="card-body">
<h5 class="card-title"><strong>{{ post.title }}</strong></h5>
</div>
</div>
</div>
</div>
<mugen-scroll :handler="getPosts" :should-handle="!loading">
loading...
</mugen-scroll>
</div>

vuetify.js how to add type="submit" to Loaders Button

i am new in Vuetify.js im trying to make submit Form using Loaders Button
i tryid but it not worked.
my tryid code
<v-btn
rounded
class="ma-2"
type="submit"
:loading="loading2"
:disabled="loading2"
color="primary"
#click="loader = 'loading2'"
>
Login
<template v-slot:loader>
<span>Loading...</span>
</template>
</v-btn>
my main code
<template>
<v-row justify="center">
<v-col cols="12" sm="6">
<form #submit.prevent="submit">
<v-card ref="form">
<v-card-text>
<h3 class="text-center">Login</h3>
<v-divider class="mt-3"></v-divider>
<v-col cols="12">
<v-img
:src="require('#/assets/1.png')"
class="my-3"
contain
height="80"
/>
</v-col>
<v-col cols="12" sm="12">
<v-text-field
v-model.trim="form.mobile_number"
type="number"
label="Mobile No"
solo
autocomplete="off"
></v-text-field>
<small class="form-text red--text" v-if="errors.mobile_number">{{
errors.mobile_number[0]
}}</small>
</v-col>
<v-col cols="12">
<v-text-field
v-model.trim="form.password"
type="password"
label="Password"
solo
autocomplete="off"
append-icon="mdi-eye"
></v-text-field>
<small class="form-text red--text" v-if="errors.password">{{
errors.password[0]
}}</small>
</v-col>
</v-card-text>
<v-divider class="mt-12"></v-divider>
<v-card-actions>
<div class="text-center">
<v-btn
rounded
color="primary"
dark
to="/AppMain/UserRegisterPage"
nuxt
>Register</v-btn
>
</div>
<v-spacer></v-spacer>
<div class="text-center">
<v-btn
rounded
class="ma-2"
type="submit"
:loading="loading2"
:disabled="loading2"
color="primary"
#click="loader = 'loading2'"
>
Login
<template v-slot:loader>
<span>Loading...</span>
</template>
</v-btn>
<!-- <v-btn rounded type="submit" color="primary" dark>Login</v-btn> -->
</div>
</v-card-actions>
</v-card>
</form>
</v-col>
</v-row>
</template>
<script>
export default {
middleware: ["guest"],
data() {
return {
loader: null,
loading: false,
loading2: false,
loading3: false,
loading4: false,
loading5: false,
form: {
mobile_number: "",
password: "",
},
};
},
watch: {
loader() {
const l = this.loader;
this[l] = !this[l];
setTimeout(() => (this[l] = false), 3000);
this.loader = null;
},
},
methods: {
async submit() {
await this.$auth.loginWith("local", {
data: this.form,
});
this.$router.push("/");
},
},
};
</script>
<style>
.custom-loader {
animation: loader 1s infinite;
display: flex;
}
#-moz-keyframes loader {
from {
transform: rotate(0);
}
to {
transform: rotate(360deg);
}
}
#-webkit-keyframes loader {
from {
transform: rotate(0);
}
to {
transform: rotate(360deg);
}
}
#-o-keyframes loader {
from {
transform: rotate(0);
}
to {
transform: rotate(360deg);
}
}
#keyframes loader {
from {
transform: rotate(0);
}
to {
transform: rotate(360deg);
}
}
</style>
You create and use the variable "loading" but never set it.
For resolve your problem, please, add in your #click method something like
loading = true
....
and then make loading same to false, for stop the loading style of your button.
Good luck!

How to pass updated data to already child vue component rendered

I'm trying to update some vars into a component from parent. My situation is this:
I have a parent component:
import LugarListComponent from './LugarListComponent';
import LugarAddComponent from './LugarAddComponent'
export default {
components:{
'lugar-list-component' : LugarListComponent,
'lugar-add-component' : LugarAddComponent,
},
data(){
return {
show: false,
nombre: '',
desc : '',
}
},
methods:{
showComponent: function () {
this.show = true;
},
hideComponent: function () {
this.show = false;
},
setLugar: function(lugar){
this.show = true;
}
},
mounted() {
//console.log('Component mounted.')
}
}
<template>
<div class="container">
<h3>Lugares</h3>
<div style="text-align: right">
<button type="button" class="btn btn-primary" v-show="!show" v-on:click.prevent="showComponent"><i class="fa fa-plus"></i> Adicionar</button>
<button type="button" class="btn btn-success" v-show="show" v-on:click.prevent="hideComponent"><i class="fa fa-arrow-left"></i> Regresar</button>
</div>
<br>
<lugar-list-component v-show="!show" #setLugar="setLugar"></lugar-list-component>
<lugar-add-component v-show="show" #hideComponent="hideComponent"></lugar-add-component>
</div>
</template>
This component has two childs components, lugar-list for list places and lugar-add for add a place. I have a show var for control when I show one of this.
I want to edit a place, but I want to send data to lugar-add for show his values into this component, but I don't find any solution for update the vars into lugar-add. Here I show the code of this components.
For lugar-add
export default {
data(){
return {
image: '',
nombre: '',
desc : ''
}
},
methods: {
onImageChange(e) {
let files = e.target.files || e.dataTransfer.files;
if (!files.length)
return;
this.createImage(files[0]);
},
createImage(file) {
let reader = new FileReader();
let vm = this;
reader.onload = (e) => {
vm.image = e.target.result;
};
reader.readAsDataURL(file);
},
uploadImage(){
axios.post('/lugar',{
image: this.image,
nombre: this.nombre,
desc: this.desc
}).then(response => {
if(response.status == 200){
this.$emit('hideComponent')
}
});
},
setAttributes(lugarEdit){
console.log('disparado');
this.nombre = lugarEdit.nombre;
this.desc = lugarEdit.desc;
}
},
mounted() {
//console.log('Component mounted.');
this.$on(
'setAttributes',
function(lugar) {
this.nombre = lugar.nombre;
this.desc = lugar.desc;
}
);
}
<template>
<div class="container">
<div class="form-group">
<label>Nombre</label>
<input type="text" v-model="nombre" class="form-control" placeholder="Nombre del lugar">
</div>
<div class="form-group">
<label for="descTexArea">Descripción</label>
<textarea v-model="desc" class="form-control" id="descTexArea" rows="3"></textarea>
</div>
<div class="form-group">
<label for="exampleFormControlFile1">Subir imágenes</label>
<input type="file" v-on:change="onImageChange" class="form-control-file" id="exampleFormControlFile1">
</div>
<div class="form-group">
<button type="button" class="btn btn-primary" #click="uploadImage">Adicionar</button>
</div>
<div class="col-md-3" v-if="image">
<img :src="image" class="img-responsive" height="70" width="90">
</div>
</div>
</template>
Here I use event for hide this component and show the lugar-list component. Here is the code for lugar-list
export default {
name: 'lugar-list-component',
data:function(){
return {
listLugares : [],
id : '',
}
},
methods:{
getLugares: function () {
fetch('/lugar')
.then(response => response.json())
.then(res => {
this.listLugares = res;
})
},
setId: function(id){
this.id = id;
},
removeLugar: function(id){
this.id = id;
axios.delete('lugar/'+id)
.then(response => {
this.getLugares();
});
},
editLugar: function(id){
this.id = id;
axios.get('lugar/'+id)
.then(response => {
this.$emit('setLugar',response);
});
},
},
mounted() {
this.getLugares();
}
}
<template>
<div class="container">
<table class="table table-striped">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Nombre</th>
<th scope="col">Desc.</th>
<th scope="col">Fecha</th>
<th scope="col">Acciones</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in listLugares">
<th scope="row">{{ index+1 }}</th>
<td>{{ item.nombre }}</td>
<td>{{ item.desc }}</td>
<td>{{ item.created_at }}</td>
<td>
<button type="button" class="btn btn-success" v-on:click.prevent="editLugar(item.id)"><i class="fa fa-edit"></i> Editar</button>
<button type="button" class="btn btn-danger" v-on:click.prevent="removeLugar(item.id)"><i class="fa fa-remove"></i> Eliminar</button>
</td>
</tr>
</tbody>
</table>
</div>
</template>
I hope that you can understand me. Thanks.
Emit an event from the 1st child component to update parent's prop. Then pass the value you want to update as a prop to the secund child element.
I don't find other solution that use routes with params, I believe that is the best solution.
Here is my routes
{
path: '/lugar', component: require('./components/lugar/LugarComponent').default,
children: [
{ path: '', component: LugarList },
{
path: 'add/:id?',
name: 'lugarAdd',
component: LugarAdd
},
{
path: 'list',
component: LugarList
}
]
}
The route for Add a place has an optional param.
Now, into the Add component I get the param with this code:
this.id = this.$route.params.id;
this.modeEdit = true;
axios.get('/lugar/'+this.id)
.then(response => {
this.nombre = response.data.nombre;
this.desc = response.data.desc;
for(let i = 0; i<response.data.images.length; i++){
this.image.push(response.data.images[i]);
}
});
When I get the place id I request for its information with axios.

Resources