Vue.js - Select / dropdown selected item vm binding is not working (bootstrap-vue) - vue.js

I'm trying to create a simple vue that binds the selected item from a select/dropdown to a property in the vm.
I haven't been able to find a clear and simple example of how this is down when using an options collection that is also in the view model.
<template>
<div>
<h1>Select box</h1>
<b-dropdown id="ddCommodity"
name="ddCommodity"
v-model="ddTestVm.ddTestSelectedOption"
text="Select Item"
variant="primary"
class="m-md-2" v-on:change="changeItem">
<b-dropdown-item disabled value="0">Select an Item</b-dropdown-item>
<b-dropdown-item v-for="option in ddTestVm.options":selected="option.value == 'LME/ST_TNI_ALL'":value="option.value">{{option.text}}</b-dropdown-item>
</b-dropdown> <span>Selected: {{ ddTestVm.ddTestSelectedOption }}</span>
</div>
</template>
<script>
export default {
components: {
},
data() {
return {
someOtherProperty: null,
ddTestVm: {
originalValue: [],
ddTestSelectedOption: "Value1",
disabled: false,
readonly: false,
visible: true,
color: "",
options: [
{
"value": "Value1",
"text": "Value1Text"
},
{
"value": "Value2",
"text": "Value2Text"
},
{
"value": "Value3",
"text": "Value3Text"
}
]
}
}
},
methods: {
changeItem: async function () {
//grab some remote data
try {
let response = await this.$http.get('https://www.example.com/api/' + this.ddTestVm.ddTestSelectedOption + '.json');
console.log(response.data);
this.someOtherProperty = response.data;
} catch (error) {
console.log(error)
}
}
},
watch: {
},
async created() {
}
}
</script>
<style>
</style>
Regardless of what i've tried i cannot get the selected value in the dropdown to change the ddTestSelectedOption property of the vm.
Could anyone assist on this issue?
Thanks.

b-dropdown in bootstrap-vue does not support v-model. As the documentation states:
Dropdowns are toggleable, contextual overlays for displaying lists of
links and actions in a dropdown menu format.
In other words, b-dropdown is essentially a UI component for displaying a menu or similar set of options.
I expect what you want is b-form-select.
That said, you could add a click handler to the options that sets the value.
<b-dropdown-item v-for="option in ddTestVm.options"
:key="option.value"
:value="option.value"
#click="ddTestVm.ddTestSelectedOption = option.value">
Here is a working example.

I thing you need b-form-select
<template>
<div>
<b-form-select v-model="selected" :options="options"></b-form-select>
<b-form-select v-model="selected" :options="options" size="sm" class="mt-3"></b-form-select>
<div class="mt-3">Selected: <strong>{{ selected }}</strong></div>
</div>
</template>
<script>
export default {
data() {
return {
selected: null,
options: [
{ value: null, text: 'Please select an option' },
{ value: 'a', text: 'This is First option' },
{ value: 'b', text: 'Selected Option' },
{ value: { C: '3PO' }, text: 'This is an option with object value' },
{ value: 'd', text: 'This one is disabled', disabled: true }
]
}
}
}
</script>

Only b-form-select can achieve the selected value behaviour.
Non-Selected Value Preview:
Selected Value Preview:
Sample Code:
<template>
<div>
<b-form-select v-model="selected" :options="options"></b-form-select>
</div>
</template>
<script>
export default {
data() {
return {
selected: null,
options: [
{ value: 1, text: 'Please select an option' },
{ value: 2, text: 'This is First option' },
{ value: 3, text: 'Selected Option' }
]
}
}
}
</script>

Wanted to leave a comment, but code example looks pale there :)
Yes, b-dropdown does not properly support Vue model, but it doesn't have to.
For those still interested in exactly dropdown (f.e. because it looks fancier), consider:
<b-dropdown :text="$i18n.locale" >
<b-dropdown-item v-for="(lang, i) in $i18n.availableLocales" :key="`Lang${i}`" :value="lang" v-on:click="$i18n.locale = lang;" >{{lang}}</b-dropdown-item>
</b-dropdown>
Slecifically v-on:click, which can handle the model value change for you.

Related

How can I show a different value apart from the dropdown options in v-select in vue 2 js

