Vue 3 Select-Option, make first option the default - javascript

I'm trying to make the first select option default, therefore showing right away when the page loads. I thought since v-bind:selected is a booleanish attribute I could just use something simple like index === 0 to select the first by default but this doesn't seem to be working, and there is no option selected on page load. I've debugged and the indexes are incrementing normally, there's nothing weird going on there. Is there just something silly I'm missing maybe? selectedThingId is a ref with a default value of zero. I've also looked in the html changing this number and nothing is ever selected!
Here's the template code:
<select name="select" id="select" v-model="selectedThingId" #change="onChangeFunction">
<option v-for="(thing, index) in things"
:value="thing.id"
:key="thing.id"
:selected="index === 0"
>
{{ thing }}
</option>
</select>

Try to set selectedThingId in onMounted hook :
const { ref, reactive, onMounted } = Vue
const app = Vue.createApp({
setup() {
const selectedThingId = ref(null)
const things = reactive([{id: 3, name: 'aaa'}, {id: 5, name: 'bbb'}, {id: 9, name: 'ccc'}])
onMounted(() => selectedThingId.value = things[0].id)
const onChangeFunction =() => {}
return { things, selectedThingId, onChangeFunction }
}
})
app.mount('#demo')
<script src="https://unpkg.com/vue#3/dist/vue.global.prod.js"></script>
<div id="demo">
<select name="select" id="select" v-model="selectedThingId" #change="onChangeFunction">
<option v-for="(thing, index) in things" :key="thing.id"
:value="thing.id"
>
{{ thing.name }}
</option>
</select>
{{ selectedThingId }}
</div>

Related

"TypeError: Cannot read properties of undefined (reading 'id')"

I'm building the website but I got error in the browser's console, what's the problem and how to fix it. And another promblem is I can not get value from first and second select but input is fine.
Here's the code.
const Refund = () => {
const [payment, setPayment] = useState({
rpayment: ""
});
const [reason, setReason] = useState({
rrefund: ""
});
const [date, setDate] = useState({
ddate: ""
});
const handleInputOnChange = ({ currentTarget: select, input}) => {
const tempPayment = {...payment};
tempPayment[select.id] = select.value;
setPayment(tempPayment);
console.log(tempPayment)
const tempReason = {...reason};
tempReason[select.id] = select.value;
setReason(tempReason);
console.log(tempReason)
const tempDate = {...date};
tempDate[input.id] = input.value;
setDate(tempDate);
console.log(tempDate)
};
return (
<div className="refund">
<fieldset className="refund__container">
<legend align="center">
</legend>
<form className="refund__form">
<div className="refund__form__container">
<div className="refund__form__item" >
<select name="rpayment" id="rpayment" value={payment["rpayment"]} onChange={handleInputOnChange}>
<option value="none" selected disabled hidden>ช่องทางการรับเงิน</option>
<option value={payment["rpayment"]}>Credit Card</option>
<option value={payment["rpayment"]}>Paypal</option>
<option value={payment["rpayment"]}>Wallet</option>
</select>
</div>
<div className="refund__form__item">
<input
type="date"
id="rdate"
name="rdate"
value={reason["ddate"]}
onChange={handleInputOnChange}
/>
</div>
<div className="refund__form__item">
<select name="reason" id="reason" value={reason["rrefund"]} onChange={handleInputOnChange}>
<option value="none" selected disabled hidden>เหตุผลที่ต้องการเงินคืน</option>
<option value={reason["rrefund"]}>I need to cancle</option>
<option value={reason["rrefund"]}>It's not comfortable</option>
<option value={reason["rrefund"]}>Too expensive</option>
</select>
</div>
</div>
</form>
<input type="submit" className="refund__btn" value="Submit" />
</fieldset>
</div>
)
}
export default Refund;
Thank you in advance!!
There's such a huge amount of problems that I don't even know which one to start from.
Well let's start with state. One state is enough.
const [formState, setFormState] = useState({
rpayment: "none", // refers to paymentMethods items
rrefund: "expensive", // refers to refundReasons value field
ddate: "" // here's input string
})
Second - selects and their change handlers.
// simple approach
const paymentMethods = ['none', 'Credit', 'Paypal', 'Wallet'];
// complex approach
const refundReasons = [
{
title: 'I need to cancle',
value: 'need'
},
{
title: 'Too expensive',
value: 'expensive'
}
// add some more
]
Select handler. Here we are currying function and returning a new one with enclosed stateFieldName referring to our composed state field.
But I'm not actually sure if we must use event.target or event.currentTarget - try both, one will work for sure.
const selectHandler = (stateFieldName) => (event) => {
setFormState({
...formState,
[stateFieldName]: event.currentTarget.value
})
}
Render selects (no styles, sorry)
// no need to apply hidden fields
<select
onChange={selectHandler('rpayment')} // yes, this is a function call, it returns a new function
>
{paymentMethods.map((item) => (
<option
key={item}
value={item}
selected={item === formState.rpayment}
>
{item}
</option>
))
}
</select>
// complex select
<select
onChange={selectHandler('rrefund')}
>
{refundReasons.map(({title, value}) => (
<option
key={value}
value={value}
selected={value === formState.rrefund}
>
{title}
</option>
))}
</select>
// input
<input
type="date"
value={formState.ddate}
onChange={selectHandler(ddate)} // probably this handler will work here too
/>
And finally submit button. If your form is supposed to send async request, your submit button doesn't need value prop and any handler. I general it must have type='submit' prop but the form itself must have a submit hander and send data stored in your state.
Something like
<form
onSubmit={() => {
axios.post('/some/api', {
data: formState
})
.then(/* you logic here */)
}}
>
// your inputs here
</form>
Sorry for approximations and possible mistakes, I hope you got the main idea.

