b-input model has value of type string - vue.js

I am trying to create a form with a number field.
<b-input v-model="testNumber" type="number"/>
On my data, I have a simple number var.
data() {
return {
testNumber: 10,
}
},
However when I trace testNumber it is a string
{{ typeof testNumber }} // String

You can add a modifier to the v-model.
(https://v2.vuejs.org/v2/guide/forms.html#number)
<b-form-input v-model.number="testNumber" />
UPDATE
Don't use the v-model.number this as bootstrap-vue recommens not to do so:
v-model modifiers .number and .trim can cause unexpected cursor jumps
when the user is typing (this is a Vue issue with v-model on custom
components). Avoid using these modifiers.
But use as b-form-input suggests:
To get around this, <b-form-input> and <b-form-textarea> have two
boolean props trim and number which emulate the native Vue v-model
modifiers .trim and .number respectively.
<b-form-input v-model="testNumber" :number="true" />

The type=number default return value type is string. you can see here HTML input elements are documented to return string representing a number
For changing this behavior of model value, you need to convert the your value when input is changing. Like below example
Please below code snippet :
new Vue({
el: '#app',
data() {
return {
number:10,
testNumber:100
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<input v-model="number" type="number"/>
<p>type of number : {{typeof number}}</p>
<input v-model="testNumber" type="number" #input="e => testNumber = +e.target.value" />
<p>type of testNumber: {{typeof testNumber}}</p>
</div>
Bootstrap-vue example
In this example, you can use value by getting .valueAsNumber and same as above you can use +.value.
new Vue({
el: '#app',
methods: {
updateVm(e) {
this.testNumber1 = e.target.valueAsNumber;
}
},
data() {
return {
number: null,
testNumber: null,
testNumber1: null
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<!-- Add this to <head> -->
<!-- Load required Bootstrap and BootstrapVue CSS -->
<link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap/dist/css/bootstrap.min.css" />
<link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue.min.css" />
<!-- Load polyfills to support older browsers -->
<script src="//polyfill.io/v3/polyfill.min.js?features=es2015%2CIntersectionObserver" crossorigin="anonymous"></script>
<!-- Load Vue followed by BootstrapVue -->
<script src="//unpkg.com/vue#latest/dist/vue.min.js"></script>
<script src="//unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue.min.js"></script>
<div id="app" style="padding:10px;">
<template>
<b-container fluid>
<b-row>
<b-col><b-input placeholder="First number" v-model="number" type="number"/></b-col>
<b-col>{{typeof number}}</b-col>
</b-row>
<b-row>
<b-col><b-input placeholder="2nd number" v-model="testNumber" type="number" v-on:input="$v => testNumber = +$v"/></b-col>
<b-col>{{typeof testNumber}}</b-col>
</b-row>
<b-row>
<b-col><b-input placeholder="3rd number" v-model="testNumber1" type="number" v-on:input="updateVm(event)"/></b-col>
<b-col>{{typeof testNumber1}}</b-col>
</b-row>
</b-container>
</template>
</div>

<template>
<b-container fluid>
<b-row class="my-1" v-for="type in types" :key="type">
<b-col sm="3">
<label :for="`type-${type}`">Type <code>{{ type }}</code>:</label>
</b-col>
<b-col sm="9">
<b-form-input :id="`type-${type}`" :type="type"></b-form-input>
</b-col>
</b-row>
</b-container>
</template>
<script>
export default {
data() {
return {
types: [
'number'
]
}
}
}
</script>

Related

Vue 3 v-model not working with custom input component in Nuxt.js

I am trying to use a custom Vue input component with a v-model, but the value is not updating in the parent component. I am using Vue 3 with Nuxt.js.
Here is my custom input component:
<template>
<input
type="text"
:value="modelValue"
#input="$emit('update:modelValue', $event.target.value)"
:placeholder="placeholder"
class="border border-gray-300 rounded-lg w-full p-2 text-black m-1"
/>
</template>
<script setup>
const props = defineProps({
modelValue: String,
placeholder: String,
});
const emit = defineEmits(["update:modelValue"]);
</script>
<script>
export default {
name: "MyInput",
};
</script>
And here is how I am using it in the parent component:
<template>
<div>
<MyInput v-model="inputValue" placeholder="Enter a value" />
</div>
</template>
<script>
import MyInput from "./MyInput.vue";
export default {
name: "MyParentComponent",
components: {
MyInput,
},
data() {
return {
inputValue: "",
};
},
};
</script>
The inputValue data property is not being updated when I type in the input field. Can someone help me figure out what I'm doing wrong?
I have a Vue 3 project without Nuxt.js using this exact same code and it works there.
there is no mistake in your codes I used exact same code and it's working with no problem
but:
there is no need to import components (Nuxt supports auto import)
your file's structure should be like this:
|__components
|
|__MyInput.vue
|
|__MyParentComponent.vue
|__app.vue
MyInput.vue
<template>
<input
type="text"
:value="modelValue"
#input="$emit('update:modelValue', $event.target.value)"
:placeholder="placeholder"
class="border border-gray-300 rounded-lg w-full p-2 text-black m-1"
/>
</template>
<script setup>
const props = defineProps({
modelValue: String,
placeholder: String,
});
const emit = defineEmits(["update:modelValue"]);
</script>
the dev tools will show the name of the component. So no additional naming is necessary.
MyComponent.vue
<template>
<div>
<MyInput v-model="inputValue" placeholder="Enter a value" />
<p>{{ inputValue }}</p>
</div>
</template>
<script setup>
let inputValue = ref("");
</script>
app.vue
<template>
<MyComponent />
</template>

Dynamic Placeholder in Vue 3 with Global Component

I am trying to set dynamic text for the placeholder attribute on my search bar. Depending on the page, I want the text in the search bar to be different (I will define it in data()).
However, since the search bar component is a global component, it doesn't seem to be editable.
(As you see below is my try, I did it with v-model based on Vue docs, however when I try with placeholder it doesn't work...)
Snippet 1 - Search bar component
<template>
<!-- Search Componenet -->
<div class="mx-5 mb-3 form-group">
<br>
<input class="mb-5 form-control" type="search" :placeholder="placeholderValue" :value="modelValue" #load="$emit('update:placeholderValue', $event.target.value)" #input="$emit('update:modelValue', $event.target.value)" />
</div>
</template>
<script>
export default {
props: ['modelValue', 'placeholderValue'],
emits: ['update:modelValue', 'update:placeholderValue']
}
</script>
Snippet 2 - Album.vue
<template>
<div class="AlbumView">
<h1>{{header}}</h1>
<h2>{{header2}}</h2>
<br>
<!-- Search Componenet -->
<SearchComponent :placeholder="placeholderValue" v-model="searchQuery" />
<!-- Dynamic Song Route Button -->
<div class="button-container-all mx-5 pb-5">
<div v-for="item in datanew" :key="item.id">
{{ item.album }}
</div>
</div>
</div>
</template>
<script>
import { datatwo } from '#/data2'
export default {
data() {
return {
placeholderValue: "Search for Albums here...",
datanew: datatwo,
searchQuery: null,
header: "Browse by Album",
header2: "Select an Album:",
publicPath: process.env.BASE_URL
};
},
}
</script>
If this is possible?
If you want to do it with v-model (the Childcomponent changes the value of the placeholder) you have to use v-model:placeholder for it to work.
And also placeholderValue is not the way to go the "Value" at the end of a prop is only needed for modelValue which is the default v-model-binding (v-model="") but if you want named v-model-binding (v-model:placeholder="") you do not want to add the "Value" in the props and emits arrays.
Example:
usage of SearchComponent
<SearchComponent :placeholder="'placeholderValue'" v-model="searchQuery" />
instead of 'placeholderValue' you can put any string you want or variable. I just put the string 'placeholderValue' as an example.
SearchComponent
<template>
<!-- Search Componenet -->
<div class="mx-5 mb-3 form-group">
<br>
<input class="mb-5 form-control" type="search" :placeholder="placeholder" :value="modelValue" #load="$emit('update:placeholderValue', $event.target.value)" #input="$emit('update:modelValue', $event.target.value)" />
</div>
</template>
<script>
export default {
name: "SearchComponent",
props: ['modelValue', 'placeholder'],
emits: ['update:modelValue'],
}
</script>
<style scoped>
</style>

Use radio button to show or hide divs in Vue 3

In my Vue 3.0.5 app, I want to switch between <div> elements by radio buttons, i.e. only show one element at a time. It works if I prefix the value argument in the input element with v-bind although I am already assign the value with v-model. However, I don't understand why.
I have also used radio buttons to select for ascending/descending order (I implemented a computed function for sorting which used the variable for asc/desc): This worked without v-bind.
It's not clear to me why the behaviour is different. Can anyone explain?
Example with v-bind
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/vue#3.0.5"></script>
</head>
<body>
<div id="app">
<div>
<label><input type="radio" v-model="showFirst" v-bind:value="true" checked />First View</label>
<br />
<label><input type="radio" v-model="showFirst" v-bind:value="false" />Second View</label>
</div>
{{ showFirst }}
<div v-if="showFirst">First view</div>
<div v-else>Second view</div>
</div>
<script>
var app = Vue.createApp({
data() {
return {
showFirst: true,
};
},
});
app.mount("#app");
</script>
</body>
</html>
Example without v-bind
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/vue#3.0.5"></script>
</head>
<body>
<div id="app">
<div>
<label><input type="radio" v-model="showFirst" value="true" checked />First View</label>
<br />
<label><input type="radio" v-model="showFirst" value="false" />Second View</label>
</div>
{{ showFirst }}
<div v-if="showFirst">First view</div>
<div v-else>Second view</div>
</div>
<script>
var app = Vue.createApp({
data() {
return {
showFirst: true,
};
},
});
app.mount("#app");
</script>
</body>
</html>
v-model is a two-way binding shorthand for :modelValue="" together with #update:modelValue="". In Vue 2, it used to be :value="" and #input="". You should only use either v-model or the other one.
When you add a property value="true", it means that you are passing the string "true" as a value to the component. When using a colon before value, you pass an expression, which evaluates to a value. So, adding :value="true" actually passes to evaluated expression down, which is a boolean (it could also be a variable, a calculation etc.). v-bind: is equal to just a colon :, usually the short form is used.
See also the docs to v-model from Vue.
The problem is not that you're not binding the value, it is that the value is not set as a boolean. If you use v-bind then it gets converted to a boolean.
So you should use v-bind:value="true" (or the shorthand `:value="true")
otherwise, you could do v-if="showFirst === 'true'"
or, for fun, get creative with number values (input: value="0" and then v-if: Boolean(parseInt(showFirst))
example
var app = Vue.createApp({
data() {
return {
showFirst: true,
};
},
});
app.mount("#app");
<script src="https://unpkg.com/vue#3.0.5"></script>
<div id="app">
<div>
<label><input type="radio" v-model="showFirst" value="true" />First View</label
><br />
<label><input type="radio" v-model="showFirst" value="false" />Second View</label>
</div>
{{ showFirst }}
<div v-if="showFirst === 'true'">First view</div>
<div v-else>Second view</div>
</div>

Vue.js export default not working

I'm following the form example and it doesn't work ::
https://bootstrap-vue.js.org/docs/components/form
This is my code, inside my simple basic index.html :
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<link type="text/css" rel="stylesheet" href="https://unpkg.com/bootstrap/dist/css/bootstrap.min.css"/>
<link type="text/css" rel="stylesheet" href="https://unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue.css"/>
<script src="https://unpkg.com/vue"></script>
<script src="https://unpkg.com/babel-polyfill#latest/dist/polyfill.min.js"></script>
<script src="https://unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue.js"></script>
</head>
<body>
<template>
<div>
<b-form #submit="onSubmit" #reset="onReset" v-if="show">
<b-form-group id="exampleInputGroup1"
label="Email address:"
label-for="exampleInput1"
description="We'll never share your email with anyone else.">
<b-form-input id="exampleInput1"
type="email"
v-model="form.email"
required
placeholder="Enter email">
</b-form-input>
</b-form-group>
<b-form-group id="exampleInputGroup2"
label="Your Name:"
label-for="exampleInput2">
<b-form-input id="exampleInput2"
type="text"
v-model="form.name"
required
placeholder="Enter name">
</b-form-input>
</b-form-group>
<b-form-group id="exampleInputGroup3"
label="Food:"
label-for="exampleInput3">
<b-form-select id="exampleInput3"
:options="foods"
required
v-model="form.food">
</b-form-select>
</b-form-group>
<b-form-group id="exampleGroup4">
<b-form-checkbox-group v-model="form.checked" id="exampleChecks">
<b-form-checkbox value="me">Check me out</b-form-checkbox>
<b-form-checkbox value="that">Check that out</b-form-checkbox>
</b-form-checkbox-group>
</b-form-group>
<b-button type="submit" variant="primary">Submit</b-button>
<b-button type="reset" variant="danger">Reset</b-button>
</b-form>
</div>
</template>
</body>
and the js code +1
<script>
export default {
data () {
return {
form: {
email: '',
name: '',
food: null,
checked: []
},
foods: [
{ text: 'Select One', value: null },
'Carrots', 'Beans', 'Tomatoes', 'Corn'
],
show: true
}
},
methods: {
onSubmit (evt) {
evt.preventDefault();
alert(JSON.stringify(this.form));
},
onReset (evt) {
evt.preventDefault();
/* Reset our form values */
this.form.email = '';
this.form.name = '';
this.form.food = null;
this.form.checked = [];
/* Trick to reset/clear native browser form validation state */
this.show = false;
this.$nextTick(() => { this.show = true });
}
}
}
</script>
It is stricly the same than on the vue bootstrap website and it doesnt work
There is a blank screen, and This is the firefox error :
SyntaxError: export declarations may only appear at top level of a module
I can't show my form, there is nothing, just a blank screen, it doesn't work !
Note : i dont wanna to use babel or whatever complex stuff, i simply need my index.html to work
Please help me thank you
The example you copied verbatim is the contents of a Vue Single File Component. Those need either Webpack or Browserify to be translated to actual JavaScript that the browser can understand.
You can re-write the code avoiding the SFC structure, using Vue.component() and pass the template in the template property, if you want to obtain a reusable Vue component, but it will be a bit more complex.
Otherwise, if it is only a single page, simply use new Vue() and bind to an element selection using the el property (see Declarative Rendering in the guide):
var app = new Vue({
el: '#app',
data: {
...
}
})
and inside your html:
<div id="app">
...
... vue perform rendering and interpolation here ...
...
</div>
You are confusing Single-File Components with using Vue in simple HTML files.
To get the latter you could do as shown below.
Basically you have to:
Remove the <template> tag and add an id to its inner div, such as: <div id="app">
Wrap the object that was being exported in a Vue constructor and add an el option to it:
<script>
new Vue({
el: '#app',
data() {
JSBin demo here.
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<link type="text/css" rel="stylesheet" href="https://unpkg.com/bootstrap/dist/css/bootstrap.min.css"/>
<link type="text/css" rel="stylesheet" href="https://unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue.css"/>
<script src="https://unpkg.com/vue"></script>
<script src="https://unpkg.com/babel-polyfill#latest/dist/polyfill.min.js"></script>
<script src="https://unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue.js"></script>
</head>
<body>
<div id="app">
<b-form #submit="onSubmit" #reset="onReset" v-if="show">
<b-form-group id="exampleInputGroup1"
label="Email address:"
label-for="exampleInput1"
description="We'll never share your email with anyone else.">
<b-form-input id="exampleInput1"
type="email"
v-model="form.email"
required
placeholder="Enter email">
</b-form-input>
</b-form-group>
<b-form-group id="exampleInputGroup2"
label="Your Name:"
label-for="exampleInput2">
<b-form-input id="exampleInput2"
type="text"
v-model="form.name"
required
placeholder="Enter name">
</b-form-input>
</b-form-group>
<b-form-group id="exampleInputGroup3"
label="Food:"
label-for="exampleInput3">
<b-form-select id="exampleInput3"
:options="foods"
required
v-model="form.food">
</b-form-select>
</b-form-group>
<b-form-group id="exampleGroup4">
<b-form-checkbox-group v-model="form.checked" id="exampleChecks">
<b-form-checkbox value="me">Check me out</b-form-checkbox>
<b-form-checkbox value="that">Check that out</b-form-checkbox>
</b-form-checkbox-group>
</b-form-group>
<b-button type="submit" variant="primary">Submit</b-button>
<b-button type="reset" variant="danger">Reset</b-button>
</b-form>
</div>
<script>
new Vue({
el: '#app',
data() {
return {
form: {
email: '',
name: '',
food: null,
checked: []
},
foods: [
{text: 'Select One', value: null},
'Carrots', 'Beans', 'Tomatoes', 'Corn'
],
show: true
}
},
methods: {
onSubmit(evt) {
evt.preventDefault();
alert(JSON.stringify(this.form));
},
onReset(evt) {
evt.preventDefault();
/* Reset our form values */
this.form.email = '';
this.form.name = '';
this.form.food = null;
this.form.checked = [];
/* Trick to reset/clear native browser form validation state */
this.show = false;
this.$nextTick(() => {
this.show = true
});
}
}
});
</script>
</body>
</html>
The above uses your code directly in the Vue instance.
If you want to use your form as a component, such as:
<div id="app">
<my-form></my-form>
</div>
Follow this JSBin demo.

Property Binding & Sync in vue.js

I'm having trouble syncing my properties in vue.js, I have an 'active' property that I want to set to the value of each instance 'plan', but at the same time I would like to sync the property with the parent, with no luck. What am I doing wrong ?
<!DOCTYPE html>
<html lang="en">
<head>
<title>Bootstrap Vue</title>
<link rel="stylesheet prefetch" href="http://bootswatch.com/paper/bootstrap.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.3/vue.js"></script>
</head>
<body>
<div class="container">
<div id="app">
<pre>
#{{ $data | json}}
</pre>
<div v-for="plan in plans">
<plan :active.sync="active" :plan.sync="plan"></plan>
</div>
</div>
</div>
<template id="plan_template">
<div>
<span >#{{ plan.name }}</span>
<span >#{{ plan.price }}/month</span>
<button #click="setActivePlan" class="btn btn-primary btn-xs">UPGRADE</button>
</div>
</template>
<script>
new Vue({
el:'#app',
data:{
plans:[
{name:'Executive',price:100},
{name:'Professional',price:50},
{name:'Personal',price:30},
{name:'Free',price:0}
],
active:{},
},
components:{
plan:{
template:'#plan_template',
props:['plan', 'active'],
methods:{
setActivePlan:function(){
this.active=this.plan;
}
}
}
}
});
</script>
</body>
</html>
Note: This answer applies to V2 of Vue JS, < 2.3.0.
If you are using up V2.3.0+ then you can use .sync and .once modifiers: documentation here
You are using version 2 of Vue. The .sync and .once modifiers have been removed. From the docs:
Props are now always one-way down. To produce side effects in the parent scope, a component needs to explicitly emit an event instead of relying on implicit binding.
I have modified your code to use events here:
new Vue({
el:'#app',
data:{
plans:[
{name:'Executive',price:100},
{name:'Professional',price:50},
{name:'Personal',price:30},
{name:'Free',price:0}
],
active:{},
},
methods: {
setActivePlan: function(plan) {
this.active = plan;
}
},
components:{
plan:{
template:'#plan_template',
props:['plan', 'active'],
methods:{
setActivePlan:function(){
// emit an event to the parent indicating that this is the active plan
this.$emit('activate-plan');
}
}
}
}
});
<link rel="stylesheet prefetch" href="http://bootswatch.com/paper/bootstrap.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.3/vue.js"></script>
<div class="container">
<div id="app">
<div v-for="plan in plans">
<plan :plan="plan"
:active="active"
#activate-plan="setActivePlan(plan)"
>
</plan>
</div>
<pre>
{{ JSON.stringify($data, null, 2) }}
</pre>
</div>
</div>
<template id="plan_template">
<div>
<span >{{ plan.name }}</span>
<span >{{ plan.price }}/month</span>
<button #click="setActivePlan" class="btn btn-primary btn-xs">UPGRADE</button>
</div>
</template>