how to retrieve and manipulate data from Vuex store - vuejs2

I try to make door configurator using Vue.
First of all I put array of components from DB into Vuex store .get('/api/furnitura')
This gives me array with all components available for different type of doors.
Hinges, handles, locks etc.
And then I am going to have few Vue components. Each component is one configurator of specific door type.
What I found in Vue documentation is that it is possible to access Vuex store variables from each point of the application like that
computed: {
getFurnitura(){
return this.$store.state.furnitura.all
}
},
But I want to manipulate store variables in Vue.
Something like that
Take all from this.$store.state.furnitura.all where type=SOMETYPE and type=ANOTHERTYPE
For one calculator I want retrieve from this.$store.state.furnitura.all only few type of hinges and handles. For another something different.
Then I want to use them in select fields. For example in DB I have about 50 hinges in DB and for one type of door I need to retrieve only 5, for another only 3 and so on.
Having API call for every select field seems not reasonable for me
<select name="hinge_selected" v-model="hinge_selected">
<option v-for="option in hinges" :value="option">
{{ option.name }}
</option>
</select>
<select name="handle_selected" v-model="handle_selected">
<option v-for="option in handles" :value="option">
{{ option.name }}
</option>
</select>
and so on
Here is my code now
resources\js\app.js
window.axios = require('axios');
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
window.Vue = require('vue');
import store from './store';
Vue.component('modalform', require('./components/modalform.vue').default);
Vue.component('calc-dush-door', require('./components/calculators/dush-door.vue').default);
Vue.component('calc-interior-door', require('./components/calculators/interior-door.vue').default);
Vue.component('calc-sliding-door', require('./components/calculators/sliding-door.vue').default);
const app = new Vue({
el: '#app',
store,
data: { },
directives: { },
computed: {
getFurnitura(){
return this.$store.state.furnitura.all
}
},
methods: { },
mounted() {
console.log("Vue ROOT instance mounted");
this.$store.dispatch('furnitura/getFurnitura');
}
});
resources\js\store\index.js
import Vue from 'vue';
import Vuex from 'vuex';
import furnitura from './modules/glass';
import furnitura from './modules/furnitura';
Vue.use(Vuex);
export default new Vuex.Store({
modules: {
glass,
furnitura
}
});
resources\js\store\modules\furnitura.js
import axios from 'axios';
const state = {
all: []
};
const getters = { };
const mutations = {
SET_FURNITURA (state, furnitura) {
state.all = furnitura;
}
};
const actions = {
getFurnitura (context) {
axios
.get('/api/furnitura') // this gives me result of Furnitura::all();
.then(response => {
context.commit('SET_FURNITURA', response.data.records)
});
}
};
export default {
namespaced: true,
state,
getters,
mutations,
actions
};
resources\js\store\components\calculators\sliding-door.vue
<template>
<div>
<select name="hinge_selected" v-model="hinge_selected">
<option v-for="option in hinges" :value="option">
{{ option.name }}
</option>
</select>
<select name="handle_selected" v-model="handle_selected">
<option v-for="option in handles" :value="option">
{{ option.name }}
</option>
</select>
</div>
</template>
<script>
export default {
name: "SlidingDoorCalc",
data: function () {
return {
width: 600,
height: 2000,
hinge_selected: [],
handle_selected: [],
}
},
computed: {
getFurnitura(){
return this.$store.state.furnitura.all
}
},
methods: { },
mounted() { },
}
</script>

You can use regular component methods, as in:
{
// ...
methods: {
getHinges(filterParameter1, filterParameter2) {
// do your filtering and sorting on
// this.$store.state.furnitura.all
},
getHandles(filterParameter1) {
// same as with getHinges
}
}
}
Then, in your component, you call it as:
<select name="hinge_selected" v-model="hinge_selected">
<option v-for="option in getHinges('some_parameter')" :value="option">
{{ option.name }}
</option>
</select>
If you want to use the same filtering logic in multiple methods, use method-style-access Vuex getters, as in:
getters: {
// ...
getHinges: (state) => (filterParameter1, filterParameter2) => {
// filter your state.furnitura.all here
}
}
and call such getters in your component methods:
{
// ...
methods: {
getHinges(filterParameter1, filterParameter2) {
return this.$store.getters.getHinges(filterParameter1, filterParameter2);
},
}
}