Well the scenario is when the selectedFruits is having some element then I need to show "Fruits Selected" and If there is no fruit selected it should display "Select Fruits" in the select. Below is my template and the data I am using. So basically it won't show the fruits selected but display the messages mentioned above in the v-select dropdown. I am new to the vue js, so wondering whether this is possible or not? or is there any alternate way to achieve the same scenario.
<template>
<div>
<v-select
v-model="selectedFruits"
:items="fruits"
label="name"
multiple
/>
</div>
</template>
<script>
export default {
data() {
return {
fruits: [
{ name: 'Apple' },
{ name: 'Mango' },
{ name: 'Banana' },
{ name: 'Berries' },
{ name: 'Muskmelon' }
],
selectedFruits: []
}
}
}
</script>
Well, I found that there is an option called selection which we can use with v-slot through which I am able to achieve the desired result. Below is the changes I made in my code:
<template>
<div>
<v-select
v-model="selectedFruits"
:items="fruits"
label="Select Fruits"
multiple
>
<template v-slot:selection="{ item, index }">
<span v-if="index === 0">{{
item && "Fruits Selected"
}}</span>
</template>
</v-select>
</div>
</template>
<script>
export default {
data() {
return {
fruits: [
{ name: 'Apple' },
{ name: 'Mango' },
{ name: 'Banana' },
{ name: 'Berries' },
{ name: 'Muskmelon' }
],
selectedFruits: []
}
}
}
</script>

How can I get a specifc selection in select vue.js?

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.

How can I sort a v-data-table by default?

I can't seem to get default sorting to work. All I can see for the argument custom-sort in the documentation is that it's a "Function used to sort items", but I don't know in what way. I can imagine many. Is it called for an initial sort? It seems to return a list of items, but when I try to create one, I get an error saying this.customSort is not a function.
<template>
<v-data-table
:headers="headers"
:items="reports"
hide-default-footer>
<template v-slot:item.requested="{ item }">
{{ datetimeToDistance(item.requested) }}
</template>
</v-data-table>
</template>
<script>
export default {
name: 'Reports',
data () {
return {
customSort: (items,index,isDesc) => console.log("never called"),
reports: [{name:"a",requested:"2020-01-01T00:00:00Z"}.{name:"b",requested:"2020-02-02T00:00:00"}],
}
},
computed: {
headers () {
return [
{text: "Name", value: "name"},
{text: "Report Type", value: "report_type"},
{text: "Requested", value: "requested", sort: (a,b) => a.localeCompare(b) },
];
},
}
}
</script>
My sorting works if you click on the links. All I really want here is to say: "When the page first loads, sort by 'requested' as though the user clicked that one initially. Then let them change the ordering."
Note: The datetimeToDistance is just a function which calls a library and isn't too important. It's just that the output of that column is not directly in the objects.
Use the sort-by and the sort-desc properties with the .sync option, and set the desired values in data.
<template>
<div>
<v-data-table
:sort-by.sync="sortBy"
:sort-desc.sync="sortDesc"
></v-data-table>
</div>
</template>
<script>
export default {
data () {
return {
sortBy: 'fat',
sortDesc: false,
},
}
</script>
https://vuetifyjs.com/en/components/data-tables/#external-sorting
I usually sort v-datatable in the pagination directive as below:
<template>
<v-data-table :headers="headers" :items="reports" :pagination.sync="pagination" hide-default-footer>
<template v-slot:item.requested="{ item }">
{{ datetimeToDistance(item.requested) }}
</template>
</v-data-table>
</template>
<script>
export default {
name: 'Reports',
data() {
return {
customSort: (items, index, isDesc) => console.log("never called"),
reports: [{ name: "a", requested: "2020-01-01T00:00:00Z" }.{ name: "b", requested: "2020-02-02T00:00:00" }],
pagination: { sortBy: 'requested', descending: true, rowsPerPage: 10 }
}
},
computed: {
headers() {
return [
{ text: "Name", value: "name" },
{ text: "Report Type", value: "report_type" },
{ text: "Requested", value: "requested", sort: (a, b) => a.localeCompare(b) },
];
},
}
}
</script>

How to fire on change event of select while setting v-model?

I have Dropdown.vue and Sprint.vue use Dropdown.vue
Dropdown.vue code
<template>
<div>
<select
v-model="selected"
#input="$emit('input', $event.target.value)"
#change="emitChangeEvent"
>
<option
v-for="(option, idx) in options"
:key="`option-${idx}`"
:value="option.value"
>
{{ option.text }}
</option>
</select>
</div>
</template>
<script>
export default {
name: 'Dropdown',
props: {
options: {
type: Array,
default: () => [],
},
value: {
required: false,
},
},
computed: {
selected: {
get() {
return this.value;
},
set(newValue) {},
},
},
methods: {
emitChangeEvent() {
this.$emit('change', this.selected);
},
},
};
</script>
In Sprint.vue
<template>
<div>
<Dropdown
v-model="sprint"
:options="sprints"
#change="sprintChanged"
/>
{{ sprint }}
<button #click="change">
Change Sprint
</button>
</div>
</template>
<script lang="ts">
import {
Component, Prop, Vue, Emit,
} from 'vue-property-decorator';
import Dropdown from '#/components/Dropdown.vue';
#Component({
components: {
Dropdown,
},
})
export default class Sprint extends Vue {
private sprint = {};
private sprints = [
{ text: 'Sprint A', value: 'A' },
{ text: 'Sprint B', value: 'B' },
{ text: 'Sprint C', value: 'C' },
];
change() {
this.sprint = 'B';
}
sprintChanged(value: any) {
console.log(`value=${value}, sprint=${this.sprint}`);
}
}
</script>
When we select an option, an event was fired. But when I click on the button to set selected option by programming, there is no event of select is fired.
What am I missing?
As far as I can see, there are two main problems in your Dropdown.vue component.
1) You are binding the v-model of the element to the computed property selected, and emitting selected in your emitChangeEvent
2) and the selected computed properties doesn't have a setter so nothing happens when the value change.
To check if this is the only problem, a quick solution is to change your emitChangeEvent to this:
emitChangeEvent(event) {
this.$emit('change', event.target.value);
}
Emitting directly the value from event will skip any problem in the definition of the computed property.

