React setState does not set state during a while loop - javascript

I want to avoid data multiplication, so i wanted to create a loop for calling my dataprovider for different site_id's.
I created a while loop and set the state values within this while loop. What I realized that from my 2 element array (I have 2 sites) only 1 is being set in the state, but the other one not.
class Dashboard extends Component {
state = {
username: localStorage.getItem('username'),
siteid: [{
id: 1,
daily: "EKdaily",
weekly: "EKweekly",
monthly: "EKmonthly",
total: "EKtotal",
},
{
id: 2,
daily: "AKdaily",
weekly: "AKweekly",
monthly: "AKmonthly",
total: "AKtotal",
}]
};
componentDidMount() {
dataProviderFactory('rest').then(
dataProvider => {
dataProvider(GET_ONE, 'users', {
id: this.state.username,
})
.then(response => this.setState({ fullname: response.data.fullname }))
dataProvider(GET_LIST, 'sites', {
filter: { q: '' },
sort: { field: '', order: '' },
pagination: { page: 1, perPage: 1 },
})
.then(response => this.setState({ sites: response.data }))
var i = 0;
while ( i < 2 ) {
var myId = this.state.siteid[i].id;
var myDaily = this.state.siteid[i].daily;
var myWeekly = this.state.siteid[i].weekly;
var myMonthly = this.state.siteid[i].monthly;
var myTotal = this.state.siteid[i].total;
console.log("id", myId, myDaily, myWeekly, myMonthly, myTotal);
dataProvider(GET_LIST, 'clicks', {
filter: { interval: 'day', site_id: myId, count: '1', },
sort: { field: 'date', order: 'DESC' },
pagination: { page: 1, perPage: 50 },
})
.then(response => this.setState({ [myDaily]: response.data.count }))
dataProvider(GET_LIST, 'clicks', {
filter: { interval: 'week', site_id: myId, count: '1', },
sort: { field: 'date', order: 'DESC' },
pagination: { page: 1, perPage: 50 },
})
.then(response => this.setState({ [myWeekly]: response.data.count }))
dataProvider(GET_LIST, 'clicks', {
filter: { interval: 'month', site_id: myId, count: '1', },
sort: { field: 'date', order: 'DESC' },
pagination: { page: 1, perPage: 50 },
})
.then(response => this.setState({ [myMonthly]: response.data.count }))
dataProvider(GET_LIST, 'clicks', {
filter: { interval: 'all', site_id: myId, count: '1', },
sort: { field: 'date', order: 'DESC' },
pagination: { page: 1, perPage: 50 },
})
.then(response => this.setState({ [myTotal]: response.data.count }))
i++;
}
}
);
}
render() {
const {
fullname,
username,
EKdaily,
EKweekly,
EKmonthly,
EKtotal,
AKdaily,
AKweekly,
AKmonthly,
AKtotal,
} = this.state;
const myPageHeader = `Udvozlunk az Admin feluleten ${fullname}!`;
return (
<div style={styles.flex}>
<div style={styles.leftCol}>
<div>
<Card>
<CardHeader title={myPageHeader} />
<CardContent>Bejelentkezett felhasznalo: {username}</CardContent>
</Card>
</div>
<div stlye={ { magrinTop: 200} }>
<Typography color="textPrimary">123.hu kattintas statisztikak</Typography>
</div>
<div style={styles.flex}>
<DisplayClick value={EKdaily} title="Napi Kattintas" />
<DisplayClick value={EKweekly} title="Heti Kattintas" />
<DisplayClick value={EKmonthly} title="Havi Kattintas" />
<DisplayClick value={EKtotal} title="Ossz Kattintas" />
</div>
<div stlye={ { magrinTop: 20} }>
<Typography color="textPrimary">345.hu kattintas statisztikak</Typography>
</div>
<div style={styles.flex}>
<DisplayClick value={AKdaily} title="Napi Kattintas" />
<DisplayClick value={AKweekly} title="Heti Kattintas" />
<DisplayClick value={AKmonthly} title="Havi Kattintas" />
<DisplayClick value={AKtotal} title="Ossz Kattintas" />
</div>
</div>
</div>
);
}
}
Only AK* is being set in this.state, EK* not.
What do I do wrong?

Use let instead of var here:
Change
var myId = this.state.siteid[i].id;
var myDaily = this.state.siteid[i].daily;
// ....
to
let myId = this.state.siteid[i].id;
let myDaily = this.state.siteid[i].daily;
// ....
var is scoped to the nearest function and not to the while block. It gets hoisted, and your code will be something like:
var i;
var myId;
var myDaily;
i = 0;
while ( i < 2 ) {
myId = this.state.siteid[i].id;
myDaily = this.state.siteid[i].daily;
}
Since dataProvider calls are asynchronous, the value of myId will be replaced with AK values by the first time call is done.
dataProvider(GET_LIST, 'clicks', {
//
})
.then(response => this.setState({ [myWeekly]: response.data.count }))
/* ^^ This callback runs after the while block
By this time, myDaily === "AKdaily" */