How to filter an array on click in react?

So, basically, I'm making a website where you can search for Minecraft hacked clients. There is a search bar on the website, but you have to search in exact terms (different topic lol). Basically on the click of a button (search button) I then want to filter using the search term, (not automatically as I have it now) I've been searching but cant find a way to do it.
Code ->
import CountUp from 'react-countup';
import { Link } from 'react-router-dom';
import '../App.css';
/*Components ->*/
import Client from '../components/client.js'
function App() {
const [search, setSearch] = React.useState({
searchname: ''
});
**Array containing client data ->** const [client, setClient] = React.useState([
{ safestatus: 'safe-status-green', safe: 'Safe (Verified)', name: 'Impact', price: 'Free', mcversion: '1.11.2 - 1.16.5', type: 'Injected', compat: 'None (Stand alone client)', desc: 'The Impact client is an advanced utility mod for Minecraft, it is packaged with Baritone and includes a large number of useful mods.', screen: 'https://impactclient.net/', download: 'https://impactclient.net/'},
{ safestatus: 'safe-status-green', safe: 'Safe (Verified)', name: 'Future', price: '15€', mcversion: '1.8.9 - 1.14.4', type: 'Injected', compat: 'None (Stand alone client)', desc: 'Vanilla, OptiFine, Forge and Liteloader support, Easy to use account manager, Auto-reconnect mod.', screen: 'https://www.futureclient.net/', download: 'https://www.futureclient.net/'}
]);
const updateSearch = (event) => {
event.persist();
setSearch((prev) => ({
...prev,
[event.target.name]: event.target.value
}));
};
return (
<body>
<div className='header'><Link to='/'>Hacked Hub</Link><div className='header-links'><Link to='/about-us'>About Us</Link> | <Link to='/faq'>FAQ</Link></div></div>
<div className='counter'><CountUp end={16} duration={0.5}></CountUp>+</div>
<div className='counter-desc'><div className='code'>hacked clients</div> and counting...</div>
<div className='nxt-tab'>
<input name='searchname' value={search.searchname} onChange={updateSearch} placeholder='Search'></input>
<select name='price'>
<option value='Free'>Free</option>
<option value='Paid'>Paid</option>
</select>
<select name='safe'>
<option value='Safe'>Safe</option>
<option value='Probably Safe'>Probably Safe</option>
<option value='Not Safe'>Not Safe (BE CAREFUL!)</option>
</select>
<select name='mcver'>
<option value='1.8.9'>1.8.9</option>
<option value='1.12.2'>1.12.2</option>
<option value='1.16.5'>1.16.5</option>
<option value='1.17+'>1.17+</option>
</select>
<select name='type'>
<option value='Main'>Injected</option>
<option value='Side'>Mod</option>
</select>
<select name='compatibility'>
<option value='With Most Other Clients'>With Most Other Clients</option>
<option value='Stand Alone'>Stand Alone</option>
</select>
<div className='client-warning'><div className='safe-status-red'><div className='code'>⚠ WARNING ⚠</div></div> Only download clients you know are <div className='code'>100%</div> safe! If we do find a client that is a <div className='code'>rat / virus / BTC miner</div> we will tag it as <div className='safe-status-red'><div className='code'>UNSAFE IMMEDIATELY</div></div>. The saftey warnings for clients are <div className='code'>MERE RECCOMENDATIONS</div>, please do thorough research before downloading any hacked client that you are not <div className='code'>100%</div> sure is safe. This page is also in <div className='code'>development</div>, meaning features are prone to break! So be careful!!!</div>
<div className='code'>Sponsored clients</div>
<h1>None XD</h1>
<div className='code'>Submitted clients</div>
{client
**Filtering the array then mapping it -> (This i want to do onClick)** .filter((client) => client.name === search.searchname)
.map((client, index) => {
return (
<Client
safestatus={client.safestatus}
safe={client.safe}
name={client.name}
price={client.price}
mcversion={client.mcversion}
type={client.type}
compat={client.compat}
desc={client.desc}
screen={client.screen}
download={client.download}
/>
);
})}
</div>
</body>
);
}
export default App;
Anyone know how I could do this onClick?
Use two pieces of state; one to track the value in your text field and the other to store the search term for filtering. Only set the latter when you click your button
const [ searchValue, setSearchValue ] = React.useState("")
const [ searchTerm, setSearchTerm ] = React.useState("")
const filteredClient = React.useMemo(() => {
if (searchTerm.length > 0) {
return client.filter(({ name }) => name === searchTerm)
}
return client
}, [ searchTerm, client ])
and in your JSX
<input
name="searchname"
placeholder="Search"
value={searchValue}
onChange={e => setSearchValue(e.target.value)}
/>
<!-- snip -->
<button
type="button"
onClick={() => setSearchTerm(searchValue)}
>Search</button>
<!-- snip -->
{filteredClient.map((client, index) => (
<Client .../>
))}