Adding img attribute to options in b-select component

I would like to add HTML img attribute to the b-form-select component of boostrap-vue inside to load img with each option?
<template>
<div>
<b-form-select v-model="selected" :options="options"></b-form-select>
<div class="mt-3">Selected: <strong>{{ selected }}</strong></div>
</div>
</template>
<script>
export default {
data() {
return {
selected: null,
options: [
{ value: null, text: 'Please select some item' },
{ value: 'a', text: 'This is First option' },
{ value: 'b', text: 'Default Selected Option' },
{ value: 'c', text: 'This is another option' },
{ value: 'd', text: 'This one is disabled', disabled: true }
]
}
}
}
</script>
It seems bootstrap-vue and bootstrap have different implementations on select components. And bootstrap-vue doesn't support thumbnails and it uses native select and options elements which makes impossible to set background image. Instead you can emulate dropdown component like select as below :
Template
<template>
<div class="hello">
<div class="back"></div>
<b-dropdown :text="selected ? selected.text : 'Please select some item'">
<b-dropdown-item
:disabled="option.disabled"
#click="select(option)"
v-for="option in options"
:key="option.value"
>
<div>
<img :src="option.src">
{{option.text}}
</div>
</b-dropdown-item>>
</b-dropdown>
</div>
</template>
Component
export default {
name: "HelloWorld",
props: {
msg: String
},
data() {
return {
selected: null,
options: [
{
value: null,
text: "Please select some item",
src: "https://mdn.mozillademos.org/files/7693/catfront.png"
},
{
value: "a",
text: "This is First option",
src: "https://mdn.mozillademos.org/files/7693/catfront.png"
},
{
value: "b",
text: "Default Selected Option",
src: "https://mdn.mozillademos.org/files/7693/catfront.png"
},
{
value: "c",
text: "This is another option",
src: "https://mdn.mozillademos.org/files/7693/catfront.png"
},
{
value: "d",
text: "This one is disabled",
disabled: true,
src: "https://mdn.mozillademos.org/files/7693/catfront.png"
}
]
};
},
methods: {
select(option) {
console.log(option);
this.selected = option;
}
}
};
Sandbox
Just like Bootstraps's custom select component BootstrapVue's <b-form-select> is based on <select>, which by HTML5 standards does not support complex HTML content in <option> elements.
If you need complex content (i.e. images, etc) in the options, you would need to create a custom component (probably based on <b-dropdpwn>) that allows you to use custom HTML5 in the "options" (dropdown items) and emulate the native select features.