Pinia | Vue3 - Accessing array in deep object - javascript

In Pinia (base.js)
export const useBaseStore = defineStore('base', {
state: () => {
return {
areaLocal: [
{ id: 0, name: 'LAX' },
{ id: 1, name: 'SFO' },
{ id: 2, name: 'SAN' },
],
areaLocal2: {
area: [
{ id: 0, name: 'AAA'},
{ id: 1, name: 'BBB'},
{ id: 2, name: 'CCC'},
]},
}
},
getters: {
getAreaById: (state) => {
return (areaId) =>
state.areaLocal.find((areaLocal) => areaLocal.id === areaId)
},..
in Vue (area.vue)
<script setup>
import { computed, ref } from 'vue'
import { storeToRefs } from 'pinia'
import { useBaseStore } from '../stores/base'
const areaStore = useBaseStore()
const { getAreaById } = storeToRefs(areaStore)
const areaIndex = ref(0)
</script>
<template>
<div class="container">
<h2>AREA: {{ getAreaById(areaIndex).name }}</h2>
....
This, areaLocal, I get "AREA: LAX", which is correct,
but HOW I can write "getter" in the pinia for access to "areaLocal2", the object "area" then the array?
state.areaLocal2.area.find((areaLocal2.area) => areaLocal.id.area === areaId
The above throws an undefined error.

getArea2ById: (state) => (areaIa) => state.areaLocal2.area.find((area) => area.id === areaId)
You are already in area by the time you hit the find higher order function so no need to reference it again inside of it.

Related

REACTJ - convert class component to functionnal component

i'm new to react and i'm trying to convert this class based component to a functionnal component but i get an error of state, how can i convert it please ?
This is my components :)
sandbox link
Thank you
In this example, it is quite straight forward as there are no component life cycle methods. You can just define all the methods in the function, and return the component.
function Demo() {
const [state, setState] = React.useState({
expandedKeys: [],
autoExpandParent: true,
checkedKeys: [],
allCheckedKeys: [],
selectedKeys: [],
newTreeView: false,
newTreeData: []
});
const onExpand = (expandedKeys) => {
console.log("onExpand", expandedKeys);
// if not set autoExpandParent to false, if children expanded, parent can not collapse.
// or, you can remove all expanded children keys.
setState({
...state,
expandedKeys,
autoExpandParent: false
});
};
const onCheck = (checkedKeys, e) => {
const allCheckedKeys = [...checkedKeys, ...e.halfCheckedKeys];
console.log("onCheck", allCheckedKeys);
console.log(createNewTreeData(treeData, allCheckedKeys));
setState((prevState) => ({
...prevState,
allCheckedKeys,
checkedKeys
}));
};
const onSelect = (selectedKeys, info) => {
console.log("onSelect", info);
setState({ ...state, selectedKeys });
};
const renderTreeNodes = (data) =>
data.map((item) => {
if (item.children) {
return (
<TreeNode title={item.title} key={item.key} dataRef={item}>
{renderTreeNodes(item.children)}
</TreeNode>
);
}
return <TreeNode {...item} />;
});
const createTree = () => {
setState((prevState) => ({
...prevState,
newTreeView: true,
newTreeData: createNewTreeData(treeData, prevState.allCheckedKeys)
}));
};
return (
<>
<Tree
checkable
onExpand={onExpand}
expandedKeys={state.expandedKeys}
autoExpandParent={state.autoExpandParent}
onCheck={onCheck}
checkedKeys={state.checkedKeys}
onSelect={onSelect}
selectedKeys={state.selectedKeys}
>
{renderTreeNodes(treeData)}
</Tree>
<button onClick={createTree}>Validate</button>
{state.newTreeView && <Tree>{renderTreeNodes(state.newTreeData)}</Tree>}
</>
);
}
import React, { useState } from 'react';
import ReactDOM from 'react-dom';
import 'antd/dist/antd.css';
import './index.css';
import { Tree } from 'antd';
const treeData = [
{
title: '0-0',
key: '0-0',
children: [
{
title: '0-0-0',
key: '0-0-0',
children: [
{
title: '0-0-0-0',
key: '0-0-0-0',
},
{
title: '0-0-0-1',
key: '0-0-0-1',
},
{
title: '0-0-0-2',
key: '0-0-0-2',
},
],
},
{
title: '0-0-1',
key: '0-0-1',
children: [
{
title: '0-0-1-0',
key: '0-0-1-0',
},
{
title: '0-0-1-1',
key: '0-0-1-1',
},
{
title: '0-0-1-2',
key: '0-0-1-2',
},
],
},
{
title: '0-0-2',
key: '0-0-2',
},
],
},
{
title: '0-1',
key: '0-1',
children: [
{
title: '0-1-0-0',
key: '0-1-0-0',
},
{
title: '0-1-0-1',
key: '0-1-0-1',
},
{
title: '0-1-0-2',
key: '0-1-0-2',
},
],
},
{
title: '0-2',
key: '0-2',
},
];
const Demo = () => {
const [expandedKeys, setExpandedKeys] = useState(['0-0-0', '0-0-1']);
const [checkedKeys, setCheckedKeys] = useState(['0-0-0']);
const [selectedKeys, setSelectedKeys] = useState([]);
const [autoExpandParent, setAutoExpandParent] = useState(true);
const onExpand = (expandedKeysValue) => {
console.log('onExpand', expandedKeysValue); // if not set autoExpandParent to false, if children expanded, parent can not collapse.
// or, you can remove all expanded children keys.
setExpandedKeys(expandedKeysValue);
setAutoExpandParent(false);
};
const onCheck = (checkedKeysValue) => {
console.log('onCheck', checkedKeysValue);
setCheckedKeys(checkedKeysValue);
};
const onSelect = (selectedKeysValue, info) => {
console.log('onSelect', info);
setSelectedKeys(selectedKeysValue);
};
return (
<Tree
checkable
onExpand={onExpand}
expandedKeys={expandedKeys}
autoExpandParent={autoExpandParent}
onCheck={onCheck}
checkedKeys={checkedKeys}
onSelect={onSelect}
selectedKeys={selectedKeys}
treeData={treeData}
/>
);
};
ReactDOM.render(<Demo />, document.getElementById('container'));
I have updated the code using ES6 arrow functions resulting in shorter and simpler code than traditional functional components.
import React,{useState} from "react";
import ReactDOM from "react-dom";
import "antd/dist/antd.css";
import "./index.css";
import { Tree } from "antd";
const { TreeNode } = Tree;
const treeData = [
{
title: "0-0",
key: "0-0",
children: [
{
title: "0-0-0",
key: "0-0-0",
children: [
{ title: "0-0-0-0", key: "0-0-0-0" },
{ title: "0-0-0-1", key: "0-0-0-1" },
{ title: "0-0-0-2", key: "0-0-0-2" }
]
},
{
title: "0-0-1",
key: "0-0-1",
children: [
{ title: "0-0-1-0", key: "0-0-1-0" },
{ title: "0-0-1-1", key: "0-0-1-1" },
{ title: "0-0-1-2", key: "0-0-1-2" }
]
},
{
title: "0-0-2",
key: "0-0-2"
}
]
},
{
title: "0-1",
key: "0-1",
children: [
{ title: "0-1-0-0", key: "0-1-0-0" },
{ title: "0-1-0-1", key: "0-1-0-1" },
{ title: "0-1-0-2", key: "0-1-0-2" }
]
},
{
title: "0-2",
key: "0-2"
}
];
const createNewTreeData = (treeData, checkedKeys) => {
return treeData.reduce((acc, treeDataItem) => {
if (checkedKeys.includes(treeDataItem.key)) {
if (treeDataItem.children) {
acc.push({
...treeDataItem,
children: createNewTreeData(treeDataItem.children, checkedKeys)
});
} else {
acc.push(treeDataItem);
}
}
return acc;
}, []);
};
const Demo =()=> {
const [state,setState] = useState({
expandedKeys: [],
autoExpandParent: true,
checkedKeys: [],
allCheckedKeys: [],
selectedKeys: [],
newTreeView: false,
newTreeData: []
});
const onExpand = (expandedKeys) => {
console.log("onExpand", expandedKeys);
// if not set autoExpandParent to false, if children expanded, parent can not collapse.
// or, you can remove all expanded children keys.
setState({
expandedKeys,
autoExpandParent: false
});
};
const onCheck = (checkedKeys, e) => {
const allCheckedKeys = [...checkedKeys, ...e.halfCheckedKeys];
console.log("onCheck", allCheckedKeys);
console.log(createNewTreeData(treeData, allCheckedKeys));
setState((prevState) => ({
...prevState,
allCheckedKeys,
checkedKeys
}));
};
const onSelect = (selectedKeys, info) => {
console.log("onSelect", info);
setState({ selectedKeys });
};
const renderTreeNodes = (data) =>
data.map((item) => {
if (item.children) {
return (
<TreeNode title={item.title} key={item.key} dataRef={item}>
{renderTreeNodes(item.children)}
</TreeNode>
);
}
return <TreeNode {...item} />;
});
const createTree = () => {
setState((prevState) => ({
...prevState,
newTreeView: true,
newTreeData: createNewTreeData(treeData, prevState.allCheckedKeys)
}));
};
return (
<>
<Tree
checkable
onExpand={onExpand}
expandedKeys={state.expandedKeys}
autoExpandParent={state.autoExpandParent}
onCheck={onCheck}
checkedKeys={state.checkedKeys}
onSelect={onSelect}
selectedKeys={state.selectedKeys}
>
{renderTreeNodes(treeData)}
</Tree>
<button onClick={createTree}>Validate</button>
{state.newTreeView && (
<Tree>{renderTreeNodes(state.newTreeData)}</Tree>
)}
</>
);
}
ReactDOM.render(<Demo />, document.getElementById("container"));

How to call array values dynamically from an existing array in Vuejs?

HelloWorld.vue
export const datalist = [
{ id: 1, val: "11", kk: "potter" },
{ id: 2, val: "22", kk: "james" },
{ id: 3, val: "55", kk: "limda" },
{ id: 4, val: "77", kk: "stepen" }
];
<template>
<div>
<b>Vuejs dynamic routing</b>
<div v-for="item in items" :key="item.id">
<b>{{ item.id }}.</b>
<router-link :to="{ name: 'UserWithID', params: { id: item.id } }">
{{ item.kk }}
</router-link>
</div>
<br /><br /><br />
<User />
</div>
</template>
<script>
import User from "./User.vue";
import { datalist } from "./datalist";
export default {
name: "HelloWorld",
components: {
User,
},
data() {
return {
items: datalist,
};
},
};
</script>
User.vue
import Vue from "vue";
import App from "./App.vue";
import VueRouter from "vue-router";
import HelloWorld from "./components/HelloWorld.vue";
import book from "./components/book.vue";
Vue.use(VueRouter);
const router = new VueRouter({
routes: [
{ path: "/", name: "User", component: HelloWorld },
{ path: "/", name: "BookWithID", component: book },
{ path: "/:id", name: "UserWithID", component: HelloWorld }
]
});
Vue.config.productionTip = false;
new Vue({
router,
render: (h) => h(App)
}).$mount("#app");
export const datalisttwo = [
{ id: 1, book: "steel", pen: "p1", gap: "1" },
{ id: 2, book: "iron", pen: "jp2", gap: "5" },
{ id: 3, book: "platinium", pen: "p3", gap: "2" },
{ id: 4, book: "gold", pen: "p4", gap: "9" }
];
<template>
<div>
<router-link :to="{ name: 'BookWithID' }">
{{ user.book }}
</router-link>
</div>
</template>
<script>
import { datalisttwo } from "./datalisttwo";
export default {
name: "User",
components: {},
data() {
return {
lists: datalisttwo,
};
},
computed: {
user: function () {
return this.lists.find((item) => item.id === this.$route.params.id);
},
},
};
</script>
As per the below code, in the datalisttwo.js I have array values like steel and pen Where i want to call both of them together like steel/pen as an api call in the mounted() .
When i click on the router-link, {{ user.book }} from User.vue component.
Ex:- I want to pass the pen/gap array values as query parameters. when clicked on {{ user.book }} from user.vue componet. Please go through codesandbox once, I tried adding computed property for pen and gap. But pen/gap --- but not calling dynamically
Here is my code:- https://codesandbox.io/s/new-hill-6yum4o?file=/src/components/User.vue
Your question and description is quite unclear, so I'll try to answer how I understand it. If that is not what you were expecting, try to explain it again clearly.
First, define your routes clearly. Here your have two routes pointing to '/'. Try to do it to have your user index at '/', your book at '/book/:id', and your user at 'user/:id'.
Second, I am unsure why you have your HelloWorld.vue in both User and UserWithId routes. If intended, disregard. If not, you should clean up that whole file to get the right route pointing to the right component.
Third, used on your example of potter, if you are looking at the book component, for which you haven't provided the code, you can do it such as:
...
computed: {
book() {
if (this.$route.params.id == null || this.$route.params.id == undefined) {
throw new Error('No book id provided')
}
return datalisttwo.find(_ => _.id == this.$route.params.id)
},
pen() {
this.book.pen
},
gap() {
this.book.gap
}
}
...
With this you'll be able to do whatever you whish with this.pen and this .gap.
Now, if you were to want to not import data list again, you can pass your retrieved pen & gap as query parameters: https://router.vuejs.org/api/
import Vue from "vue";
import App from "./App.vue";
import VueRouter from "vue-router";
import HelloWorld from "./components/HelloWorld.vue";
import book from "./components/book.vue";
Vue.use(VueRouter);
const router = new VueRouter({
routes: [
{ path: "/", name: "User", component: HelloWorld },
{ path: "/", name: "BookWithID", component: book },
{ path: "/:id", name: "UserWithID", component: HelloWorld }
]
});
Vue.config.productionTip = false;
new Vue({
router,
render: (h) => h(App)
}).$mount("#app");
export const datalisttwo = [
{ id: 1, book: "steel", pen: "p1", gap: "1" },
{ id: 2, book: "iron", pen: "jp2", gap: "5" },
{ id: 3, book: "platinium", pen: "p3", gap: "2" },
{ id: 4, book: "gold", pen: "p4", gap: "9" }
];
<template>
<div>
<router-link :to="{ name: 'BookWithID' }">
{{ user.book }}
</router-link>
</div>
</template>
<script>
import { datalisttwo } from "./datalisttwo";
export default {
name: "User",
components: {},
data() {
return {
lists: datalisttwo,
};
},
computed: {
user: function () {
return this.lists.find((item) => item.id === this.$route.params.id);
},
},
};
</script>
I ran this directly.
Error 1:
Imports must be at the top.
Look at 25, 0
Error 2:
Exports must be at the top
Look at 41, 8

How to fetch data with same name but in different interface in angular

I have two interface, one is cropFilter which is for checkbox filter and second interface is holding my data called Crop.
let me share my code for better understanding.
1. crop.model.ts
export class Crop { // Interface 1
name: string;
district: string
subCategory: Subcategory[];
}
export class Subcategory {
id: number;
name: string;
}
export class CropFilter { // Interface 2
name: string
checked: boolean
}
2. cropFilter.ts
import { CropFilter } from "./crop.model";
export const CROPSFILTER: CropFilter[] = [
{
name: "Rice",
checked: false
}, {
name: "Wheat",
checked: false
}, {
name: "Barley",
checked: false
}
]
The above interface is for checkbox filtration.
3. crop.data.ts
import { Crop } from "./crop.model";
export const CROPS: Crop[] = [
{
name: "Rice",
district: "Thane",
subCategory: [
{
id: 1,
name: "Basmati",
},
{
id: 2,
name: "Ammamore",
}
]
},
{
name: "Rice",
district: "Nashik",
subCategory: [
{
id: 1,
name: "Basmati",
},
{
id: 2,
name: "Ammamore",
}
]
},
{
name: "Wheat",
district: "Nashik",
subCategory: [
{
id: 1,
name: "Durum",
},
{
id: 2,
name: "Emmer",
}
]
},
{
name: "Barley",
district: "Ratnagiri",
subCategory: [
{
id: 1,
name: "Hulless Barley",
},
{
id: 2,
name: "Barley Flakes",
}
]
},
{
name: "Barley",
district: "Thane",
subCategory: [
{
id: 1,
name: "Hulless Barley",
},
{
id: 2,
name: "Barley Flakes",
}
]
}
];
This is the actual data. All I want to fetch data from crop.data.ts based on crop.filter.ts
for better clearance let me show you the html part as well :
1. all-trade.html
<div class="container" *ngIf="crops$ | async">
<div *ngFor="let item of cropFilterCheckbox$ | async; let i = index">
<mat-checkbox [checked]="item.checked" (change)="onChange($event, i, item)">
{{ item.name }}
</mat-checkbox>
</div>
<br />
<h4>JSON data:</h4>
<pre>
{{ cropFilterCheckbox$ | async | json }}
<div *ngFor="let crop of cropFilterCheckbox$ | async"
[hidden]="!crop.checked"
>{{ crop.name }}
</div>
<button type="button" class="btn">Basic</button>
</pre>
</div>
2. crop.service.ts
import { Injectable } from "#angular/core";
import { Observable, of } from "rxjs";
import { Crop, CropFilter, DistrictFilter } from "../shared/crop.model";
import { CROPS } from "../shared/crop.data";
import { CROPSFILTER } from '../shared/cropFilter';
#Injectable({
providedIn: "root"
})
export class CropService {
constructor() { }
crops: Crop[] = CROPS;
cropFilterCheckbox: CropFilter[] = CROPSFILTER;
getAllCrops(): Observable<Crop[]> {
return of(this.crops);
}
getCropFilter(): Observable<CropFilter[]> {
return of(this.cropFilterCheckbox)
}
getCrop(name: string): Observable<any> {
const crop = this.crops.filter(crop => crop.name === name)[0];
return of(crop);
}
}
The final output looks like this :
Now please guide me how to fetch data from crop.data.ts based on crop.filter.ts
Like when user check Rice checkbox, its should fetch all the details of Rice present in crop.data.ts file and display on the screen.
On checkbox change write an event handle like below. Maintain which are the checkbox user has checked in a variable "AppliedFilter" and then pass that array list to your service method.
onChange(status, name) {
if (status && this.appliedFilter.indexOf(name) === -1) {
this.appliedFilter.push(name);
} else {
this.appliedFilter = this.appliedFilter.filter((x) => x !== name);
}
this.crops$ = this.cropService.getCrop(this.appliedFilter);
}
In your service method based on that array filter your records like below.
getCrop(names: string[]): Observable<any> {
const crop = this.crops.filter((crop) => names.includes(crop.name));
return of(crop);
}
Here is the working sandbox.
https://codesandbox.io/s/filter-data-x2p0w?file=/src/app/app.component.ts:289-294

How to checkbox filtering in reactjs and handle state? and show the available item after the checkbox

I want to make a filter system using multiple checkbox. But when i checked one checkbox it filter the state but when i unchecked it how i can get back the all data in state . Also if i select multiple checkbox then it will filter from the filtered item.
Here is my code.
state = {
restaurant : [
{name: 'La mesa', cuisine: ['italian', 'indian']},
{name: 'Red Bull', cuisine: ['chiness', 'french']}
{name: 'Purnima', cuisine: ['thai', 'arabic']}
]
cuisine: [
{id: 1, name: 'italian'},
{id: 2, name: 'indian'},
{id: 3, name: 'chiness'}
{id: 4, name: 'french'},
{id: 4, name: 'arabic'},
]
}
handleCuisineFilter = (e) => {
if (e.target.checked) {
const filter =
this.state.restaurant.length &&
this.state.restaurant.filter((rest) => rest.cuisine.includes(e.target.value));
this.setState({ restaurant: filter });
} else {
Now when unchecked how i can get previous state???
}
};
render() {
return (
<div>
{this.state.cuisine.length && this.state.cuisine.map(
cuisine=> (<li>
<input
id={cuisine.id}
type='checkbox'
onChange={this.handleCuisineFilter}
name='check'
value={cuisine.name}
/>
{cuisine.name } {here will be count of number of restaurant}
</li>
))}
{this.state.restaurant.length && this.state.restaurant.map(rest=> <h5>rest.name</h5>)}
</div>
I tried to explain best via my code . Help me please. Thank you in advance
You have to keep track of checked state for each filter and then filter against all filters at once every time.
Here is the solution
EDIT
import React, { Component } from "react";
import "./App.css";
class App extends Component {
state = {
restaurant: [
{ name: "La mesa", cuisine: ["italian", "indian"] },
{ name: "Red Bull", cuisine: ["chiness", "french"] },
{ name: "Purnima", cuisine: ["thai", "arabic"] },
],
// maintain a checked state for each filter
cuisine: [
{ id: 1, name: "italian", checked: false },
{ id: 2, name: "indian", checked: false },
{ id: 3, name: "chiness", checked: false },
{ id: 4, name: "french", checked: false },
{ id: 5, name: "arabic", checked: false },
],
};
setFilter = (cuisine, flag) => {
this.setState((prevState) => ({
cuisine: prevState.cuisine.map((c) =>
// check state for the selected cuisine
c.id === cuisine.id ? { ...c, checked: flag } : c
),
}));
};
handleCuisineFilter = (e, cuisine) => {
if (e.target.checked) {
this.setFilter(cuisine, true);
} else {
this.setFilter(cuisine, false);
}
};
filterRestaurants = (restaurant) => {
const checkedFilters = this.state.cuisine.filter((c) => c.checked);
const noFiltersChecked = checkedFilters.length === 0;
if (noFiltersChecked) {
return true;
} else {
// EDITED:
const tmp = checkedFilters.reduce(
(hasRestaurantAllTheseCuisines, nextCuisine) =>
(hasRestaurantAllTheseCuisines =
hasRestaurantAllTheseCuisines &&
restaurant.cuisine.includes(nextCuisine.name)),
true
);
return tmp;
}
};
render() {
return (
<div>
{this.state.cuisine.length &&
this.state.cuisine.map((cuisine) => (
<li key={cuisine.id}>
<input
id={cuisine.id}
type="checkbox"
onChange={(e) => this.handleCuisineFilter(e, cuisine)}
name="check"
value={cuisine.name}
/>
{cuisine.name} {/* here will be count of number of restaurant */}
</li>
))}
{/* Use .filter() with cuisine state */}
{this.state.restaurant.length &&
this.state.restaurant
.filter(this.filterRestaurants)
.map((rest) => <h5 key={rest.name}>{rest.name}</h5>)}
</div>
);
}
}
export default App;
Edited the code. The only change was the filter check here
...
const tmp = checkedFilters.reduce(
(hasRestaurantAllTheseCuisines, nextCuisine) =>
(hasRestaurantAllTheseCuisines =
hasRestaurantAllTheseCuisines &&
restaurant.cuisine.includes(nextCuisine.name)),
true
);
...

Vuex: Calculate percentage of values in array of objects, and update the value in a component

I'm creating a voting app using Vuex.
There are buttons to vote for each dogs. Right now I'm successful for updating vote number by clicking the buttons:
store.js
export const store = new Vuex.Store({
state: {
dogs: [
{ id: 0, name: 'Dog1', vote: 0, percentage: 0 },
{ id: 1, name: 'Dog2', vote: 0, percentage: 0 },
{ id: 2, name: 'Dog3', vote: 0, percentage: 0 },
{ id: 3, name: 'Dog4', vote: 0, percentage: 0 },
{ id: 4, name: 'Dog5', vote: 0, percentage: 0 }
]
},
getters: {
dogs: state => {
return state.dogs;
},
},
mutations: {
vote: (state, payload) => {
const index = state.dogs.findIndex(dog => dog.id === payload);
state.dogs[index].vote++;
},
},
actions: {
voteAction(store, payload) {
store.commit('vote', payload)
},
}
})
Button.vue
<template>
<div v-for="(dog, index) in dogs" :key="index">
<button type="button" #click="vote(dog.id)">{{ dog.name }}</button>
</div>
</template>
<script>
import { mapGetters } from 'vuex';
import { mapMutations } from 'vuex';
export default {
computed: {
dogs() {
return this.$store.getters.dogs
}
},
methods: {
vote(id) {
this.$store.dispatch('voteAction', id);
},
}
}
</script>
Now I want to calculate the percentage of each dog's voting rate and update all the dog's percentage accordingly. I might could achieve this by:
Get total number of votes
Calculate each dog's vote percentage
Update all the percentages
I'm trying to implement this function in store.js methods but I don't know how to write the logic. And I'm planning to receive the updated percentages on Result.js
<template>
Result.vue
<div v-for="(dog, index) in dogs" :key="index">
<div
class="progress-bar"
role="progressbar"
:style="{width: dog.percentage + '%'}"
aria-valuenow="dog.percentage"
aria-valuemin="0"
aria-valuemax="100"
>{{ dog.name }} {{ dog.percentage }}% ({{ dog.vote }} votes)</div>
</div>
</template>
<script>
export default {
computed: {
dogs() {
return this.$store.getters.dogs
},
},
}
</script>
How do I get all updated percentages from store.js to Result.vue?
I would consider remove percentage from state data. it doesn't look that should belong to state since its value is derived from other state data values calculation. you could use getters for get a dog's percentage that like:
getters: {
dogs: state => {
return state.dogs;
},
percentage: (_state, getters) => dog => {
const totalVotes = getters.totalVotes;
return totalVotes ? (100 * dog.vote) / totalVotes : 0;
},
totalVotes: state =>
state.dogs.reduce((count, dog) => (count += dog.vote), 0)
}
now you would map percentage getter as well at vue component. Instead of calling dog.percentage you would call percentage(dog)
You have already resolved major part, please modify you store.js like below and check if it works.
Working example on netlify (css part removed)- https://dog-voting.netlify.app/
code on github- https://github.com/manojkmishra/dogvoting
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {dogs: [
{ id: 0, name: 'Dog1', vote: 0, percentage: 0 },
{ id: 1, name: 'Dog2', vote: 0, percentage: 0 },
{ id: 2, name: 'Dog3', vote: 0, percentage: 0 },
{ id: 3, name: 'Dog4', vote: 0, percentage: 0 },
{ id: 4, name: 'Dog5', vote: 0, percentage: 0 }
] , totalvotes:0,
},
getters: {
dogs: state => { return state.dogs; },
},
mutations: {vote: (state, payload) => {
const index = state.dogs.findIndex(dog => dog.id === payload);
state.dogs[index].vote++;
state.totalvotes++;
for (let j=0;j< state.dogs.length; j++)
{let per= (100 * state.dogs[j].vote)/state.totalvotes;
state.dogs[j].percentage=per;
}
},
},
actions: {voteAction(store, payload) {
store.commit('vote', payload)
},
},
modules: { }
})
I would include my store.js data in Result.vue like this
<template>
<div v-for="(dog, index) in allDogs" :key="index">
<div
class="progress-bar"
role="progressbar"
:style="{width: dog.percentage + '%'}"
aria-valuenow="dog.percentage"
aria-valuemin="0"
aria-valuemax="100"
>{{ dog.name }} {{ dog.percentage }}% ({{ dog.vote }} votes)
</div>
</div>
</template>
<script>
import { mapGetters } from "vuex";
export default {
computed: {
...mapGetters({
allDogs: "dogs"
})
},
}
</script>

Categories