In my component, i have multiselect that should get options selected on matched value passed:
<select class="custom-select" multiple="" v-model="items">
<option v-for="item in objects_list" :selected="itemSelected(item)" :key="item.id" :value="item.id">
{{item.name}}
</option>
</select>
...
methods() {
itemSelected(item) {
let selected;
let object_selected = this.value.filter(object => {
console.log(object)
if(item.id == object) {
return true;
}
});
}
the selected binding doesn't seems to work, how to correctly implement multiselect to bind selected options ?
The best way to do this is to use the CREATED Function from Vue.js
<template>
<div>
<select class="ur-CSS-class" multiple v-model="selected">
<option v-for="item in objects_list" :key="item.id" :value="item.id">
{{ item.name }}
</option>
</select>
<br />
<span>Selected : {{ selected }}</span>
</div>
</template>
<script>
var your_list = [
{ name: "one", id: 13 },
{ name: "two", id: 18 },
{ name: "three", id: 11 },
{ name: "four", id: 7 },
{ name: "five", id: 1 },
{ name: "six", id: 2 },
];
export default {
name: "App",
data() {
return {
objects_list: your_list,
selected: []
};
},
props:{
inputData: {
type: Object,
required: true
}
},
computed: {
selected() {
let selected = this.objects_list.filter(item => {
return this.inputData.find(i => i == item.id);
});
return selected;
}
}
};
</script>
Related
I have a dropdown menu that triggers a method to get data based on what is selected in the dropdown. I would like to add a condition that changes the #change method if a checkbox is selected. The method would switch based on which checkbox is selected. I guess a v-if that runs a check somewhere--Im not sure.
new Vue({
el: "#app",
data: {
selection: 'Select a option',
todos: [{
name: 'apple'
}, {
name: 'oranges'
}, {
name: 'carrots'
}]
},
methods:{
changeP (event) {
this.getSetA();
// alert("this is set a");
},
changeP1 (event) {
this.getSetB();
alert("this is set b");
},
changeP2 (event) {
this.getSetC();
alert("this is set c");
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<h2>Fruit:</h2>
<select v-model="selection" #change="changeP($event)">
<option value="Select a option" disabled>Choose an fruit:</option>
<option v-for="(t, i) in todos" :key="i" :value="t.name">{{ t.name }}</option>
</select>
<br>
<input type="checkbox" id="vehicle1" name="vehicle1" value="apple"> Apples
<input type="checkbox" id="vehicle2" name="vehicle2" value=""> Oranges
<input type="checkbox" id="vehicle3" name="vehicle3" value="">pears
</div>
new Vue({
el: '#app',
data: {
selection: 'Select an option',
selectOptions: [ { name: 'apple' }, { name: 'oranges' }, { name: 'carrots' }],
checkboxOptions: [ 'vehicle1', 'vehicle2', 'vehicle3' ],
checkedItems: [],
message: '',
},
methods: {
onDropdownChange() {
this.message = '';
if (this.checkedItems.length) {
this.message = `You've selected: `;
for(let i = 0; i < this.checkedItems.length; i++) {
this.message += this.checkedItems[i] + ' ';
}
}
switch(this.selection) {
case 'apple':
return this.onAppleSelected();
case 'oranges':
return this.onOrangesSelected();
default:
return this.onCarrotsSelected();
}
},
onAppleSelected() {
if (this.message.length) {
this.message += 'and apple.';
return;
}
this.message = `You've selected apple`;
},
onCarrotsSelected() {
this.message += ' CARROT!'
},
onOrangesSelected() {
this.message += ' No Scurvy';
}
},
template: `
<div id="app">
<h2>Fruit:</h2>
<select v-model="selection" #change="onDropdownChange">
<option value="Select an option">Choose</option>
<option v-for="(o, i) in selectOptions" :key="o.name" :value="o.name">{{o.name}}</option>
</select>
<div style="display:block" v-for="(o, i) in checkboxOptions" :key="o">
<label :for="o">{{o}}</label>
<input type="checkbox" :id="o" :name="o" :value="o" v-model="checkedItems" />
</div>
<p>{{message}}</p>
</div>
`
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
</div>
You should probably have a single method that decides which method to call based on the state of the component.
I have a dropdown that I want to give a validation.
So here is my dropdown component:
<template>
<div class="custom-select" :tabindex="tabindex" #blur="open = false">
<div class="selected" :class="{ open: open }" #click="open = !open">
{{ selected.name }}
</div>
<div class="items" :class="{ selectHide: !open }">
<div v-if="defaultValue != ''">{{defaultValue}}</div>
<div v-for="(option, i) of options" :key="i" #click=" selected = option; open = false; $emit('input', option);">
{{ option.name }}
</div>
</div>
</div>
</template>
<script>
export default {
props: {
options: {
type: Array,
required: true,
},
defaultValue: {
type: String,
required: false,
default: "Choose an option",
},
tabindex: {
type: Number,
required: false,
default: 0,
},
},
data() {
return {
open: false,
selected: this.setDefaultValue(),
};
},
mounted() {
this.$emit("input", this.selected);
},
methods: {
setDefaultValue () {
if (this.defaultValue == '' && this.options.length > 0) {
return this.options[0];
}
return { name: this.defaultValue};
}
}
};
</script>
and in my parent component, I am using this dropdown, the fetched value from API call and filled with variations. So what I am trying to do is if the value is not selected (default: "Choose an option"), I want to give an error message which is saying, the dropdown is mandatory.
<Dropdown
:options="getVariations"
class="select"
#input="getSelected"
/>
<script>
import Dropdown from "../components/Dropdown";
import apiHelper from "../helpers/apiHelper";
export default {
components: {Dropdown},
data() {
return {
selected: "",
variationId: "",
selectedObject: null
};
},
computed: {
getVariations() {
return this.product.attributes.variations
}
},
methods: {
getSelected(opt) {
this.selectedObject = opt;
this.selected = opt.description;
this.variationId = opt.id;
}
},
};
</script>
This is a simple example of what you are asking for:
// v-model property
selectedVModel: null
// html
<select id="testselect" v-model="selectedVModel" required>
<option :value="null">Default</option>
<option value="bar">Option 1</option>
<option value="foo">Option 2</option>
<option value="baz">Option 3</option>
</select>
<div v-if="!selectedVModel">Error! Choose something!</div>
You can handle the error inside of your child component. In this example, if the v-model as selectedVModel is empty, the <div> with v-if="!selectedVModel" will be shown. As selectedVModel is null it will automatically select <option :value="null">Default</option>. If you chosse one of the options selectedVModel get´s a value so v-if="!selectedVModel" results into false, no error then. If you select Default again, the value turns to null again which results into true at v-if="!selectedVModel", so the error will be visible again.
Combine this simple example with your code and let me know if it helped you.
If you want to validate in your parent component, try to check selectedObject.id :
Vue.component('Dropdown', {
template: `
<div class="custom-select" :tabindex="tabindex" #blur="open = false">
<div class="selected" :class="{ open: open }" #click="open = !open">
{{ selected.name }}
</div>
<div class="items" :class="{ selectHide: !open }">
<div v-if="defaultValue != ''">{{defaultValue}}</div>
<div v-for="(option, i) of options" :key="i" #click=" selected = option; open = false; $emit('input', option);">
{{ option.name }}
</div>
</div>
</div>
`,
props: {
options: {
type: Array,
required: true,
},
defaultValue: {
type: String,
required: false,
default: "Choose an option",
},
tabindex: {
type: Number,
required: false,
default: 0,
},
},
data() {
return {
open: false,
selected: this.setDefaultValue(),
};
},
mounted() {
this.$emit("input", this.selected);
},
methods: {
setDefaultValue () {
if (this.defaultValue == '' && this.options.length > 0) {
return this.options[0];
}
return { name: this.defaultValue};
}
}
})
new Vue({
el: '#demo',
data() {
return {
selected: "",
variationId: "",
selectedObject: null,
product: {
attributes: {
variations: [{id: 1, name: 'name1', description: 'desc1'}, {id: 2, name: 'name2', description: 'desc2'},{id: 3, name: 'name3', description: 'desc3'},]
}
}
};
},
computed: {
getVariations() {
return this.product.attributes.variations
}
},
methods: {
getSelected(opt) {
this.selectedObject = opt;
this.selected = opt.description;
this.variationId = opt.id;
},
submit() {
if (!this.selectedObject.id) alert('pls select option')
else console.log(this.selectedObject)
}
},
})
Vue.config.productionTip = false
Vue.config.devtools = false
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="demo">
<button #click="submit">submit</button>
<Dropdown
:options="getVariations"
class="select"
#input="getSelected"
/>
</div>
Can anyone help with this problem? How can I emit two properties from child component to parent on select input change? I can submit the value, see below, but would like to emit the value and the name property of segmentLocations object. This is the child component:
<template>
<div class="container">
<div>
<select v-model="selectedSegmentValue" v-on:change="$emit('selectLocation', $event.target.value)">
<option selected value="">Choose your location...</option>
<option v-for="segmentLocation in segmentLocations"
:value="segmentLocation.value"
:key="segmentLocation.value">
{{ segmentLocation.name }}>
</option>
</select>
</div>
</div>
</template>
<script>
export default {
data() {
return {
segmentLocations: [
{ value: "Residential", name: 'Residential building' },
{ value: "Workplace", name: 'Workplace' },
{ value: "Hospitality", name: 'Hospitality or Retail' },
{ value: "Real Estate", name: 'Real Estate' },
{ value: "Commercial Parking", name: 'Commercial Parking' },
{ value: "Fleets", name: 'Fleets' },
{ value: "Cities & Governments", name: 'Cities & Governments' },
{ value: "Corridor", name: 'Highway, Corridor or Petrol Station' }
],
}
}
};
</script>
And this is the parent:
<template>
<Segments
v-on:selectLocation="quote.selectedSegmentValue = $event"
:selectedValue="quote.selectedSegmentValue">
</Segments>
</template>
<script>
export default {
data() {
return {
quote: {
selectedSegmentValue: "",
selectedSegmentName: ""
},
};
},
</script>
I think the existing answers and mine share a similar technique, but I created a couple of simplified sample components based on your components.
Child component:
<template>
<div class="emit-two-properties">
<div class="form-group">
<label for="segment-location">Segment Location</label>
<select class="form-control" id="segment-location"
v-model="segmentLocation" #change="selectSegmentLocation">
<option v-for="(segLoc, index) in segmentLocations" :key="index"
:value="segLoc">{{ segLoc.name }}</option>
</select>
</div>
</div>
</template>
<script>
export default {
data() {
return {
segmentLocation: {},
segmentLocations: [
{ value: "Residential", name: 'Residential building' },
{ value: "Workplace", name: 'Workplace' },
{ value: "Hospitality", name: 'Hospitality or Retail' },
{ value: "Real Estate", name: 'Real Estate' },
{ value: "Commercial Parking", name: 'Commercial Parking' },
{ value: "Fleets", name: 'Fleets' },
{ value: "Cities & Governments", name: 'Cities & Governments' },
{ value: "Corridor", name: 'Highway, Corridor or Petrol Station' }
],
}
},
methods: {
selectSegmentLocation() {
this.$emit('select-segment-location-event', this.segmentLocation);
}
}
}
</script>
Parent component:
<template>
<div class="parent">
<h4>Parent.vue</h4>
<div class="row">
<div class="col-md-6">
<form #submit.prevent="submitForm">
<emit-two-properties #select-segment-location-event="updateSegmentLocation" />
<button class="btn btn-secondary">Submit</button>
</form>
<p><span>Selected Segment Location Value:</span>{{ segmentLocation.value }}</p>
<p><span>Selected Segment Location Name:</span>{{ segmentLocation.name }}</p>
</div>
</div>
</div>
</template>
<script>
import EmitTwoProperties from './EmitTwoProperties'
export default {
components: {
EmitTwoProperties
},
data() {
return {
segmentLocation: {}
}
},
methods: {
updateSegmentLocation(segLoc) {
this.segmentLocation = segLoc;
}
}
}
</script>
you can create a method to get name and value from event.target (remove value from the end of child emit):
changeSelectedSegment(selected){
this.selectedSegmentName = selected.name
this.selectedSegmentValue = selected.value
}
in the parent change v-on:selectLocation to v-on:selectLocation="changeSelectedSegment($event)"
you can define a method like this (this method emit an object with name and value properties to parent
)
methods: {
selectLocation(event){
if(event.target.value !== ''){
const item = this.segmentLocations.find( item => item.value === event.target.value)
this.$emit('selectLocation', {
name: item.name,
value: event.target.value
})
}
}
},
and change this line :
<select v-model="selectedSegmentValue" v-on:change="$emit('selectLocation', $event.target.value)">
to this:
<select v-model="selectedSegmentValue" v-on:change="selectLocation">
I have an array of names that I loop over using v-for I'm trying to filter these results when a user starts typing in a search box.
I've added my code below for reference if I do my loop as v-for="entry in entries" then it output the array but doesn't work with the computed and filteredList function
<template>
<div class="container-flex">
<div class="entries">
<div class="entries__header">
<div class="entries__header__title">
<p>Entries</p>
</div>
<div class="entries__header__search">
<input
type="text"
name="Search"
class="input input--search"
placeholder="Search..."
v-model="search">
</div>
</div>
<div class="entries__content">
<ul class="entries__content__list">
<li v-for="entry in filteredList">
{{ entry.name }}
</li>
</ul>
</div>
</div>
</div>
</template>
<script>
import addEntry from '#/components/add-entry.vue'
export default {
name: 'entry-list',
search: '',
components: {
addEntry
},
data: function() {
return {
entries: [
{
name: 'Paul'
},
{
name: 'Barry'
},
{
name: 'Craig'
},
{
name: 'Zoe'
}
]
}
},
computed: {
filteredList() {
return this.entries.filter(entry => {
return entry.name.toLowerCase().includes(this.search.toLowerCase())
})
}
}
}
Try to move the search prop in to data option like this:
export default {
name: 'entry-list',
components: {
addEntry
},
data: function() {
return {
search: '',
entries: [
{
name: 'Paul'
},
{
name: 'Barry'
},
{
name: 'Craig'
},
{
name: 'Zoe'
}
]
}
},
computed: {
filteredList() {
if(this.search === '') return this.entries
return this.entries.filter(entry => {
return entry.name.toLowerCase().includes(this.search.toLowerCase())
})
}
}
}
Also add a check if the search prop is empty to return the full entries list.
Demo fiddle
Struggling to sort out how to get a selected value from a Typeahead component to pass back to the parent component. I'm allowing the user to search from a variety of data to link a record to a post. Once the user clicks one of the typeahead drop-down records, I pass the item to the sendlink method - I've checked that the data passes ok. When I do the emit using the selected-link event, I'm not getting the data in the parent component.
PostList.vue
<template>
<div>
<div v-if='posts.length === 0' class="header">There are no posts yet!</div>
<form action="#" #submit.prevent="createPost()" class="publisher bt-1 border-fade bg-white" autocomplete="off">
<div class="input-group">
<input v-model="post.content" type="text" name="content" class="form-control publisher-input" placeholder="What's the lastest?" autofocus>
<span class="input-group-btn">
<button type="submit" class="btn btn-primary">Post</button>
</span>
</div>
<span class="publisher-btn file-group">
<i class="fa fa-camera file-browser"></i>
<input type="file">
</span>
</form>
<div #click="doit" v-on:selected-link="onSelectedLink">{{ modellink.name }}</div>
<typeahead
source="/api/typeahead"
placeholder="Link Post to Trip, Business, etc"
filter-key="title"
:start-at="3">
</typeahead>
<post v-for="post in posts"
:key="post.id"
:post="post"
#post-deleted="deletePost($event)">
</post>
</div>
</template>
<script>
var axios = require("axios");
import post from './PostItem.vue';
import typeahead from './Typeahead.vue';
export default {
components: {
post,
typeahead
},
props: ['postableId', 'postableType', 'model'],
data: function() {
return {
modellink: {
"name": "n/a",
"description": "",
"id": null,
"model": "n/a"
},
post: {
id: 1,
content: "",
edited: false,
created_at: new Date().toLocaleString(),
user: {
id: 1,
name: '',
}
},
posts: [
{
id: 1,
content: "",
edited: false,
created_at: new Date().toLocaleString(),
user: {
id: 1,
name: '',
}
}
]
};
},
created() {
this.fetchPostsList();
},
methods: {
onSelectedLink: function (talink) {
alert(JSON.stringify(talink, null, 4));
this.link = talink
},
doit() {
alert(JSON.stringify(this.modellink, null, 4));
},
fetchPostsList() {
if( this.postableId ) {
axios.get('/api/' + this.postableType + '/' + this.postableId + '/posts').then((res) => {
this.posts = res.data;
});
} else {
axios.get('/api/post').then((res) => {
//alert(JSON.stringify(res.data[0], null, 4));
this.posts = res.data;
});
}
},
createPost() {
axios.post('api/post', {content: this.post.content, user_id: Laravel.userId, vessel_id: Laravel.vesselId })
.then((res) => {
this.post.content = '';
// this.post.user_id = Laravel.userId;
// this.task.statuscolor = '#ff0000';
this.edit = false;
this.fetchPostsList();
})
.catch((err) => console.error(err));
},
deletePost(post) {
axios.delete('api/post/' + post.id)
.then((res) => {
this.fetchPostsList()
})
.catch((err) => console.error(err));
},
}
}
</script>
Typeahead.vue
<template>
<div>
<input
v-model="query"
#blur="reset"
type="text"
class="SearchInput"
:placeholder="placeholder">
<transition-group name="fade" tag="ul" class="Results">
<li v-for="item in items" :key="item.id">
<span #click="sendlink(item)">
<strong>{{ item.name }}</strong> - <small>{{ item.model }}</small><br>
<small>{{ item.description }}</small>
</span>
</li>
</transition-group>
<p v-show="isEmpty">Sorry, but we can't find any match for given term :( </p>
</div>
</template>
<script>
var axios = require("axios");
export default {
name: 'Typeahead',
props: {
modellink: {
type: Object,
required: false
},
source: {
type: [String, Array],
required: true
},
filterKey: {
type: String,
required: true
},
startAt: {
type: Number,
default: 3
},
placeholder: {
type: String,
default: ''
}
},
data() {
return {
items: [],
query: '',
taitem: ''
}
},
computed: {
lookup() {
if(this.query.length >= this.startAt) {
axios.get(this.source + '/' + this.query).then((res) => {
this.items = res.data;
return res.data;
});
}
},
isEmpty() {
if( typeof this.lookup === 'undefined' ) {
return false
} else {
return this.lookup.length < 1
}
}
},
methods: {
sendlink: function (taitem) {
this.$emit('selected-link', taitem);
},
reset() {
this.query = ''
}
}
}
</script>
In your PostList.vue, move the v-on:selected-link="onSelectedLink" from the div to typeahead like below. When emitting an event from child to parent, the listener on the parent needs to be on the child component tag for it to work.
<div #click="doit">{{ modellink.name }}</div>
<typeahead
source="/api/typeahead"
placeholder="Link Post to Trip, Business, etc"
filter-key="title"
:start-at="3"
v-on:selected-link="onSelectedLink">
</typeahead>