Related

Is it possible to pass 2 requests to the vuex store?

From the backend, 2 APIs come to me: one for cities, the other for the product category list. I call the axios method in the store where I change my state and show them in the component. But for some reason, only one method is executed (the city call method from the api), and the category list is not. As far as I know, the action is asynchronous. Provided code below:
Questionnaire.vue
<select
type="choseCountry"
class="questionnaire__questions-input questionnaire__input"
v-model="form.countryID"
>
<option value="">Выберите страну</option>
<option v-for="country in COUNTRIES" :key="country.id" :value="country.id">{{country.name}}</option>
</select>
<select type="text" class="questionnaire__questions-input questionnaire__input">
<option value="">Выберите город</option>
<option v-for="city in cities" :key="city.id" :value="form.cityID">{{city.name}}</option>
</select>
<select class="questionnaire__input" type="text">
<option value="">Категория продуктов</option>
<option v-for="category in PRODUCT_CATEGORIES" :key="category.id" :value="form.category.id">{{category.name}}</option>
</select>
index.js:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
countries: [],
productCategories: []
},
getters: {
COUNTRIES(state){
return state.countries
},
PRODUCT_CATEGORIES(state){
return state.productCategories
}
},
mutations: {
SET_COUNTRIES_TO_STATE: (state, countries) => {
state.countries = countries
},
SET_PRODUCT_CATEGORIES_TO_STATE: (state, productCategories) => {
state.productCategories = productCategories
},
},
actions: {
GET_COUNTRIES_FROM_API({commit}) {
return axios('http://127.0.0.1:8000/api/v1/borrower/countries/', {
method: "GET"
})
.then((countries) => {
commit('SET_COUNTRIES_TO_STATE', countries.data)
return countries
})
},
GET_PRODUCT_CATEGORIES_FROM_API({commit}) {
return axios('http://127.0.0.1:8000/api/v1/borrower/category/', {
method: "GET"
})
.then((productCategories) => {
commit('SET_PRODUCT_CATEGORIES_TO_STATE', productCategories.data)
return productCategories
})
}
},
modules: {
//countries
}
})
Only one get request is visible in the terminal:
Do I need to divide it all into modules to make it all work? What could be the problem?

Vue 3 : issue in getting onchange event from child component