Auto-select first <select> option when loading async options in Vue 3

I have a <select> HTML element which I want to v-model bind to a ref value in Vue.js 3's setup() method. So when a user selects a different option, the Form.ProductID ref updates.
Here is the <select> code:
<select v-model="Form.ProductID" id="ProductID" name="ProductID">
<option value="1">Option A</option>
<option value="2">Option B</option>
<option value="3">Option C</option>
</select>
And setup():
export default {
name: "ComponentProductSelector",
setup() {
const Form = ref({
ProductID: '2',
Price: null,
Currency: null
})
onMounted(() => Form.value.ProductID)
document.querySelector("#ProductID option:first-of-type")
}
}
On first load in vue devtools, it shows the data as being:
Form (Object Ref)
ProductID: "[object HTMLOptionElement]"
When I select an option in the <select> element, Form.ProductID updates as expected and shows which option I selected e.g.:
Form (Object Ref)
ProductID: 3
The problem is that on the page first load, the <select> element is not selecting the option with value="2" even though I am hard coding it in the setup(). It just shows a blank option! However if I change the <select> element to the following code then it does:
<select ref="Form.ProductID" id="ProductID" name="ProductID">
<option value="1">Option A</option>
<option value="2">Option B</option>
<option value="3">Option C</option>
</select>
Now the option with value="2" is selected by default when the component is rendered, however the actual value of Form.ProductID does not update and vue devtools continues to show ProductID: "[object HTMLOptionElement]" as the data.
How can I get the <select> element to work using v-model and also select a default option when the component loads?
Answering the updated question in comments about how to select the first option when async loading. Once the data is loaded, set the value of Form to the first item in the options array (cloned to avoid mutating it), rather than manually manipulating the input DOM.
For example:
<select v-model="Form.ProductID" id="ProductID" name="ProductID" v-if="options">
<option v-for="option in options" :key="option.ProductID" :value="option.ProductID">
{{ option.ProductID }}
</option>
</select>
setup() {
const options = ref(null);
const Form = ref(null);
axios.get('...').then(response => {
options.value = response.data;
Form.value = { ...options.value[0] }; // Clone and select first option
});
return { options, Form }
}
There's a v-if on the <select> to delay its rendering until the data is ready.
Here's a demo:
const { createApp, ref } = Vue;
const app = createApp({
setup() {
const options = ref(null);
const Form = ref(null);
axios.get('https://jsonplaceholder.typicode.com/posts').then(response => {
options.value = response.data;
Form.value = { ...options.value[0] }; // Clone and select first option
});
return { options, Form }
}
});
app.mount("#app");
<div id="app">
<select v-model="Form.id" id="ProductID" name="ProductID" v-if="options">
<option v-for="option in options" :key="option.id" :value="option.id">
{{ option.id }}
</option>
</select>
</div>
<script src="https://unpkg.com/vue#next"></script>
<script src="https://unpkg.com/axios"></script>

