I am receiving multiple json files from a server. They are all accessible on different urls based on years (2018,2019 and 2020). I am prefilling these years into a dropdown but now I want to fire a get call with axios everytime I change the value(?year=2018, ?year=2019 or ?year=2020). I also have another dropdown that is prefilled with IDs but have no idea how to attach a certain ID to selected year. These dropdowns are acting as filter for a table that is rendered below.
So to be more clear, when I reload I fire a get call for current year like so: baseurl?year=2019, with this selection I get ALL the data but then if I select an ID, this ID needs to be added to url like so:
baseurl?year=2019?id=0
My current code:
data() {
return {
year:[],
id: 0,
}
},
computed: {
axiosParams(){
const params = new URLSearchParams();
params.append('year', this.year);
return params;
},
//this returns my current year
year() {
var now = new Date()
var nowy = now.getFullYear()
return nowy
},
//this method makes sure that the dropdown is always preffiled
//with following years - eg. next year I only need 2019, 2020 and
//2021
years() {
var yearsArray = []
var now = new Date()
for (let i = -1; i < 2; i++) {
var nowy = now.getFullYear() + i
yearsArray.push(nowy)
}
return yearsArray
},
},
methods: {
getYears: function() {
axios.get('myurl',{
params : this.axiosParams
}
}).then((response) => {
this.year = response.data;
this.table = response.data;
})
},
getId: function() {
axios.get('myurl',{
params : {
year: this.year,
id : this.id
}
}
}).then((response) => {
this.id = response.data;
this.table = response.data;
})
},
},
created: {
this.getYears();
this.getId();
}
My HTML:
<select v-model="year">
<option v-model="yearsArray" v-for="year in years">{{year}} .
</option></select>
<select v-model="id"><option v-for="item in id">{{item}}</option> .
</select>
Thanks!
So, if I understand, you want to trigger an axios call when an id is selected. This can be done a couple of ways but this way will trigger on the selection of an id, and also on the selection of a year.
<select v-model="selectedYear" #change="yearSelected">
<option v-for="year in years" :key="year" :value="year">{{year}} .</option>
</select>
<select v-model="selectedId" #change="idSelected">
<option v-for="id in ids" :key="id" :value="id">{{id}}</option> .
</select>
Here, the years is from your computed property for years and ids is what you said was a dropdown "prefilled with IDs". The two v-model properties are initially set to null then are assigned a value on selection. They are defined in data like so.
data: () => ({
selectedId: null,
selectedYear: null,
}),
Each has a function call to do something with the selected option, #change="idSelected" which calls this method:
methods: {
idSelected() {
console.log(this.selectedId)
// here you make you axios call using this.selectedId as the param
axios.get('myurl',{
params : {
year: this.selectedYear,
id : this.selectedId
}
},
...
}
You could have the two selects without the #change and have a button that triggers the function call with #click. Either way you use the selectedId and selectedYear in that method.
Related
This is an example of my database
I am making a website using nodejs, MongoDB, and pug. I have displayed this data in a pug form with the data in the select boxes. Like this but in pug using for loops and all the data-
<!DOCTYPE html>
<html>
<body>
<form>
<select name="Type">
<option value="Electronic">Electronic</option>
</select>
<select name="Brands">
<option value="Apple">Apple</option>
<option value="Dell">Dell</option>
<option value="Samsung">Samsung</option>
</select>
<select name="Products">
<option value="Macbook Pro">Macbook Pro</option>
<option value="Inspioren">Inspioren</option>
<option value="Galaxy S20">Galaxy S20</option>
</select>
</form>
</body>
</html>
What I want to do is, change the select box options based on what the user has selected in other select options.
For example:- If a user selects "Type: "Electronic", and "Brand: Apple",I want to display only Apple products as the options.
Or if a user selects "Type: "Electronic" it should automaticly display brands list in the select box for brands.
The data will be added from the database (it will not be hardcodded).
One possibility could be to modify your Pug view to include select tag id's and leave the downstream selects blank and then add a script at the end of your view to add event listeners and do some DOM manipulation using JQuery or just javascript after the view is loaded. The event handlers would then allow you to conditionally fill in your next select tag based on whatever you choose in the current tag.
Something along the lines of:
script.
document.addEventListener("DOMContentLoaded",
function(e) {
const typeSelect = document.getElementById('type')
typeSelect.onchange = function(e) {
const brandSelect = document.getElementById('brand')
if (typeSelect.options[typeSelect.selectedIndex].text === 'Apple') {
['MacBook Pro', 'MacBook Air', 'MacBook Mini'].forEach(function(product) {
let newOption = createElement('option')
newOption.textContent = product
brandSelect.appendChild(newOption
}
else if ( // etc... ) {}
else {
// functionality to clear out the downstream select tags.
}
}
const brandSelect = document.getElementById('brand')
brandSelect.onchange = // etc, etc....
})
Server side code, with some test data:
app.get("/avi", (req, res) => {
res.render("avi", { Types: [ "Electronic"], data:
[ { Type: "Electronic", Brand: "Apple", Products: [ "MacBook Pro", "MacBook Air", "Mac Mini"]},
{ Type: "Electronic", Brand: "Dell", Products: [ "Inspioren", "Latitude"]},
{ Type: "Electronic", Brand: "Samsung", Products: [ "Galaxy S20", "Galaxy S10"]}
]
});
});
You should include a Types list, otherwise your Pug script will have to go through the raw data to identify the number of unique Types.
avi.pug:
html
head
body
p Linked Dropdowns
form
p Type:
select#types(onchange="typeChanged()")
option(value="") Select Type
each item of Types
option(value=item)=item
p Brand:
select#brands(onchange="brandChanged()")
option(value="") Select Brand
p Product:
select#products()
option(value="") Select Product
script.
const data = JSON.parse(`!{JSON.stringify(data)}`);
const types = document.getElementById("types")
const brands = document.getElementById("brands")
const products = document.getElementById("products")
const typeChanged = () => {
clearBrands(); clearProducts();
if (types.selectedIndex!==0) {
const selectedType = types.options[types.selectedIndex].text;
data.forEach(d => {
if (d.Type===selectedType) {
const newBrand = document.createElement("option")
newBrand.value = newBrand.innerText = d.Brand;
brands.appendChild(newBrand);
}
});
}
}
const brandChanged = () => {
clearProducts();
if (brands.selectedIndex!==0) {
const selectedType = types.options[types.selectedIndex].text;
const selectedBrand = brands.options[brands.selectedIndex].text;
data.forEach(d => {
if (d.Type===selectedType && d.Brand===selectedBrand) {
d.Products.forEach(p => {
const newProd = document.createElement("option")
newProd.value = newProd.innerText = p;
products.appendChild(newProd);
});
}
})
}
}
const clearBrands = () => {
const item0 = brands.childNodes[0];
brands.textContent = "";
brands.appendChild(item0);
}
const clearProducts = () => {
const item0 = products.childNodes[0];
products.textContent = "";
products.appendChild(item0);
}
The data at the server is injected into the client side Javascript with:
const data = JSON.parse(`!{JSON.stringify(data)}`);
This method is not very robust. There will be problems if anywhere in the data there is a backtick ` character.
I am answering my own question here. What I did was add the onchange function on the Type select box. And created a script inside my file, with the following code-
function change() {
var Type_name = document.getElementById("type").value;
var Brand= document.getElementById('brands');
var Product = document.getElementById('products');
Brand.options.length = 0;
Product.options.length = 0;
var DefaultBrand = new Option("Brand", "Null");
var DefaultProduct = new Option ("Product", "Null");
Brand.add(DefaultBrand, undefined);
Product.add(DefaultProduct, undefined);
var product_master = !{JSON.stringify(product_names)};
for (const brands of product_master) {
if (brands.Type == Type_name){
const BrandOption = new Option(brands.Brand, brands.Brand);
Brand.add(BrandOption, undefined);
}
}
And another function similar to this to add products.
This worked for me. But if anyone wants to improve this code, I would appreciate the help.
So I have a multiselection dropdown with select2 and I fetch the info from a database. The thing is that prepopulating the dropdown with many values works with the example from the docs, but I also want to include an extra data field. This is how I try to populate the dropdown:
var productSelect = $('#product_ids');
$.ajax({
type: 'GET',
url: '/ajaxOrdersData/' + orderID
}).then(function (datas) {
datas.forEach(data => {
var option = new Option(data.name, data.id, true, true);
option.dataset.price = data.price;
productSelect.append(option).trigger('change');
});
productSelect.trigger({
type: 'select2:select',
params: {
data: datas.map(function (item) {
return {
text: item.nfame,
id: item.id,
price: item.price,
}
})
}
});
});
The above code does create an option tag with a data-price value but then, retrieving that value like below, doesn't work:
var products = $('#product_ids').select2('data');
The products variable doesn't have the price data attribute.
What other ways there are for me to fetch the price data attribute of all products in multiselect when clicking on a button on the DOM?
This is a solution I came up with:
var products =$('#product_ids option:selected');
And then to retrieve the value:
$.each(products, function(index, value) {
actualTotals += Number(value.dataset.price);
});
An alternative solution is to keep the .select2('data'); way of fetching the selections but instead use jquery to retrieve each elements attributes from the resulting object:
var products =$('#product_ids').select2('data');
$.each(products, function(index, value) {
price = value.element.attributes['data-price'].value;
});
I have data in vue
newContract: {
latest_contract: false,
invoicing_period: "",
}
in computed method I am applying different properties to this.newContract.invoicing_period
computed: {
invoicing_period() {
const period = this.newContract.invoicing_period
const monthToPct = {
1615: "mid_month",
calendarMonth: "calendar_month",
};
return monthToPct[period];
}
}
and in methods I am appending this.invoicing_period from computed
let fd = new FormData();
fd.append("latest_contract", newContract.latest_contract);
fd.append("invoicing_period", this.invoicing_period);
in HTML I have a select dropdown.
<select
id="invoicingDropdown"
name="invoicingPperiod"
v-model="newContract.invoicing_period"
>
<option selected value="1615">15./16.</option>
<option value="calendarMonth">30/31.</option>
</select>
The problem is, that I am sending the correct values after choosing the options from dropdown, but it is not updating the newContract.invoicing_period from vue data().
Thank you.
SAMPLE https://stackblitz.com/edit/usjgwp?file=index.html
I want to show a number of kendo dropdownlist(s) on a page. The exact number depends on an API call. This API call will give me an array of stakeholder objects. Stakeholder objects have the following properties: Id, name, type, role and isSelected.
The number of dropdownlist that has to be shown on this page should be equal to the number of unique type values in the API response array. i.e,
numberOfDropdowns = stakeholders.map(a => a.type).distinct().count().
Now, each dropdown will have a datasource based on the type property. i.e, For a dropdown for type = 1, dataSource will be stakeholders.filter(s => s.type == 1).
Also the default values in the dropdowns will be based on the isSelected property. For every type, only one object will have isSelected = true.
I have achieved these things by using the following code:
<template>
<div
v-if="selectedStakeholders.length > 0"
v-for="(stakeholderLabel, index) in stakeholderLabels"
:key="stakeholderLabel.Key"
>
<label>{{ stakeholderLabel.Value }}:</label>
<kendo-dropdownlist
v-model="selectedStakeholders[index].Id"
:data-source="stakeholders.filter(s => s.type == stakeholderLabel.Key)"
data-text-field="name"
data-value-field="Id"
></kendo-dropdownlist>
<button #click="updateStakeholders">Update form</button>
</div>
</template>
<script>
import STAKEHOLDER_SERVICE from "somePath";
export default {
name: "someName",
props: {
value1: String,
value2: String,
},
data() {
return {
payload: {
value1: this.value1,
value2: this.value2
},
stakeholders: [],
selectedStakeholders: [],
stakeholderLabels: [] // [{Key: 1, Value: "Stakeholder1"}, {Key: 2, Value: "Stakeholder2"}, ... ]
};
},
mounted: async function() {
await this.setStakeholderLabels();
await this.setStakeholderDataSource();
this.setSelectedStakeholdersArray();
},
methods: {
async setStakeholderLabels() {
let kvPairs = await STAKEHOLDER_SERVICE.getStakeholderLabels();
kvPairs = kvPairs.sort((kv1, kv2) => (kv1.Key > kv2.Key ? 1 : -1));
kvPairs.forEach(kvPair => this.stakeholderLabels.push(kvPair));
},
async setStakeholderDataSource() {
this.stakeholders = await STAKEHOLDER_SERVICE.getStakeholders(
this.payload
);
}
setSelectedStakeholdersArray() {
const selectedStakeholders = this.stakeholders
.filter(s => s.isSelected === true)
.sort((s1, s2) => (s1.type > s2.type ? 1 : -1));
selectedStakeholders.forEach(selectedStakeholder =>
this.selectedStakeholders.push(selectedStakeholder)
);
},
async updateStakeholders() {
console.log(this.selectedStakeholders);
}
}
};
</script>
The problem is that I am not able to change the selection in the dropdownlist the selection always remains the same as the default selected values. Even when I choose a different option in any dropdownlist, the selection does not actually change.
I've also tried binding like this:
<kendo-dropdownlist
v-model="selectedStakeholders[index]"
value-primitive="false"
:data-source="stakeholders.filter(s => s.type == stakeholderLabel.Key)"
data-text-field="name"
data-value-field="Id"
></kendo-dropdownlist>
If I bind like this, I am able to change selection but then the default selection does not happen, the first option is always the selection option i.e, default selection is not based on the isSelected property.
My requirement is that I have to show the dropdown with some default selections, allow the user to choose different options in all the different dropdowns and then retrieve all the selection then the update button is clicked.
UPDATE:
When I use the first method for binding, The Id property of objects in the selectedStakeholders array is actually changing, but it does not reflect on the UI, i.e, on the UI, the selected option is always the default option even when user changes selection.
Also when I subscribe to the change and select events, I see that only select event is being triggered, change event never triggers.
So it turns out that it was a Vue.js limitation (or a JS limitation which vue inherited),
Link
I had to explicitly change the values in selectedStakeholders array like this:
<template>
<div
v-if="selectedStakeholders.length > 0"
v-for="(stakeholderLabel, index) in stakeholderLabels"
:key="stakeholderLabel.Key"
>
<label>{{ stakeholderLabel.Value }}:</label>
<kendo-dropdownlist
v-model="selectedStakeholders[index].Id"
:data-source="stakeholders.filter(s => s.type == stakeholderLabel.Key)"
data-text-field="name"
data-value-field="Id"
#select="selected"
></kendo-dropdownlist>
<button #click="updateStakeholders">Update form</button>
</div>
</template>
And in methods:
selected(e) {
const stakeholderTypeId = e.dataItem.type;
const selectedStakeholderIndexForTypeId = this.selectedStakeholders.findIndex(
s => s.type == stakeholderTypeId
);
this.$set(
this.selectedStakeholders,
selectedStakeholderIndexForTypeId,
e.dataItem
);
}
I want to change the second select list according to the selected value in the first one. It worked when i did two Vue instances for each select, but i wanted to do a small app so its all a bit cleaner.
The types JSON array needs to be outside the Vue JS. You can see it in the fiddle.
Somehow i just dont get how to update the second selectlist.
Before i did something like this and it worked perfectly:
// methods of first select (category)
methods: {
update: function (value)
this.options = types[value]
}
}
...
// methods of second select (typselect)
methods: {
onChange(event) {
typselect.update(event.srcElement.value)
}
}
The app:
<div id="app">
<select v-model="category" v-on:change="onChange">
<option>Choose</option>
<option value="5">type1</option>
<option value="6">type2</option>
<option value="11">type3</option>
</select>
<select id="typselect">
<option v-for="option in options" v-bind:value="option.value">{{ option.text }}</option>
</select>
</div>
So i switched that for something like this:
new Vue({
el: '#app',
data: {
category: '5'
},
computed: {
options: function(event) {
console.log('should be called on change');
let options = ''
options = 1;
// options = types[event.srcElement.value]; // this would be so easy...
return options
}
},
methods: {
onChange: function(e) {
console.log(event.srcElement.value);
this.options = this.options
}
}
})
But i just don't get how to get the second selectlist updated.
Here come a fiddle:
https://jsfiddle.net/Honkoman/g9g5uukr/2/
Your computed should look like this.
computed: {
options: function(event) {
return types[this.category]
}
},
Updated fiddle.