I made a component to provide a select including a button that reset the select to the initial state (no option selected).
I get well the onchange event in parent when an option is selected but nothing when the reset button is clicked, although the select is reset. In my use case the list is still filtered even when nothing is selected
Here is the parent file :
<template>
<liste-filter
:nom="'marque'"
:label="'Choose a make'"
:liste="marques"
v-model="selMarque"
#change="changefilter"></liste-filter>
<h1/>
<div v-for="vh in vehicules" :key="vh.lib" class="w-full">
{{ vh.lib }}
</div>
<div v-if="vehicules.length === 0">The list is empty</div>
</template>
<script>
import ListeFilter from "./ListeFilter.vue"
export default {
components: {
ListeFilter
},
data() {
return {
marques:[{id:"Renault",libelle:"Renault"},{id:"Peugeot",libelle:"Peugeot"}],
selMarque: "",
vehicules: [],
tabData:[{"lib":"Renault","modeles":[{"lib":"Clio"},{"lib":"Captur"}]},{"lib":"Peugeot","modeles":[{"lib":"208"},{"lib":"308"},{"lib":"3008"}]}]
};
},
methods: {
changefilter() {
this.vehicules = [];
for (var i = 0; i < this.tabData.length; i++) {
if(this.selMarque != "" && this.tabData[i].lib == this.selMarque) {
this.vehicules = this.tabData[i].modeles;
}
}
}
}
}
</script>
And here is the component file ListeFilter.vue :
<template>
<span class="relative">
<select
:name="nom"
:id="nom"
:label="label"
v-model="sel"
class="w-64 text-sm"
>
<option disabled value="">{{ label }}</option>
<option v-for="elem in liste" :value="elem.id" :key="elem.id">
{{ elem.libelle }}
</option>
</select>
<button
v-show="sel"
#click="reset"
>X</button>
</span>
</template>
<script>
export default {
props: ["nom", "label", "liste", "modelValue"],
emits: ["update:modelValue"],
computed: {
sel: {
get() {
return this.modelValue;
},
set(value) {
this.$emit("update:modelValue", value);
},
},
},
methods: {
reset() {
this.sel = ""
},
},
};
</script>
I also made a Vue SFC Playground to test HERE
Thanks in advance for your help.
PS : if you know a component that does the same I take it ! (vue-select does not seem Vue 3 compliant)
I think you should be listening for the #update:modelValue="changefilter" event instead of the change event #change="changefilter".
It's documented here. https://v3-migration.vuejs.org/breaking-changes/v-model.html#overview
Try changing your App.vue to this.
Here is a Playground
<template>
<liste-filter
:nom="'marque'"
:label="'Choose a mark'"
:liste="marques"
v-model="selMarque"
#update:modelValue="changefilter"
>
</liste-filter>
<h1/>
<div v-for="vh in vehicules" :key="vh.lib" class="w-full">
{{ vh.lib }}
</div>
<div v-if="vehicules.length === 0">The list is empty</div>
</template>
<script>
import ListeFilter from "./ListeFilter.vue"
export default {
components: {
ListeFilter
},
data() {
return {
marques:[{id:"Renault",libelle:"Renault"},{id:"Peugeot",libelle:"Peugeot"}],
selMarque: "",
vehicules: [],
tabData:[{"lib":"Renault","modeles":[{"lib":"Clio"},{"lib":"Captur"}]},{"lib":"Peugeot","modeles":[{"lib":"208"},{"lib":"308"},{"lib":"3008"}]}]
};
},
methods: {
changefilter() {
console.log('change happened');
this.vehicules = [];
for (var i = 0; i < this.tabData.length; i++) {
if(this.selMarque != "" && this.tabData[i].lib == this.selMarque) {
this.vehicules = this.tabData[i].modeles;
}
}
}
}
}
</script>

Save selected values of input despite switching between two components in VUEJS