Related

How can I use the defined filter function inside of an object? | Nuxt

I have a filter dropdown in the header and when users apply the filters I want to run the assigned function according to their choices but I couldn't run it, I got "... is not a function" error.
Also, even if I solve this problem, I will probably have problems in applying more than one filter at the same time, I would be very grateful if anyone could offer a solution in the form of applying filters in multiple ways.
Example Object:
const filters = [
{
section: 'Call Types',
slug: 'call-types',
type: 'multiselect',
options: [
{
label: 'Inbound',
slug: 'inbound',
value: true,
func: (data) =>
data.items.filter((item) => {
return item.className === 'ib'
}),
},
{
label: 'Outbound',
slug: 'outbound',
value: true,
func: (data) =>
data.items.filter((item) => {
return item.className === 'ob'
}),
},
],
},
{
section: 'Data Types',
slug: 'data-types',
type: 'multiselect',
options: [
{
label: 'Sentiment',
slug: 'sentiment',
value: false,
func: (data) =>
data.items.filter((item) => {
return item.d1 === 1
}),
},
{
label: 'Capture Fails',
slug: 'capture-fails',
value: false,
func: (data) =>
data.items.filter((item) => {
return item.d2 > 0.5
}),
},
{
label: 'Audio Notes',
slug: 'audio-notes',
value: false,
func: (data) =>
data.items.filter((item) => {
return item.d1 === 1 && item.d2 > 0.5
}),
},
],
},
{
section: 'Others',
slug: 'others',
type: 'singleselect',
options: [
{
label: 'Show All Agents',
slug: 'show-all-agents',
value: true,
func: (data) =>
data.items.filter((item) => {
return item
}),
},
],
},
]
exports.filters = filters
And here is the method I'm trying to run when users click to "Filter" button.
Timeline vue file:
<template>
<vis-timeline
:groups="filteredData.groups"
:items="filteredData.items"
/>
</template>
<script>
import { mapActions, mapState } from 'vuex'
import VisTimeline from '~/components/UI/VisTimeline.vue'
export default {
components: { VisTimeline },
computed: {
...mapState({
callTimeline: (state) => state.agents.callTimeline,
timelineLoading: (state) => state.agents.loading,
filterOption: (state) => state.agents.filterOption,
}),
filteredData() {
var filtered = Object.assign(
{},
JSON.parse(JSON.stringify(this.callTimeline))
)
return this.filterItems(filtered)
},
},
methods: {
filterItems(data) {
if (this.filterOption) {
this.filterOption.forEach((filter) => {
filter.options.some((option) => {
if (option.value) {
return option.func(data)
}
})
})
}
return data
}
}
}
</script>
Header vue file:
<template>
<a-layout-header style="background: #fff; padding: 0">
<a-dropdown
:trigger="['click']"
:visible="visible"
#visibleChange="handleVisible"
>
<template #overlay>
<a-menu>
<a-menu-item-group
v-for="filter in filters"
:key="filter.slug"
:title="filter.section"
>
<a-menu-item :key="filter.slug">
<a-checkbox
v-for="option in filter.options"
:key="option.slug"
:name="option.slug"
:checked="option.value"
#change="filterChanges"
>
{{ option.label }}
</a-checkbox>
</a-menu-item>
</a-menu-item-group>
<a-menu-divider />
<a-menu-item key="buttons"
><div class="flex justify-between">
<a-button #click="visible = false" type="danger" ghost
>Cancel</a-button
>
<a-button #click="applyFilters" type="primary">Filter</a-button>
</div></a-menu-item
>
</a-menu>
</template>
<div class="mb-1">
<a-button type="primary">
<div class="flex flex-row justify-center space-x-2 items-center">
<a-icon type="filter" />
<span>Filters</span>
<a-icon type="down" />
</div>
</a-button>
</div>
</a-dropdown>
</a-layout-header>
</template>
<script>
import { mapActions} from 'vuex'
import { filters } from '~/utils/Filters'
export default {
name: 'DBHeader',
data() {
return {
visible: false,
filters: filters,
}
},
methods: {
...mapActions({
setFilterOption: 'agents/setFilterOption',
}),
handleVisible(flag) {
this.visible = flag
},
filterChanges(e) {
this.filters.forEach((filter) => {
filter.options.some((option) => {
if (option.slug === e.target.name) {
option.value = e.target.checked
}
})
})
},
applyFilters() {
var filters = Object.assign([], JSON.parse(JSON.stringify(this.filters)))
this.setFilterOption(filters)
this.visible = false
},
},
}
</script>

