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?
Related
How are you?
I'm studying Vue and I'm stuck on the current task not knowing where to go.
I have a select that when I click I need to show on screen only what corresponds to that selection. For example, when placing the "to do" option in the select, only the tasks with a concluded=false should appear on the screen. I've only gotten this far and I need help to continue. Can you help me? Thanks
This is my App.vue
<template>
<div id="app">
<h1>Lista de Tarefas</h1>
<List :data="list" #remove="handleRemove"/>
<Form #add="addNewTask" #onChange="handleN"/>
</div>
</template>
<script>
import List from "./components/List.vue";
import Form from "./components/Form.vue";
export default {
components: {
List,
Form,
},
data() {
return {
list: [],
};
},
methods: {
addNewTask(newTask) {
this.list.push(newTask);
},
handleRemove(item) {
const index = this.list.findIndex(i => i.id === item.id)
this.list[index].excluded = true
},
handleN(item) {
const index = this.list.findIndex(i => i.id === item.id)
this.list[index].concluded = true
}
},
};
</script>
This is my List.vue
<template>
<ul>
<select v-model="selected" #change="onChange($event)">
<option disabled value="">Escolha a visualização</option>
<option v-for="option in options" :key="option.text">
{{ option.text }}
</option>
</select>
<li v-for="item in itens" :key="item.id">
<input type="checkbox" id="checkbox" v-model="item.concluded" />
<label for="checkbox"> {{ item.description }} </label>
<button #click="() => $emit('remove', item)">Excluir</button>
</li>
</ul>
</template>
<script>
export default {
props: {
data: {
type: Array,
default: () => {},
},
},
data() {
return {
selected: "",
options: [
{ text: "Todos", value: "1" },
{ text: "A fazer", value: "2" },
{ text: "Concluído", value: "3" },
{ text: "Deletado", value: "4" },
],
};
},
computed: {
itens() {
return this.data.filter((item) => item.excluded === false);
},
},
methods: {
onChange(event) {
console.log(event.target.value);
return this.data.filter((item) => item.concluded === false);
},
},
};
</script>
This is my Form.vue
<template>
<form #submit.prevent="handleNewTask">
<input type="text" v-model="newTask" placeholder="Insira a tarefa"/>
<input type="submit" value="Adicionar"/>
</form>
</template>
<script>
import Task from '../types/Task.js'
export default {
data() {
return {
newTask: "",
};
},
methods: {
handleNewTask() {
this.$emit('add', new Task(this.newTask))
this.newTask = ''
}
},
};
</script>
And this is my Task.js
export default class {
constructor(description) {
this.description = description,
this.id = Math.random(),
this.concluded = false,
this.excluded = false
}
}
I watch some tutorials, read the documentation and some StackOverflow questions but I really can't get out of here
Thanks in advance for the help
Based on how you have structured your app, our only concern should be with the List.vue file.
Your goal is to filter the results based on the selection (selected property). However, your issue is that you are not even using that anywhere.
I know you are hard coding the filter on the onChange method but that is, first of all wrong because you aren't really changing anything (you are returning an array), and secondly it's inefficient.
A better way to do it is to update the computed itens function like so:
itens() {
return this.data.filter((item) => {
if (this.selected === '1'){
return item.concluded === false
} else if (this.selected === '2'){
// filter another way
} else if (... // so on and so forth
});
},
Also, I would filter out the excluded items before sending them to the component. If you aren't going to use it, don't send it.
Remove the onChange event on the <select> and the associated method since they are now unused.
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
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);
},
}
}
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.
I'm a bit new at Vuejs2 and rxjs. So please be kind ^_^. I have an Observable api endpoint. I want to change the param value "food_type" via a select drop down on the same page. I want it so that when I select an item via the drop down the param value is updated, changing the end point and the data on the page gets reloaded. How can I achieve this?
here is my select drop down….
<div class="col-sm-2 divTableHead hand">
<select name="food_type" id="food_type" class="form-control" v-model="food_type">
<option value="" selected>Feeding</option>
<option value=“A”>One</option>
<option value=“AB”>Two Bee</option>
<option value=“BB”>Bee Bee</option>
<option value=“CB”>Cee Bee</option>
<option value=“CC”>Cee Cee</option>
</select>
</div>
here is what my Observable looks like…
data() {
return {
thisCat: [],
food_type: ''
}
},
subscriptions() {
return {
thisCat: Observable.from(axios.get(`${process.env.KITTY_URL}/api/v1/feedings/?cat__slug&cat__name=${this.$route.params.catName}&food_type=${""}`)
.catch(error => console.log(error)))
.pluck("data","results")
}
},
Thank you -_^
Seems like what you're looking for is a Watcher.
This is most useful when you want to perform asynchronous or expensive
operations in response to changing data.
That's exactly the case!
Check out the example below I prepared for you using the JSONPlaceholder API.
var app = new Vue({
el: '#app',
data: {
postID: '',
loading: false,
postContent: null,
},
watch: {
postID: function () {
this.fetchPost()
}
},
methods: {
fetchPost: function(id) {
this.loading = true;
fetch('https://jsonplaceholder.typicode.com/posts/'+this.postID)
.then(response => response.json())
.then(json => {
this.postContent = {
title: json.title,
body: json.body
}
this.loading = false;
})
},
}
});
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="app">
<select v-model="postID">
<option value="" disabled>Select a post</option>
<option value="1">Post #1</option>
<option value="2">Post #2</option>
<option value="3">Post #3</option>
<option value="4">Post #4</option>
<option value="5">Post #5</option>
</select>
<h2 v-if="loading">Loading...</h2>
<div v-if="postContent" class="post_content">
<h3>{{postContent.title}}</h3>
<p>{{postContent.body}}</p>
</div>
</div>
As you can see, the watcher watches for any changes of that property and perform whatever you told it to do. In this case, call the fetchPost method and perform a fetch.