Vue js populate new selects when changing the main one - javascript

I need your help about populating or loading new select with vue js, I know how to do this with jquery but in vue i don't know how because i'm new with this library.
i have the main select :
<select>
<option value='3'>FRANCE</option>
<option value='5'>USA</option>
<option value='6'>CANADA</option>
<option value='8'>MOROCCO</option>
</select>
I want that if i choose FRANCE i get a select of FRANCE cities from database, and also when i select USA i get an other select of USA cities from database.
So for example i will get :
<select>
<option value='6'>CANADA</option>
<option value='8'>MOROCCO</option>
</select>
<select>
<option value='34'>France city one</option>
<option value='35'>France city two</option>
</select>
<select>
<option value='3'>Usa city one</option>
<option value='5'>Usa city two</option>
</select>
When choosing France and USA i will populate select of cities with an array
I appreciate any help, I don't realy know how can i do this with vue js,
I don't want add all select of cities in my html because i don't know how much countries i have.
I tried this but this not resolve my probleme :
const addProduct = new Vue({
el: '#addProduct',
data: {
name: '',
name_url: '',
cities: '',
countries: [],
range: 0
},
created: function () {
this.$http.get('/api/countries').then(response => {
this.countries = response.data
}, response => {
});
},
methods: {
addForm: function(val, data) {
this.range += 1;
alert(this.range)
var index = _.findIndex(this.countries,{city_id: val});
this.countries.splice(index, 1)
}
},
watch: {
'cities' (val, oldVal) {
this.$http.post('/api/cities/values', {city_id:val}).then(response => {
this.addForm(val, response.data);
}, response => {
});
}
}
});
in html :
<div class="uk-grid" data-uk-grid-margin>
<div class="uk-width-medium-1-4">
<label for="attribute">Countries</label>
<md-select name="country" id="country" v-model="country">
<md-option v-for="country in countries" :value="country.country_id">#{{ country.name }}</md-option>
</md-select>
</div>
</div>
<div class="uk-grid" data-uk-grid-margin>
<my-cities v-for="n in range"></my-cities>
</div>
<script type="x-template" id="my-cities">
<div class="uk-width-medium-1-4">
<label for="attr">Cities</label>
<md-select name="attr" id="attr" v-model="attr">
<md-option value="">Select </md-option>
<md-option value="val in values">Select</md-option>
</md-select>
</div>
</script>
an example like this on Jsfiddle : http://jsfiddle.net/pu8pp62v/3/