React state.map return empty array

I created an application using the covid19 api. I store state and function in the context api. I want to create an apexchart, but the dailydata map that keeps it in the same place returns an empty array. I need to enter my options data with dailyDate
const {country} = useContext(countryContext);
const [dailyData,setDailyData] = useState([]);
useEffect(() => {
const fetchdata = async () => {
const data = await dayOneAllStatus(country);
setDailyData(data);
};
fetchdata();
},[country])
const [options,setOptions] = useState({
chart: {
height: 350,
type: 'area'
},
dataLabels: {
enabled: false
},
stroke: {
curve: 'smooth'
},
xaxis: {
type: 'datetime',
categories: dailyData.map(item => item.Date)
},
tooltip: {
x: {
format: 'dd/MM/yy'
},
},
});
const [series,setSeries] = useState([
{
name: 'Vaka',
data: dailyData.map(item => item.Confirmed)
}, {
name: 'İyileşen',
data: dailyData.map(item => item.Recovered)
},
{
name: 'Ölüm',
data: dailyData.map(item => item.Deaths)
}
]);
return (
<div id="chart">
<Chart options={options} series={series} type="area" height={350} />
</div>
)
dailyData console.log example
Array(426) [ {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, … ]
[0…99]
[100…199]
[200…299]​
[300…399]
[400…425]
length: 426
When you call useState, the parameter that's passed in is the initial-value. So when the function re-runs because dailyData has updated, the values in options and series don't update.
If you're not actually using setSeries and setOptions anywhere, you're better off leaving them as normal variables that are computed using dailyData:
const {country} = useContext(countryContext);
const [dailyData,setDailyData] = useState([]);
useEffect(() => {
const fetchdata = async () => {
const data = await dayOneAllStatus(country);
setDailyData(data);
};
fetchdata();
},[country])
const options = {
chart: {
height: 350,
type: 'area'
},
dataLabels: {
enabled: false
},
stroke: {
curve: 'smooth'
},
xaxis: {
type: 'datetime',
categories: dailyData.map(item => item.Date)
},
tooltip: {
x: {
format: 'dd/MM/yy'
},
},
};
const series = [
{
name: 'Vaka',
data: dailyData.map(item => item.Confirmed)
}, {
name: 'İyileşen',
data: dailyData.map(item => item.Recovered)
},
{
name: 'Ölüm',
data: dailyData.map(item => item.Deaths)
}
];
return (
<div id="chart">
<Chart options={options} series={series} type="area" height={350} />
</div>
)

Total sum of all input values

I want to sum every existing and added value to a total sum, which I can always see.
I'm kinda struggling with how to include my existing values and those I want to add afterward.
In IncomeList I have two inputs. One for the incomeReason and with the other I can add the value (newIncomeValue). Every Item in my List contains an id. With sumValue I want to display the total amount, but I'm not sure how to do it
It looks like this:
IncomeItem.vue
<template>
<div class="income-item">
<div class="income-item-left">
<div v-if="!editing" #dblclick="editincome" class="income-item-label"
:class="{ completed : completed }">{{ title }}</div>
</div>
<div class="income-item-right"> {{ value }} </div>
<div class="remove-item" #click="removeincome(income.id)">
×
</div>
</div>
</template>
<script>
export default {
name: 'income-item',
props: {
income: {
type: Object,
required: true,
},
checkAll: {
type: Boolean,
required: true,
}
},
data() {
return {
'id': this.income.id,
'title': this.income.title,
'value': this.income.value,
'completed': this.income.completed,
'editing': this.income.editing,
'beforeEditCache': '',
}
},
watch: {
checkAll() {
// if (this.checkAll) {
// this.completed = true
// } else {
// this.completed = this.income.completed
// }
this.completed = this.checkAll ? true : this.income.completed
}
},
directives: {
focus: {
inserted: function (el) {
el.focus()
}
}
},
methods: {
removeincome(id) {
this.$emit('removedincome', id)
},
}
}
</script>
IncomeList.vue
<template>
<div>
<input type="text" class="income-input" placeholder="What needs to be done" v-model="newIncome" #keyup.enter="addincome">
<input type="text" class="value-input" placeholder="€" v-model="newIncomeValue" #keyup.enter="addValue">
<transition-group name="fade" enter-active-class="animated fadeInUp" leave-active-class="animated fadeOutDown">
<income-item v-for="income in incomesFiltered" :key="income.id" :income="income" :checkAll="!anyRemaining"
#removedIncome="removeincome" #finishedEdit="finishedEdit">
</income-item>
</transition-group>
<div class="extra-container">
<div><label><input type="checkbox" :checked="!anyRemaining" #change="checkAllincomes"> Check All</label></div>
<div>{{ remaining }} items left</div>
</div>
<div class="sum-container">
<div><label> Einkommen: </label></div>
<div>{{ sumValue }} €</div>
</div>
</div>
</template>
<script>
import IncomeItem from './IncomeItem'
export default {
name: 'income-list',
components: {
IncomeItem,
},
data () {
return {
newIncome: '',
newIncomeValue: '',
idForincome: 3,
incomes: [
{
'id': 1,
'title': 'Finish Vue Screencast',
'value': 300,
'completed': false,
'editing': false,
},
{
'id': 2,
'title': 'Take over world',
'value': 315,
'completed': false,
'editing': false,
},
]
}
},
computed: {
remaining() {
return this.incomes.filter(income => !income.completed).length
},
anyRemaining() {
return this.remaining != 0
},
incomesFiltered() {
return this.incomes
},
sumValue() {
var total = parseInt(document.getElementsByClassName('newIncomeValue').value)
return total;
},
},
methods: {
addincome() {
if (this.newIncome.trim().length == 0) {
return
}
this.incomes.push({
id: this.idForincome,
title: this.newIncome,
value: this.newIncomeValue,
completed: false,
})
this.newIncome = ''
this.newIncomeValue = ''
this.this.idForincome++
},
removeincome(id) {
const index = this.incomes.findIndex((item) => item.id == id)
this.incomes.splice(index, 1)
},
checkAllincomes() {
this.incomes.forEach((income) => income.completed = event.target.checked)
},
clearCompleted() {
this.incomes = this.incomes.filter(income => !income.completed)
},
finishedEdit(data) {
const index = this.incomes.findIndex((item) => item.id == data.id)
this.incomes.splice(index, 1, data)
},
//Same for Value
addValue() {
if (this.newIncomeValue.trim().length == 0) {
return
}
this.incomes.push({
id: this.idForincome,
title: this.newIncome,
value: this.newIncomeValue,
completed: false,
})
this.newIncome = ''
this.newIncomeValue = ''
this.this.idForincome++
},
}
}
</script>
If you want to sum the value property of your incomesFiltered, you can use reduce in your computed:
sumValue() {
return this.incomesFiltered.reduce((a, c) => a + c.value, 0);
}

Repeating Edit and Delete Button in React via Mui Datatable

Hi Everyone, I'm trying to achieve adding edit and delete button in ReactJS using Mui Datatable, but the problem is that it keeps on repeating because of the Map sorry I'm just new in ReactJS anyways, here is my image and my code:
This is an example of my image:
And This My Code:
import React, { Component } from "react";
import { Link } from "react-router-dom";
import axios from "axios";
import MUIDataTable from "mui-datatables";
const Client = (props) => (
<>
<Link to={"client/edit/" + props.client._id} className="btn btn-primary">
Edit
</Link>
<a
href="client"
onClick={() => {
props.deleteClient(props.client._id);
}}
className="btn btn-danger"
>
Delete
</a>
</>
);
export default class ClientsList extends Component {
constructor(props) {
super(props);
this.deleteClient = this.deleteClient.bind(this);
this.state = { clients: [] };
}
componentDidMount() {
axios
.get("http://localhost:5000/clients/")
.then((response) => {
this.setState({ clients: response.data });
})
.catch((error) => {
console.log(error);
});
}
deleteClient(id) {
axios.delete("http://localhost:5000/clients/" + id).then((response) => {
console.log(response.data);
});
this.setState({
clients: this.state.clients.filter((el) => el._id !== id),
});
}
// This is the map I was talking about:
clientList() {
return this.state.clients.map((currentclient) => {
return (
<Client
client={currentclient}
deleteClient={this.deleteClient}
key={currentclient._id}
/>
);
});
}
render() {
const columns = [
{
name: "name",
label: "Name",
options: {
filter: true,
sort: true,
},
},
{
name: "address",
label: "Address",
options: {
filter: true,
sort: true,
},
},
{
name: "mobile",
label: "Mobile",
options: {
filter: true,
sort: true,
},
},
{
name: "email",
label: "Email",
options: {
filter: true,
sort: true,
},
},
{
name: "gender",
label: "Gender",
options: {
filter: true,
sort: true,
},
},
{
name: "birthday",
label: "Birthday",
options: {
filter: true,
sort: true,
},
},
{
name: "facebookPage",
label: "Facebook Page",
options: {
filter: true,
sort: true,
},
},
{
name: "facebookName",
label: "Facebook Name",
options: {
filter: true,
sort: true,
},
},
{
name: "existing",
label: "Existing",
options: {
filter: true,
sort: true,
},
},
{
name: "remarks",
label: "Remarks",
options: {
filter: true,
sort: true,
},
},
{
name: "Action",
options: {
customBodyRender: () => {
return <>{this.clientList()}</>;
},
},
},
];
const { clients } = this.state;
return (
<>
<br />
<br />
<br />
<div style={{ margin: "10px 15px", overflowX: "auto" }}>
<Link to={"client/create/"} className="btn btn-primary pull-right">
Add Client Data
</Link>
<br />
<br />
<br />
<MUIDataTable data={clients} columns={columns} />
</div>
</>
);
}
}
Thank you for your help and understanding I really appreciate it!
You can just not use map in the clientList() function because you are returning (edit, delete ) of all the clients for each row in the table. you also can pass the row data like I will show in the link on each button and have the _id as a hidden column on your table so that you can have access on it.
import React, { Component } from "react";
import { Link } from "react-router-dom";
import axios from "axios";
import MUIDataTable from "mui-datatables";
const Client = (props) => (
<>
<Link to={"client/edit/" + props.client._id} className="btn btn-primary">
Edit
</Link>
<a
href="client"
onClick={() => {
props.deleteClient(props.client._id);
}}
className="btn btn-danger"
>
Delete
</a>
</>
);
export default class ClientsList extends Component {
constructor(props) {
super(props);
this.deleteClient = this.deleteClient.bind(this);
this.state = {
clients: [{
}]
};
}
componentDidMount() {
axios
.get("http://localhost:5000/clients/")
.then((response) => {
this.setState({ clients: response.data });
})
.catch((error) => {
console.log(error);
});
}
deleteClient(id) {
axios.delete("http://localhost:5000/clients/" + id).then((response) => {
console.log(response.data);
});
this.setState({
clients: this.state.clients.filter((el) => el._id !== id),
});
}
// This is the map I was talking about:
clientList(currentclient) {
// current cleint her is an array that contain all the columns values for the row specify
// assuming that _id will be the first column
return (
<Client
client={currentclient}
deleteClient={this.deleteClient}
key={currentclient[0]}
/>
);
}
render() {
const columns = [
{
name: "_id",
options: {
display: false,
}
},
{
name: "name",
label: "Name",
options: {
filter: true,
sort: true,
},
},
{
name: "address",
label: "Address",
options: {
filter: true,
sort: true,
},
},
{
name: "mobile",
label: "Mobile",
options: {
filter: true,
sort: true,
},
},
{
name: "email",
label: "Email",
options: {
filter: true,
sort: true,
},
},
{
name: "gender",
label: "Gender",
options: {
filter: true,
sort: true,
},
},
{
name: "birthday",
label: "Birthday",
options: {
filter: true,
sort: true,
},
},
{
name: "facebookPage",
label: "Facebook Page",
options: {
filter: true,
sort: true,
},
},
{
name: "facebookName",
label: "Facebook Name",
options: {
filter: true,
sort: true,
},
},
{
name: "existing",
label: "Existing",
options: {
filter: true,
sort: true,
},
},
{
name: "remarks",
label: "Remarks",
options: {
filter: true,
sort: true,
},
},
{
name: "Action",
options: {
customBodyRender: (value, tableMeta, updateValue) => {
return <>{this.clientList(tableMeta.rowData)}</>;
},
},
},
];
const { clients } = this.state;
return (
<>
<br />
<br />
<br />
<div style={{ margin: "10px 15px", overflowX: "auto" }}>
<Link to={"client/create/"} className="btn btn-primary pull-right">
Add Client Data
</Link>
<br />
<br />
<br />
<MUIDataTable data={clients} columns={columns} />
</div>
</>
);
}
}
you can deconstruct clients data from state and then pass it to MUIDataTable
const { clients } = this.state;
const rows = clients.map((client) => {
return {
// assuming atributes
name: client.name,
address: client.address,
mobile: client.mobile,
email: client.email,
gender: client.gender,
birthday: client.birthday,
action: <Link to=`client/edit/${client.id}` calssName='btn btn-primary'> Edit </Link> <a href='client' onClick={() => this.deleteClient(client.id)}> delete </a>
}
}
and then pass it in data props in MUIDataTable
<MUIDataTable data={rows} columns={columns} />
this is an example of a working snippet, tweak it to match you need
const rows = orders.map((order) => {
return {
ref: <Link to={"/orders/" + order.ref}>{order.ref.slice(0, 8)}</Link>,
amount: order.amount,
donated: order.ticketsDetails[0].ticketDonate != "" ? "Yes" : "No",
date: order.createdAt.slice(0, 16),
};
});
const columns = [
{
label: "Ref",
name: "ref",
options: {
filter: true,
sort: true,
},
},
{
label: "Amount",
name: "amount",
options: {
filter: true,
sort: true,
},
},
{
label: "Date",
name: "date",
options: {
filter: true,
sort: true,
},
},
{
label: "Donated",
name: "donated",
options: {
filter: true,
sort: true,
},
},
];
return (
<div className="orders-container">
<MUIDataTable columns={columns} data={rows} />
</div>
);

v-model is not changing data with switch and select dropdown in vuejs

I'm making a dynamic admin panel with crud generator with the help of laravel and vue.
I have a table where I'm loading data asynchronously from API. There is an is_featured column in my table which I want to be a switch. So that the user can change the value from the table page instead of going to edit page.
To generate my entire table there is a configuration object that contains which fields to show and the type of that field and other metadata. In the configuration object, there is a field named prerender which is responsible to prerender fields that require calling other API or some editable fields like select dropdown or switch.
To make switch and select fields work, I have an empty object named fieldData.
When a field with type: boolean and prerender: true is found, my code will initialize a property with field.name as property name in field data and fill it with the corresponding values under that field name
this.fieldData[field.name] = {};
this.tableData.forEach(
data => (this.fieldData[field.name][data.id] = data[field.name])
);
BUT THE SWITCH AND SELECT ARE NOT WORKING HERE
So I need help.
Here's my entire code for reference
<template>
<div class="app-container">
<el-row :gutter="20" style="display: flex; align-items: center;">
<el-col :span="10">
<h1 style="text-transform: capatilize;">{{ resourceName }}</h1>
</el-col>
<el-col :span="14" style="display: flex; justify-content: flex-end; align-items: center">
<el-input
v-model="navigation.search"
placeholder="Search anything here"
prefix-icon="el-icon-search"
style="width: 300px; margin: 0 10px;"
#keydown.enter.native="handleGlobalSearch"
/>
<FilterPannel
style="margin: 0 10px"
:filter-pannel-obj="filterPannelObj"
#set-filter="handleFilterVals"
#reset-filter="getTableData({})"
/>
<Import
:url="`/api/${resourceName}/upload`"
#import-success="handleImportSucces"
#import-error="handleImportError"
/>
<Export :url="`/api/${resourceName}/export`" :selected-ids="selected.map(el => el.id)" />
<el-button
type="info"
icon="el-icon-delete"
#click="$refs['table'].clearSelection()"
>Clear Selection</el-button>
<el-button type="danger" icon="el-icon-delete" #click="handleMultipleDelete">Delete Selected</el-button>
</el-col>
</el-row>
<el-row>
<el-table
ref="table"
v-loading="loading.tableData"
:data="tableData"
border
:row-key="getRowKeys"
#sort-change="handleSortChange"
#selection-change="handleSelectionChange"
>
<el-table-column type="selection" label="Selection" reserve-selection />
<el-table-column label="Actions" width="200">
<template slot-scope="scope">
<div style="display: flex; justify-content: space-around;">
<el-button
icon="el-icon-view"
type="primary"
circle
#click="$router.push(`/${resourceName}/view/${scope.row.id}`)"
/>
<el-button
icon="el-icon-edit"
type="success"
circle
#click="$router.push(`/${resourceName}/edit/${scope.row.id}`)"
/>
<el-button
icon="el-icon-delete"
type="danger"
circle
#click="handleDeleteClick(scope.row.id)"
/>
</div>
</template>
</el-table-column>
<el-table-column
v-for="field in fieldsToShow"
:key="field.name"
:prop="field.name"
:label="field.name.replace('_',' ')"
sortable="custom"
>
<template slot-scope="scope">
<div
v-if="field.type=='multilangtext'"
class="cell"
>{{ JSON.parse(scope.row[field.name])[$store.state.app.language] }}</div>
<div v-if="field.type=='text'" class="cell">{{ scope.row[field.name] }}</div>
<el-tag v-if="field.type=='tag'">{{ scope.row.type }}</el-tag>
<img v-if="field.type=='image'" :src="scope.row.icon" width="100px" height="100px" />
<div v-if="field.type=='oneFrom'" class="cell">
<el-tag
:key="scope.row[field.name]"
>{{field.multilang ? scope.row[field.name][$store.state.app.language] : scope.row[field.name]}}</el-tag>
</div>
<div v-if="field.type=='manyFrom'" class="cell">
<el-tag
style="margin: 5px"
v-for="(item, index) in scope.row[field.name]"
:key="`${field.multilang ? item[$store.state.app.language] : item}+${index}`"
>{{field.multilang ? item[$store.state.app.language] : item}}</el-tag>
</div>
<div v-if="field.type=='boolean'" class="cell">
{{scope.row.id}} =>
{{field.name}} =>>
{{scope.row[field.name]}} =>>>
{{fieldData[field.name][scope.row.id]}}
<el-switch
:key="`switch${scope.row.id}`"
v-model="fieldData[field.name][scope.row.id]"
:active-value="1"
:inactive-value="0"
></el-switch>
</div>
<div v-if="field.type=='select'" class="cell">
<el-select :key="`select${scope.row.id}`" v-model="fieldData[field.name][scope.row.id]" placeholder="Select">
<el-option
v-for="item in field.options"
:key="item"
:label="item"
:value="item"
></el-option>
</el-select>
</div>
</template>
</el-table-column>
</el-table>
</el-row>
<el-row>
<pagination
style="padding: 0;"
:total="paginationData.total"
:page.sync="paginationData.current_page"
:limit.sync="paginationData.per_page"
#pagination="handlePagination"
/>
</el-row>
</div>
</template>
<script>
import Pagination from '#/components/Pagination';
import FilterPannel from '#/components/FilterPannel';
import Export from './components/Export'; // ? not needed
import Import from './components/Import';
import axios from 'axios';
import Resource from '#/api/resource';
const resourceName = 'coupons';
const ResourceApi = new Resource(resourceName);
export default {
name: 'CategoryList',
components: {
Pagination,
FilterPannel,
Export,
Import,
},
data() {
return {
resourceName: resourceName,
language: 'en',
tableData: [],
fieldData: {},
fieldsToShow: [
{ name: 'title', type: 'multilangtext' },
// { name: 'description', type: 'multilangtext' },
{ name: 'code', type: 'text' },
// { name: 'expiry_date', type: 'text' },
{ name: 'type', type: 'tag' },
{
name: 'store_id',
type: 'oneFrom',
url: '/api/stores/',
foreignKey: 'store_id',
attrName: 'name',
multilang: true,
prerender: true,
},
{
name: 'tags',
type: 'manyFrom',
url: '/api/tags?idsarr=',
foreignKey: 'tags',
attrName: 'name',
multilang: true,
prerender: true,
},
{
name: 'brands',
type: 'manyFrom',
url: '/api/brands?idsarr=',
foreignKey: 'brands',
attrName: 'name',
multilang: true,
prerender: true,
},
// { name: 'brands', type: 'text' },
{ name: 'is_featured', type: 'boolean', prerender: true },
{
name: 'status',
type: 'select',
options: ['publish', 'draft', 'trash'],
prerender: true,
},
],
paginationData: {
current_page: 0,
last_page: 0,
per_page: 0,
total: 0,
},
navigation: {
page: 1,
limit: 10,
sort: '',
'sort-order': 'asc',
filters: '',
search: '',
},
filters: '',
selected: [], // ? for selection
loading: {
tableData: false,
},
allData: [],
filterPannelObj: {
title: {
default: '',
type: 'Input',
label: 'Title',
},
description: {
default: '',
type: 'Input',
label: 'Description',
},
promo_text: {
default: '',
type: 'Input',
label: 'Promo Text',
},
type: {
default: [],
type: 'checkbox',
label: 'Type',
src: [
{ value: 'coupon', label: 'Coupon' },
{ value: 'offer', label: 'Offer' },
],
},
code: {
default: '',
type: 'Input',
label: 'Code',
},
store_id: {
default: [],
type: 'select',
label: 'Store',
src: [],
multiple: true,
},
brands: {
default: [],
type: 'select',
label: 'Brands',
src: [],
multiple: true,
},
tags: {
default: [],
type: 'select',
label: 'Tags',
src: [],
multiple: true,
},
cats: {
default: [],
type: 'select',
label: 'Categories',
src: [],
multiple: true,
},
},
};
},
watch: {
async 'navigation.search'(newVal, oldVal) {
await this.handleGlobalSearch();
},
},
async created() {
await this.getTableData({});
this.allData = await ResourceApi.list({ limit: -1 });
// to fill filter dialog selects
// To get brands
this.filterPannelObj.brands.src = (await axios.get(
`/api/brands?limit=-1`
)).data.map(({ name, id }) => ({
label: JSON.parse(name)[this.$store.state.app.language],
value: id,
}));
// To get tags
this.filterPannelObj.tags.src = (await axios.get(
`/api/tags?limit=-1`
)).data.map(({ name, id }) => ({
label: JSON.parse(name)[this.$store.state.app.language],
value: id,
}));
// To get categories
this.filterPannelObj.cats.src = (await axios.get(
`/api/categories?limit=-1`
)).data.map(({ name, id }) => ({
label: JSON.parse(name)[this.$store.state.app.language],
value: id,
}));
// To get stores
this.filterPannelObj.store_id.src = (await axios.get(
`/api/stores?limit=-1`
)).data.map(({ name, id }) => ({
label: JSON.parse(name)[this.$store.state.app.language],
value: id,
}));
},
methods: {
printScope(x) {
console.log('TCL: printScope -> x', x);
},
async getTableData(query) {
this.loading.tableData = true;
const responseData = await ResourceApi.list(query);
this.tableData = responseData.data;
this.paginationData = this.pick(
['current_page', 'last_page', 'per_page', 'total'],
responseData
);
Object.keys(this.paginationData).forEach(
key => (this.paginationData[key] = parseInt(this.paginationData[key]))
);
await this.handlePrerender();
this.loading.tableData = false;
},
async handlePrerender() {
this.fieldsToShow.forEach(async field => {
if (field.prerender) {
switch (field.type) {
case 'oneFrom': {
await this.setRelatedFieldName(field);
break;
}
case 'manyFrom': {
await this.setRelatedFieldName(field);
break;
}
case 'boolean': {
this.fieldData[field.name] = {};
this.tableData.forEach(
data => (this.fieldData[field.name][data.id] = data[field.name])
);
break;
}
case 'select': {
this.fieldData[field.name] = {};
this.tableData.forEach(
data => (this.fieldData[field.name][data.id] = data[field.name])
);
break;
}
}
}
});
},
// utils
pick(propsArr, srcObj) {
return Object.keys(srcObj).reduce((obj, k) => {
if (propsArr.includes(k)) {
obj[k] = srcObj[k];
}
return obj;
}, {});
},
// ? remember to refactor the parameter id
async setRelatedFieldName({
name,
type,
url,
foreignKey,
attrName,
multilang,
}) {
this.tableData.forEach(async data => {
if (type === 'oneFrom') {
data[name] = (await axios.get(`${url}${data[foreignKey]}`)).data[
attrName
];
if (multilang) {
data[name] = JSON.parse(data[name]);
}
} else if (type === 'manyFrom') {
data[name] = (await axios.get(`${url}${data[foreignKey]}`)).data.map(
idata => (multilang ? JSON.parse(idata[attrName]) : idata[attrName])
);
}
});
},
// Sort
async handleSortChange(change) {
this.navigation.sort = change.prop;
if (change.order === 'ascending') {
this.navigation['sort-order'] = 'asc';
} else if (change.order === 'descending') {
this.navigation['sort-order'] = 'desc';
}
await this.getTableData(this.navigation);
},
// Pagination
async handlePagination(obj) {
// obj page obj containing {page: ..., limit: ...}
this.navigation.page = obj.page;
this.navigation.limit = obj.limit;
await this.getTableData(this.navigation);
},
// Global Search
async handleGlobalSearch() {
await this.getTableData(this.navigation);
},
// ? Skipped for now
// Filters
async handleFilterVals(filterparams) {
console.log('TCL: handleFilterVals -> filterparams', filterparams);
this.navigation.filters = JSON.stringify(filterparams.filters);
this.navigation.sort = filterparams.sort.field;
this.navigation.sort = 'name';
this.navigation['sort-order'] = filterparams.sort.asc ? 'asc' : 'desc';
await this.getTableData(this.navigation);
},
async handleDeleteClick(id) {
ResourceApi.destroy(id)
.then(res => {
this.$message.success('Delete Successfully');
this.getTableData({ page: this.paginationData.current_page });
})
.error(err => {
this.$message.error(err);
});
},
// Selection methods
handleSelectionChange(selection) {
this.selected = selection;
},
getRowKeys(row) {
return row.id;
},
// Multiple Delete
handleMultipleDelete() {
axios
.delete(
`/api/${this.resourceName}/delete-multiple?ids=${this.selected
.map(item => item.id)
.join(',')}`
)
.then(async () => {
this.$message.success('Records deleted successfully');
await this.getTableData({ page: this.paginationData.current_page });
if (this.tableData.length === 0) {
await this.getTableData({
page: this.paginationData.current_page,
});
}
this.$refs.table.clearSelection();
})
.catch();
},
// Import Events
handleImportSucces() {
this.$message.success('New Data Imported');
this.getTableData({});
},
handleImportError(err) {
this.$message.error('There were some errors. CHK console');
console.log(err);
},
},
};
</script>
<style lang="scss" scoped>
</style>
I guess this is a reactive issue. The value of dict and actually not affecting the feildData dict.
changes -
this.fieldData[field.name] = {};
replace this with
self.$set(self.fieldData,field.name,{});
and
this.fieldData[field.name][data.id] = data[field.name] // to
//Replace
self.$set(self.fieldData[field.name],data.id, data[field.name]);
I think this will fix the issue.Instead of using = use $set for value assignment.
Codepen - https://codepen.io/Pratik__007/pen/gObGOKx

Categories

Resources