So I have two components that are imported into my app.vue:
<script>
import Leaderboard from "./components/Comp1.vue";
import Search from "./components/Comp2.vue";
export default {
name: "App",
components: {
Comp1,
Comp2,
},
}
These components are called, when I click on the corresponding button. This all works fine.
But in the components I have some input fields such as in Comp1.vue:
<template>
<div>
<select
class="form-select"
name="event"
id=""
v-model="selectedEvent"
>
<option value="">Please choose an event:</option>
<option v-for="event in eventsList" :key="event">
{{ event }}
</option>
</select>
</div>
</template>
<script>
data: function () {
return {
selectedEvent: "",
</script>
Here I can choose, which event to watch. But after switching to Comp2 and then again choosing Comp1, the selectedEvent is empty. Obviously, because its defined empty in data.
Is there any way to store the selected value in a session variable or would you prefer a different technique?
UI looks like this:
You can maintain an Object in your parent which you can pass as props to a props and then have a two way handshake
<Leaderboard :formInputs="formInputs"></Leaderboard>
<script>
import Leaderboard from "./components/Comp1.vue";
import Search from "./components/Comp2.vue";
export default {
name: "App",
components: {
Comp1,
Comp2,
},
data() {
return {
formInputs: {
compOneInput: '',
compTwpInput: ''
}
},
methods: {
updateData(payload) {
this.formInputs[payload.key] = payload.value;
}
}
and then pass this formInputs to your child Component from where you
you can emit the change whenever you update the input inside that
<template>
<div>
<select
class="form-select"
name="event"
id=""
v-model="selectedEvent"
>
<option value="">Please choose an event:</option>
<option v-for="event in eventsList" :key="event">
{{ event }}
</option>
</select>
</div>
</template>
<script>
export default {
data: function () {
return {
selectedEvent: this.formInputs.compOneInput ? this.formInputs.compOneInput : '',
}
},
watch: {
formInputs(newVal) {
this.selectedEvent = newVal.compOneInput;
},
selectedEvent(newVal, oldVal) {
if(newVal !== oldVal) {
this.$emit('updateData', {key: compOneInput, value: this.selectedEvent});
}
}
}
props: {
formInputs: Object
}
}
</script>
Using the above example for component one , you can implement the same for component two also
you can add a watcher on selectedEvent then store the data in vuex store

How to update data from vue-tables-2 after action from Template?

I'm using a custom component as a column on vue-tables-2, to do that I'm using a vue-component as described here: vue-components
I've created a button that opens a modal to the user confirm some information, and after that I make a request to the backend and the record is changed on the database.
Now I want to refresh the data on the table, but I don't know how to do that. The documentation said about using the $ref, but this is not an option because my component is not the parent.
How can I do that?
Links to the code:
Component using 'vue-tables-2'
<template>
<div>
<div id="payment">
<input type="checkbox" v-model="onlyPending" #change="filterPay()">Apenas pendentes</input>
<v-server-table url="/api/payments" :columns="columns" :options="options" ></v-server-table>
</div>
</div>
</template>
<script>
import pay from './ModalConfirmPay.vue'
import {Event} from 'vue-tables-2';
export default {
name: "AeraListPayment",
props: ['groupId'],
data: function(){
let groupId = this.groupId;
return {
columns: ['name','value','course','due_date','paid','installment','pay'],
options: {
responseAdapter : function(data) {
data.data = data.data.map(payment => {
payment.paid = payment.paid ? "pago" : "pendente";
return payment;
})
return data;
},
headings: {
installment: 'Parcela',
paid: 'Status',
value: 'Valor',
due_date: 'Vencimento',
pay: 'Ação',
course: 'Curso',
name: 'Nome'
},
templates : {
pay
},
customFilters: ['onlyPending','groupId'],
initFilters:{groupId:groupId,onlyPending:true}
},
onlyPending: true
}
},
methods: {
filterPay(){
Event.$emit('vue-tables.filter::onlyPending', this.onlyPending);
}
}
}
</script>
Component that is being used as a custom column:
<template>
<div>
<button #click.prevent="show">Pagar</button>
<modal :name="modalName">
<p>Confirma o pagamento de {{data.value}} ?</p>
<p>Parcela: {{data.installment}}</p>
<p>Vecimento: {{data.due_date}}</p>
<button #click.prevent="pay">Confirmar</button>
<button #click.prevent="hide">Cancelar</button>
</modal>
</div>
</template>
<script>
import PaymentService from '../../services/PaymentService'
let service = new PaymentService();
export default {
name:"ModalConfirmPay",
props: ["data"],
computed: {
modalName: function () {
// `this` aponta para a instância Vue da variável `vm`
return `confirm-pay-${this.data.clientGroup_id}-${this.data.installment}`
}
},
methods: {
show () {
this.$modal.show(this.modalName);
},
pay ( ) {
service.pay(this.data)
.then(this.hide());
},
hide () {
this.$modal.hide(this.modalName);
}
}
}
</script>
First, defined an EventBus if you don't have
EventBus.vue
import Vue from 'vue'
export default new Vue()
In ListPayment.vue, import EventBus and listen for refresh-table event. Note that I add ref="table" to vue-tables-2 element
<template>
<v-server-table ref="table" ... />
</template>
<script>
import EventBus from './EventBus.vue'
export default {
mounted() {
EventBus.$on('refresh-table', this.refreshTable)
},
beforeDestroy() {
EventBus.$off('refresh-table', this.refreshTable)
},
methods: {
refreshTable() {
this.$refs.table.refresh();
}
}
}
</script>
Finally, emit event in modal
pay() {
service.pay(this.data)
.then(() => {
EventBus.$emit('refresh-table')
})
.then(this.hide());
}

How to get variable value from a component and assign it to a variable in another component in vuejs

This is my code, i'm not using webpack, just the vuejs cdn.I want to get the value of the districts in the regionComponent to the another variable in the districtComponent and display it.
const regionsComponent = Vue.component('region-component', {
template: `
<div class="col-lg-6">
<div class="form-group" >
<label class="label-text" for="officeRegion">Region</label> <span class="red">*</span>
<select class="form-control" id="officeRegion"
name="officeRegion" :value = "value" v-on:input ="updateRegion($event.target.value)">
<option value='' >Select region</option>
<option v-for="region in regions" :value="region.regionId">{{region.region}}</option>
</select>
</div>
</div>`,
data() {
return {
regions:[],
}
},
created(){
this.getRegions();
},
props:['value','districts'],
methods: {
getRegions: function() {
let apiurl = document.getElementById("apiurl").value;
let apikey = document.getElementById("apikey").value;
let headers = {
'Content-Type': 'application/json',
'apiKey': apikey
}
axios.get(apiurl+'regions/all', {headers: headers})
.then((res)=>{
console.log(res.data.data[0].region)
if(res.data.responseCode === "01"){
this.regions = res.data.data;
} else {
console.log("failed to load regions")
}
})
},
updateRegion: function(value){
this.$emit('input', value);
// console.log(value)
if(value){
thisdistricts = this.getRegionDistrict(value)
}
},
getRegionDistrict: function (regionId){
let apiurl = document.getElementById("apiurl").value;
let apikey = document.getElementById("apikey").value;
let headers = {
'Content-Type': 'application/json',
'apiKey': apikey
}
axios.get(apiurl+'region/districts/all?regionId='+ regionId, {headers: headers})
.then((res)=>{
console.log(res.data)
if(res.data.responseCode==="01"){
return this.districts = res.data.data
// console.log(this.districts)
}
})
}
}
})
const districtComponent = Vue.component('district-component', {
template:`
<div class="col-lg-6">
<div class="form-group">
<label class="label-text" for="officeDistrict">District</label> <span class="red">*</span>
<select class="form-control" id="officeRegion" name="officeRegion" v-bind="district" v-on:input= "updateDisct($event.target.value)">
<option value='' >Select district</option>
<option v-for="district in districts" :value="district.districtId">{{district.districtName}}</option>
</select>
</div>
</div>`,
props:['district'],
data() {
return {
districts:[],
}
},
methods: {
updateDisct: function(district){
this.$emit('input', district);
console.log(district)
}
}
})
var app = new Vue({
el: '#myForm',
components: {
vuejsDatepicker,
regionsComponent,
},
data: function (){
return {}
}
})
If Vuex is too complicated for now, you can use the native $root when you need a simple way.
I use Vuex with name spaced modules it is brilliant but it is a large learning curve and the documentation is not really that great on it.
To leverage $root you could move regions to to the data attribute of your new Vue({}) instance and reference that from anywhere using this.$root.regions in script or $root.regions in template.
var app = new Vue({
el: '#myForm',
components: {
vuejsDatepicker,
regionsComponent,
},
data: function (){
return {
regions: []
}
}
})
getRegions: function() {
...
axios.get(apiurl+'regions/all', {headers: headers}).then((res)=>{
if(res.data.responseCode === "01"){
this.$root.regions = res.data.data;
} else {
console.log("failed to load regions")
}
})
},
<select ... >
<option value='' >Select region</option>
<option v-for="region in $root.regions" :value="region.regionId">{{region.region}}</option>
</select>
This way any component at any nested level can access a global data set without any sharing faff.
this.$set($root.myGlobal, newValue); can also help out with any
reactivity issues in some cases.
You can use Vuex and have a global store to store your data
With Vuex you can share data/complex state management across components. A Vuex store instance is composed of state, getters, actions, and mutations. In summary, the state "holds" your data, you can retrieve it via your state or the getters. Actions can be async and for example can hold an API call, that later on, you can pass the data from the API call to a mutation that ultimately make the change effectively, mutations cannot be async.
See: The simplest store
All the operations made with Vuex have a trace of what is happening inside your app. It seems a little daunting at first, but actually, it is really easy to manage your data.