Vue select change event firing before I trigger any action

I have a Vue app that seems to fire the change event even before I change the selection, recently tried the #input instead but still the same thing happens(as shown below)
Please note I have tried #change and #input and the event still fires on loading of the controls
Now this was working before I made css changes to scope the component, so that it doesn't effect the surrounding css. But cant fathom why this would make any difference.
Does anyone know why when adding the options tag and contents would make the change event fire?
<div class="form-group" :class="{formError: errors.has('formSection')}">
<label for="formSection">Section*</label>
{{ formModel }}
<select
v-model="formModel.idSection1"
class="form-control"
id="formSection"
name="formSection"
#input="onChangeSectionLevel1">
<option v-for="sectionLevel1 in formModel.sectionLevel1"
v-bind:value="sectionLevel1.value"
v-bind:key="sectionLevel1.id">
{{ sectionLevel1.value }}
</option>
</select>
<span v-if="errors.has('formSection')">This field is required</span>
</div>
As soon as I add in the options tag which loops through the items the onChangeSectionLevel1 function gets called. I thought it might be vee-validate but taken this out and still happens.
methods: {
onChangeSectionLevel1() {
alert("changed");
...
}
}
Update:
I have noticed that if I print out the object that is being bound, I get this which missing the idSection1 item.
{
"idSection2": null,
"idSection3": null,
}
If I then just put a dummy option as below then I can see my 3 data items including the idSection1 that is missing if I loop through with the v-for
<select
v-model="formModel.idSection1"
class="form-control"
id="formSection"
name="formSection"
#change="onChangeSectionLevel1">
<option>Hello World</option>
</select>
The data item still has the idSection1 listed
{
"idSection1": null,
"idSection2": null,
"idSection3": null
}
Many thanks in advance
Not really an answer, but the code above is find and works as expected in js fiddle
https://jsfiddle.net/andrewp37/a4obkhd5/1/
Code:
<div id="app">
<label for="formSection">Section*</label>
{{ formModel }}
<select
v-model="formModel.idSection1"
class="form-control"
id="formSection"
name="formSection"
#change="onChangeSectionLevel1">
<option v-for="sectionLevel1 in formModel.sectionLevel1"
v-bind:value="sectionLevel1.value"
v-bind:key="sectionLevel1.id">
{{ sectionLevel1.value }}
</option>
</select>
</div>
new Vue({
el: "#app",
data: {
formModel: {
idSection1: null,
sectionLevel1: [
{
id: 1,
value: "Section 1"
}
]
}
},
methods: {
onChangeSectionLevel1() {
alert("changed");
}
}
})
I had noticed that with lots of breakpoints added, the model was being replaces after the page was mounted.