This is an example that you can maybe use (but need some modifications to use your API call) :
new Vue({
el: "#app",
data: function() {
return {
selectedCountries: [],
selectOptionsCountries: [
{ value: 3, name: 'FRANCE' },
{ value: 5, name: 'USA' },
{ value: 6, name: 'CANADA' },
{ value: 8, name: 'MOROCCO' }
],
selectedCities: [],
selectOptionsCities: []
}
},
methods: {
},
watch: {
selectedCountries: function(newValue, oldValue) {
this.selectOptionsCities = [];
this.selectedCities = [];
for( var i = 0, length = newValue.length; i < length; i++ ){
this.selectedCities[i] = [];
if( newValue[i] === 3 ){
this.selectOptionsCities.push(
[{ value: 31, name: 'Paris' },
{ value: 32, name: 'Marseille' }]
)
}
if( newValue[i] === 5 ){
this.selectOptionsCities.push(
[{ value: 51, name: 'New-York' },
{ value: 52, name: 'Boston' }]
)
}
if( newValue[i] === 6 ){
this.selectOptionsCities.push(
[{ value: 61, name: 'Montreal' },
{ value: 62, name: 'Vancouver' },
{ value: 63, name: 'Ottawa' },
{ value: 64, name: 'Toronto' }]
)
}
if( newValue[i] === 8 ){
this.selectOptionsCities.push(
[{ value: 81, name: 'Rabat' },
{ value: 82, name: 'Casablanca' },
{ value: 83, name: 'Fes' }]
)
}
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.2.5/vue.js"></script>
<div id="app">
Selected countries : {{ selectedCountries }}
<br />
Selected cities : {{ selectedCities }}
<br />
<select v-model="selectedCountries" multiple>
<option v-for="(option, index) in selectOptionsCountries" :value='option.value'>
{{ option.name }}
</option>
</select>
<select v-for="(optionsCities, index) in selectOptionsCities" v-model="selectedCities[index]" multiple>
<option v-for="(option, index) in optionsCities" :value='option.value'>
{{ option.name }}
</option>
</select>
</div>

Added after author's comment:
Check this fiddle: http://jsfiddle.net/jjpfvx5q/1/
Inside 'chosenCities' array you have all selected cities by country (one city per country.)
Original answer:
Here is an example for you: fiddle
Is that what you are trying to achieve?
setTimeout functions are just pretending a real data fetching.
<script src="//unpkg.com/vue/dist/vue.js"></script>
<div id="app">
<template>
<div>
<select v-model="country">
<option disabled value="">Please select one</option>
<option
v-for="c in countries"
:value="c.id">{{ c.name }}</option>
</select>
<span>Selected: {{ country }}</span>
<span v-if="cities.length">Cities:</span>
<ul v-if="cities.length">
<li v-for="c in cities">{{ c }}</li>
</ul>
</div>
</template>
</div>
<script>
var Main = {
data() {
return {
country: {},
countries: [],
cities: [],
coInit: [{ id: '3', name: 'France' }, { id: '2', name: 'USA' }],
cFrance: ['Paris', 'Whatever'],
cUSA: ['NY', 'LA']
}
},
methods: {
loadCountries: function () {
setTimeout(() => { this.countries = this.coInit }, 500);
},
getCities: function() {
if(this.country) {
switch (this.country) {
case '3':
setTimeout(() => { this.cities = this.cFrance }, 500);
break;
case '2':
setTimeout(() => { this.cities = this.cUSA }, 500);
break;
}
}
}
},
mounted() {
this.loadCountries();
},
watch: {
country: function() {
this.getCities();
}
}
}
var Ctor = Vue.extend(Main);
new Ctor().$mount('#app');
</script>

Related

Vue.js Filter by dropdown if word is found in array (not exact match only)

https://jsfiddle.net/75f3c2po/
How can I make the above Vue.js code filter by dropdown match to the entire array even if there's commas separating other words? Right now it only matches if the type: BMW but I'd like it to also show cards that might have something like type: BMW, Ford.
Also, I'm super new to Vue, was hoping someone could also show me how I could have it update as soon as a select option is selected without having to hit the "search" button?
Thanks so much
To be able to find by type you could split your type string by a comma, and check if the array includes the value:
this.searchResult = this.items.filter(function(item) {
let filtered = true
if (filterType) {
filtered = item.type.split(',').includes(filterType)
}
if (filtered) {
if (filterCountry && filterCountry.length > 0) {
filtered = item.country == filterCountry
}
}
if (filtered) {
if (filterYear && filterYear.length > 0) {
filtered = item.year == filterYear
}
}
return filtered
})
To be able to search immediately you could use #change handler on your selects:
<select v-model="selectedType" #change="search">
I've provided a snippet below based on your fiddle.
Changed your searchResults property to a computed property, so that it updates automatically when it detects a change in the selectedType, selectedCountry or selectedYear.
To check against the type, I would first split your csv list and then check if the type exists in that array. I've done that using item.type.split(',').map(x => x.trim()).indexOf(filterType) !== -1;
Make sure you understand the basics of Vue and read Computed Properties and Watchers
Vue.config.devtools = false;
Vue.config.productionTip = false;
new Vue({
el: '#app',
data: {
selectedType: '',
selectedCountry: '',
selectedYear: '',
items: [{
name: 'Nolan',
type: 'mercedes, ford',
year: '2020',
country: 'england'
},
{
name: 'Edgar',
type: 'bmw',
year: '2020',
country: 'belgium'
},
{
name: 'John',
type: 'bmw, audi',
year: '2019',
country: 'england'
},
{
name: 'Axel',
type: 'mercedes',
year: '2020',
country: 'england'
}
],
},
computed: {
searchResult: function() {
let filterType = this.selectedType,
filterCountry = this.selectedCountry,
filterYear = this.selectedYear
return this.items.filter(function(item) {
let filtered = true
if (filterType && filterType.length > 0) {
filtered = item.type.split(',').map(x => x.trim()).indexOf(filterType) !== -1;
}
if (filtered) {
if (filterCountry && filterCountry.length > 0) {
filtered = item.country == filterCountry
}
}
if (filtered) {
if (filterYear && filterYear.length > 0) {
filtered = item.year == filterYear
}
}
return filtered;
})
}
}
})
.list-item {
margin-top: 50px;
}
.card {
box-shadow: 0px 10px 16px rgba(0, 0, 0, 0.16);
width: 400px;
padding: 20px 30px;
margin-bottom: 30px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<select v-model="selectedType">
<option value="" disabled selected hidden>Type</option>
<option value="mercedes">Mercedes</option>
<option value="bmw">BMW</option>
</select>
<select v-model="selectedCountry">
<option value="" disabled selected hidden>Country</option>
<option value="belgium">Belgium</option>
<option value="england">England</option>
</select>
<select v-model="selectedYear">
<option value="" disabled selected hidden>Year</option>
<option value="2019">2019</option>
<option value="2020">2020</option>
</select>
<div class="list-item" v-for="item in searchResult">
<div class="card">
<p>Name: {{ item.name }}</p>
<p>Car: {{ item.type }}</p>
<p>Year: {{ item.year }}</p>
<p>Country: {{ item.country }}</p>
</div>
</div>
</div>

Loop data is not showing at first in my vue.js search feature

I have a search feature using Vue.Js. The code below works when clicking the search button. My concern is, all data does not appear at first, it appears only when I click search. What I want, data appears at the start. Is there anything missing in the code I created? please help me solving this problem
<template>
<div>
<select v-model="selectedLevel">
<option value="" disabled selected hidden>Level</option>
<option value="beginner">Beginner</option>
<option value="intermediate">Intermediate</option>
<option value="advanced">Advanced</option>
</select>
<select v-model="selectedTime">
<option value="" disabled selected hidden>Time</option>
<option value="30">30 Min</option>
<option value="60">60 Min</option>
</select>
<select v-model="selectedType">
<option value="" disabled selected hidden>Type</option>
<option value="cycling">Cycling</option>
<option value="boxing">Boxing</option>
</select>
<button #click="search">Search</button>
<div class="list-item" v-for="item in searchResult">
<div class="card">
<p>Class Name: {{ item.type }}</p>
<p>Level: {{ item.level }}</p>
<p>Time: {{ item.time }}</p>
</div>
</div>
</div>
</template>
export default {
data() {
return {
selectedType: '',
selectedTime: '',
selectedLevel: '',
items: [{
type: 'cycling',
time: '30',
level: 'beginner'
},
{
type: 'boxing',
time: '60',
level: 'beginner'
},
{
type: 'cycling',
time: '60',
level: 'advanced'
},
{
type: 'boxing',
time: '30',
level: 'advanced'
}
],
searchResult: [],
}
},
methods: {
search() {
let filterType = this.selectedType,
filterTime = this.selectedTime,
filterLevel = this.selectedLevel
this.searchResult = this.items.filter(function(item) {
let filtered = true
if (filterType && filterType.length > 0) {
filtered = item.type == filterType
}
if (filtered) {
if (filterTime && filterTime.length > 0) {
filtered = item.time == filterTime
}
}
if (filtered) {
if (filterLevel && filterLevel.length > 0) {
filtered = item.level == filterLevel
}
}
return filtered
})
}
}
}
You need to run the search method once the component is mounted because now runs only when you click the button, try the below:
inside export default add the below:
data() {
return {
selectedType: '',
selectedTime: '',
selectedLevel: '',
items: [{
type: 'cycling',
time: '30',
level: 'beginner'
},
{
type: 'boxing',
time: '60',
level: 'beginner'
},
{
type: 'cycling',
time: '60',
level: 'advanced'
},
{
type: 'boxing',
time: '30',
level: 'advanced'
}
],
searchResult: [],
}
},
mounted(){
this.search();
}
Just add the mounted part
When the page loads this.searchResult is empty array.
You search method is called only when you click search button. and then filtered items are assigned to this.searchResult
Simple solution would be to manually call the search method on component mounted or created hook.
Better solution would be to rewrite your search logic using pipe and computed propeties

Vue JS set first item in a populated select to be selected with v-model

I have tricky challenge with vuejs, I want to have two select fields. the first one should select fruits for example, and the second should list all fruits. If I select vegetable from the first select field, the second select field should list all vegetable.
I stumble and find similar stuff online but I don't know how to make first item in the second select field selected.
anytime I select fruits, the first item on the list in second select first should be selected as default, and if I select vegetable, the first item in the second select field should be selected as default.
pls help me check the code here: https://jsfiddle.net/aj6g87dh/1/
new Vue({
el: '#test',
data: {
category: 'fruits',
list: '',
optionsData: {
fruits: [
{ text: 'Orange', value: 'orange' },
{ text: 'Banane', value: 'banana' },
],
vegetables: [
{ text: 'Brocolis', value: 'brocolis' },
{ text: 'Radish', value: 'radish' },
]
}
},
computed: {
options: function() {
let options = ''
switch (this.category) {
case 'fruits':
options = this.optionsData.fruits
break;
case 'vegetables':
options = this.optionsData.vegetables
break;
default:
options = this.optionsData.fruits
}
return options
}
},
methods: {
onChange: function() {
this.options = this.options
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.0.5/vue.js"></script>
<div id="test">
<select v-model="category" v-on:change="onChange" id="select1">
<option value="fruits">Fruits</option>
<option value="vegetables">Vegetables</option>
</select>
<select id="select2" v-model="list">
<option v-for="option in options" v-bind:value="option.value"> {{ option.text }} </option>
</select>
<span>{{ }}</span>
</div>
You can remove onChange method and add a watch property. This way you can handle changing logic there.
Also, you can simplify options retrieval to one line.
new Vue({
el: '#test',
data: {
category: 'fruits',
list: '',
optionsData: {
fruits: [{
text: 'Orange',
value: 'orange'
},
{
text: 'Banane',
value: 'banana'
},
],
vegetables: [{
text: 'Brocolis',
value: 'brocolis'
},
{
text: 'Radish',
value: 'radish'
},
]
}
},
computed: {
options: function() {
return this.optionsData[this.category]
}
},
watch: {
category: {
handler: function(newVal) {
this.list = this.optionsData[newVal][0].value;
},
immediate: true
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="test">
<select v-model="category" id="select1">
<option value="fruits">Fruits</option>
<option value="vegetables">Vegetables</option>
</select>
<select id="select2" v-model="list">
<option v-for="(option, i) in options" v-bind:value="option.value"> {{ option.text }} </option>
</select>
<span>{{ }}</span>
</div>

Vue v-for after condition using vuex

While I am trying to create a form I encountered this problem which I don't have any solution.
There is a Vuex data on Vehicles Make and Model of vehicle, now once the make is selected, I want the other form to loop through the selected Make and find other models... something like that.
Here is what I did so far:
cars.js - (vuex module)
const state = {
make: [
{
name: 'Audi',
carid: '1',
models: [
{
modelid: '1.1',
name: 'A7',
},
{
modelid: '1.2',
name: 'A8',
},
],
},
{
name: 'BMW',
carid: '2',
models: [
{
modelid: '2.1',
name: '5 Series',
},
{
modelid: '2.2',
name: '7 Series',
},
],
},
],
}
Cars.vue
<template>
<div class="labelos">
<div class="label-name">
<h4>Car make:</h4>
</div>
<div class="label-body">
<label for="car-make">
<select v-model="selectedType" name="carmake" required>
<option value=""></option>
<option v-for="(cars, index) in cars.make" :key="index" :value="cars.carid">{{ cars.name }}</option>
</select>
</label>
</div>
</div>
<div class="labelos">
<div class="label-name">
<h4>Car model:</h4>
</div>
<div class="label-body">
<label for="car-model">
<select>
<option value=""></option>
<option v-for="(model, index) in cars.make" :key="index" :value="cars.carid">{{ model.carid }}</option>
</select>
</label>
Model:
</div>
</div>
</template>
<script>
import { mapState } from 'vuex';
export default {
name: 'cars',
data() {
return {
selectedType: '',
selectedCity: '',
};
},
methods: {
},
components: {
Headers,
Footers,
},
computed: {
...mapState([
'cities', 'cars',
]),
},
};
</script>
So as you can see on first label I am looping through makes, and once a car make is selected that carid is saved on selectedType, now how is that possible to load second dropdown according to that selection, so if carid 1 is selected, the list will load car models available on given carid (in this example carid 1)
Looking forward to hear from someone, I am stuck here.. I don't know any solution how to do this... this is so far I have done
Cheers
You should create a computed property which returns model options based on the value of the selected make type. Then you can bind to that and it will automatically update whenever the selected make changes:
models() {
if (this.selectedType) {
return this.cars.make.find((car) => car.carid === this.selectedType).models;
}
}
Here's a working example:
const store = new Vuex.Store({
state: {
cars: {
make: [{
name: 'Audi',
carid: '1',
models: [
{ modelid: '1.1', name: 'A7' },
{ modelid: '1.2', name: 'A8' },
]
}, {
name: 'BMW',
carid: '2',
models: [
{ modelid: '2.1', name: '5 Series' },
{ modelid: '2.2', name: '7 Series' }
],
}]
}
}
})
new Vue({
el: '#app',
store,
data() {
return {
selectedType: '',
};
},
computed: {
...Vuex.mapState(['cars']),
models() {
if (this.selectedType) {
return this.cars.make.find((car) => car.carid === this.selectedType).models;
}
}
},
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.3/vue.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/3.0.1/vuex.min.js"></script>
<div id="app">
<h4>Car make:</h4>
<select v-model="selectedType" name="carmake" required>
<option value=""></option>
<option v-for="(cars, index) in cars.make" :key="index" :value="cars.carid">{{ cars.name }}</option>
</select>
<h4>Car model:</h4>
<select>
<option value=""></option>
<option v-for="(model, index) in models" :key="index" :value="model.modelid">{{ model.name }}</option>
</select>
</div>
Working example with your data:
const state = {
make: [
{
name: 'Audi',
carid: '1',
models: [
{modelid: '1.1', name: 'A7'},
{modelid: '1.2', name: 'A8'}
]
}, {
name: 'BMW',
carid: '2',
models: [
{modelid: '2.1', name: '5 Series'},
{modelid: '2.2', name: '7 Series'}
]
}
]
}
new Vue({
el: '#app',
data: {
state: state,
selected: 0
},
computed: {
models () {
var maker = this.state.make.find(m => m.carid === this.selected)
return maker ? maker.models : []
}
}
})
<div id="app">
<select v-model="selected">
<option value="0" selected>Choose maker</option>
<option
v-for="maker in state.make"
:key="maker.carid"
:value="maker.carid"
>{{ maker.name }}</option>
</select>
<br>
<select>
<option value="0" selected>Select model</option>
<option
v-for="model in models"
:key="model.modelid"
:value="model.modelid"
>{{ model.name }}</option>
</select>
</div>
<script src="https://unpkg.com/vue#2.5.3/dist/vue.min.js"></script>
If you can, change 'modelid' to simple integers - 1, 2, etc., at least. And if you can and you know how to do it, change your data structure - divide makers and models to separate arrays/objects.
Here's a plugin for this specific task you're trying to accomplish: vue-dependon.
It hasn't been updated for 1-2years, but I think that you can check its source code and see how it works.
UPDATE:
All you need from the sourcecode is the loadOptions function and the code between L83 and L105.
You can adapt that code to your needs.

Dynamically change select input options in vuejs 2

How to change dynamically the options in a select dropdown v-model ?
I have 2 select inputs, one should change according to the others.
For example, if i select "fruits" the select display the fruits, if i select "vegetables" it displays the vegetables.
I don't use Vuejs, but after looking at the documentation:
var TypesArr = {
Fruit: [{ text: 'Apple', value: 'Apple' }, { text: 'Orange', value: 'Orange' }, { text: 'Mango', value: 'Mango' }],
Meat: [{ text: 'Steak', value: 'Steak' }, { text: 'Pork', value: 'Pork' }]
}
var selectTwo = new Vue({
el: '#select2',
data: {
selected: TypesArr['Fruit'][0],
options: TypesArr['Fruit']
},
methods: {
update: function (value)
{
this.options = TypesArr[value]
}
}
})
new Vue({
el: '#select1',
data: {
selected: 'Fruit',
options: [ { text: 'Fruit', value: 'Fruit' }, { text: 'Meat', value: 'Meat' } ]
},
methods: {
onChange: function (event)
{
selectTwo.update(event.srcElement.value)
}
}
})
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<select v-on:change="onChange" id="select1">
<option v-for="option in options" v-bind:value="option.value">
{{ option.text }}
</option>
</select>
<select id="select2">
<option v-for="option in options" v-bind:value="option.value">
{{ option.text }}
</option>
</select>
The other answers are not really 'Vue' answers.
How you handle this depends on what you want to do with the select box. I'm assuming you'll to handle the input on a form.
Two options:
Use a Computed property
Use v-if to show different select boxes. This would be ideal if each select box has a different v-model
Computed Property
<template>
<div class="container">
<select id="firstInput" v-model="selected">
<option v-for="option in firstInputOptions" :value="option">
{{ option }}
</option>
</select>
<select id="secondInput" v-model="secondInputSelected">
<option v-for="option in secondInputOptions" :value="option">
{{ option }}
</option>
</select>
</div> <!-- /container -->
</template>
<script>
export default {
computed: {
secondInputOptions(){
return this.selected === 'fruit' ? this.fruit : this.vegetables
}
},
data () {
return {
fruit: ['apple', 'banana', 'orange'],
vegetables: ['carrot', 'beet', 'celery'],
firstInputOptions: ['fruit', 'vegetables']
selected: 'fruit',
secondInputSelected: ''
}
},
}
</script>
Conditional Rendering
<template>
<div class="container">
<select id="firstInput" v-model="selected">
<option v-for="option in firstInputOptions" :value="option">
{{ option }}
</option>
</select>
<select id="secondInputFruit" v-model="selected" v-if="selected == 'fruit'">
<option v-for="option in secondInputOptions" :value="option">
{{ option }}
</option>
</select>
<select id="secondInputVegetables" v-model="vegetableSelected" v-else-if="selected == 'vegetables'">
<option v-for="option in secondInputOptions" :value="option">
{{ option }}
</option>
</select>
</div> <!-- /container -->
</template>
<script>
export default {
data () {
return {
fruits: ['apple', 'banana', 'orange'],
fruitSelected: '',
vegetables: ['carrot', 'beet', 'celery'],
vegetableSelected: '',
firstInputOptions: ['fruit', 'vegetables']
selected: 'fruit'
}
},
}
</script>
Using pure javascript
var typesArr = {fruit: ['Apple', 'Orange', 'Mango'], meat: ['Steak', 'Pork']}
function changeContext(value)
{
if (typesArr.hasOwnProperty(value)) {
var out = ''
for (var i = 0; i < typesArr[value].length; i++) {
out += '<option value="' + typesArr[value][i] + '">' + typesArr[value][i] + '</option>'
}
document.getElementById('select2').innerHTML = out
}
}
changeContext('fruit')
<select onchange="changeContext(this.value)">
<option value="fruit">Fruit</option>
<option value="meat">Meat</option>
</select>
<select id="select2"></select>

Categories