How to get the text of the selected option using vuejs?

I want to get the text of a selected option input and display it somewhere else. I know how to do it using jQuery but I want to know how can we do it using Vuejs.
Here is how we do in jQuery. I mean the text of Selected Option not the value.
var mytext = $("#customerName option:selected").text();
Here is my HTML
<select name="customerName" id="">
<option value="1">Jan</option>
<option value="2">Doe</option>
<option value="3">Khan</option>
</select>
{{customerName}}
Now how can I display the selected option under it. like Jan, Doe, Khan ?
Instead of define the value only as the id, you can bind the selected value with an object with two attributes: value and text.
For example with products:
<div id="app">
<select v-model="selected">
<option v-for="product in products" v-bind:value="{ id: product.id, text: product.name }">
{{ product.name }}
</option>
</select>
</div>
Then you can access to the text through the "value":
<h1>Value:
{{selected.id}}
</h1>
<h1>Text:
{{selected.text}}
</h1>
Working example
var app = new Vue({
el: '#app',
data: {
selected: '',
products: [
{id: 1, name: 'A'},
{id: 2, name: 'B'},
{id: 3, name: 'C'}
]
}
})
<div id="app">
<select v-model="selected">
<option v-for="product in products" v-bind:value="{ id: product.id, text: product.name }">{{ product.name }}
</option>
</select>
<h1>Value:
{{selected.id}}
</h1>
<h1>Text:
{{selected.text}}
</h1>
</div>
<script src="https://unpkg.com/vue#2.4.4/dist/vue.js"></script>
I had this issue, where I needed to get a data attribute from a selected option, this is how I handled it:
<select #change="handleChange">
<option value="1" data-foo="bar123">Bar</option>
<option value="2" data-foo="baz456">Baz</option>
<option value="3" data-foo="fiz789">Fiz</option>
</select>
and in the Vue methods:
methods: {
handleChange(e) {
if(e.target.options.selectedIndex > -1) {
console.log(e.target.options[e.target.options.selectedIndex].dataset.foo)
}
}
}
But you can change it to get innerText or whatever. If you're using jQuery you can $(e).find(':selected').data('foo') or $(e).find(':selected').text() to be a bit shorter.
If you are binding a model to the select element, it will only return the value (if set) or the contents of the option if there is no value set (like it would on submitting a form).
** EDIT **
I would say that the answer #MrMojoRisin gave is a much more elegant way of solving this.
The below code worked to get the Text from the selected option. I added a v-on:change , which calls a function onChange() to the select element.
methods:{
onChange: function(e){
var id = e.target.value;
var name = e.target.options[e.target.options.selectedIndex].text;
console.log('id ',id );
console.log('name ',name );
},
<select name="customerName" id="" v-on:change="onChangeSite($event)">
<option value="1">Jan</option>
<option value="2">Doe</option>
<option value="3">Khan</option>
</select>
Assuming you have a customers list and a selected customer on your model, an example like below should work perfect:
<select v-model="theCustomer">
<option :value="customer" v-for="customer in customers">{{customer.name}}</option>
</select>
<h1>{{theCustomer.title}} {{theCustomer.name}}</h1>
I guess your values should be in the JS. Then you can easily manipulate it. Simply by adding:
data: {
selected: 0,
options: ['Jan', 'Doe', 'Khan']
}
Your markup will be cleaner:
<select v-model="selected">
<option v-for="option in options" value="{{$index}}">{{option}}</option>
</select>
<br>
<span>Selected: {{options[selected]}}</span>
Here is the update JSFiddle
As th1rdey3 pointed out, you might want to use complex data and values couldn't simple be array's indexes. Still you can use and object key instead of the index. Here is the implementation.
You can use Cohars style or you can use methods too. Data is kept in options variable. The showText method finds out the selected values text and returns it. One benefit is that you can save the text to another variable e.g. selectedText
HTML:
<div id='app'>
<select v-model="selected">
<option v-for="option in options" v-bind:value="option.value">
{{ option.text }}
</option>
</select>
<br>
<span>Selected: {{ showText(selected) }}</span>
</div>
JAVASCRIPT:
var vm = new Vue({
el: '#app',
data: {
selected: 'A',
selectedText: '',
options: [{
text: 'One',
value: 'A'
}, {
text: 'Two',
value: 'B'
}, {
text: 'Three',
value: 'C'
}]
},
methods: {
showText: function(val) {
for (var i = 0; i < this.options.length; i++) {
if (this.options[i].value === val){
this.selectedText = this.options[i].text;
return this.options[i].text;
}
}
return '';
}
}
});
JSFiddle showing demo
I tried to use the following suggestion of MrMojoRisin's
v-bind:value="{ id: product.id, text: product.name }"
However for me this was resulting in that Object's toString() representation being assigned to value, i.e. [object Object]
What I did instead in my code was to call JSON.stringify on a similar object bound to the value:
v-bind:value="JSON.stringify({id: lookup[lookupIdFields[detailsSection]], name: lookup.Name})"
Then of course I can convert it back to an object using JSON.parse and get the requisite id and name values from the result
الفترة
اختر الفترة
{{ period.name }}
<label for="period_id" class="block font-medium text-sm text-gray-700">الفترة</label>
<select v-model="form.period_id" id="period_id" class="border-gray-300">
<option disabled value="">اختر الفترة</option>
<option v-for="period in periods" :value="period.id" :selected="building.period_id === period.id ? 'selected' : null">
{{ period.name }}
</option>
</select>
Outside of the template I access the name of the option like this:
let option_name = this.options[event.target.options.selectedIndex].name
To do this take this approach to set up your template:
Defined your options in an array like
[{"name":"Bar","value":1},{"name":"Baz","value":2}] ... etc
Put your options in the data function of the component
<script>export default {function data(){return { options }}}</script>
Load the options in the template using v:for:
<option v-for="option in options" v-bind:value="option.value">{{option.name}}</option>
Use #change="getOptionName" on the select element:
<select #change="getOptionName">
In your methods get the name:
getOptionName(event){ let option_name = this.options[event.target.options.selectedIndex].name }
Note in this case the object options in event.target.options does not have anything to do with your data named options ... hopefully that will avoid confusion
So a more advanced approach, but I believe when it is all set up correctly getting the name is not terrible, although It could be easier.
You can find it out in the Vue documentation here : http://vuejs.org/guide/forms.html#Select
Using v-model :
<select v-model="selected">
<option selected>A</option>
<option>B</option>
<option>C</option>
</select>
<br>
<span>Selected: {{ selected }}</span>
In your case, I guess you should do :
<select name="customerName" id="" v-model="selected">
<option>Jan</option>
<option>Doe</option>
<option>Khan</option>
</select>
{{selected}}
Here is a working fiddle : https://jsfiddle.net/bqyfzbq2/
I think some of the answers here are a bit too complex, so I'd like to offer an easier solution. I'm only going to use an event handler in the example and leave out things like model binding, etc.
<select #change="yourCustomMethod">
<option value="1">One</option>
<option value="2">Two</option>
</select>
Then in your Vue instance:
...
methods: {
yourCustomMethod: function(event) {
var key = event.target.value, // example: 1
value = event.target.textContent; // example: One
}
}
...

Categories

Resources