Use radio button to show or hide divs in Vue 3 - vue.js

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>

Related

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>

How to pass in img src in vue.js using props?

I have an info-card.vue component that is used twice in a landing page, but I want different data displayed in each of them. Here is the info-card.vue component:
<template>
<div class="card-container glass-effect">
<div class="illustration-container">
<img src="{{ image }}" alt="Businesses" class="illustration">
</div>
<div class="title-container">{{ title }}</div>
<div class="paragraph-container">{{ content }}</div>
</div>
</template>
<script>
export default {
props: ['image','title', 'content']
}
</script>
And here is the landing-info.vue page that the info-card component is used in:
<div class="business-side">
<info-card image="/images/image1.png" title="BUSINESSES" content="This is some content"></info-card>
</div>
<div class="customer-side">
<info-card image="/images/image2.png" title="CUSTOMERS" content="This is some content"></info-card>
</div>
But this didn't work, I'm new to vue so any ideas?
You can't use mustache {{   }} in vue attributes. Instead use v-bind:attr="" or :attr="" where attr is the dynamic attribute you want to bind.
So, your image component should be:
<img v-bind:src="image" alt="Businesses" class="illustration" />
or
<img :src="image" alt="Businesses" class="illustration">
The colon is a shorthand for v-bind.
Read more on v-bind here.

b-input model has value of type string

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>

adding component on add button

i am a absolute beginner in vuejs,i have a feature of adding dynamic input fields on click of a button it will keep on adding rows and keeping in mind the counter should be incrementing also so that i can validate on backend, this is my code so far
<div id="settlement_container" class="container-fluid mt-4">
<div class="card rounded-0 shadow-lg">
<div class="card-body p-0">
<div class="card-header px-2">
<div class="row wow fadeIn">
<div class="col-5">
<h3>Add Store Status</h3>
</div>
</div>
</div>
<form class="custom-form-group" action="{{url('stores/addStoreStatusDB')}}" method="POST">
<div class="form-group col-6">
<label for="exampleInputEmail1">Tax</label>
<input type="text" class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp" name="tax" placeholder="Tax" required>
</div>
<div class="display-inline">
<div class="form-group col-md-6">
<button #click="addstatus" class="btn btn-primary">Add Rows</button>
</div>
</div>
<div class="display-inline">
<div class="form-group col-md-6">
<button type="submit" class="btn btn-primary">Update Tax</button>
</div>
</div>
<dynamic-rows/>
</form>
</div>
</div>
</div>
{{-- Main layout --}}
#push('script')
<script src="{{ asset('js/app_vue.js') }}" ></script>
<script>
Vue.component('dynamic-rows',{
//accept data inside template
props:['counter'],
//accept data inside template
template:"<label for='exampleInputEmail1'>counter</label>"
});
const app = new Vue({
el: '#settlement_container',
data: {
counter:0
},
component:['dynamic-rows'],
methods:{
addstatus:function(e){
appendDiv=""
e.preventDefault();
alert("inside");
}
}
});
</script>
now i can do this in jquery in 5 minutes , but as i am beginner in vuejs i cant developer the sense of it of how to do it, i have a component and i want to repeat the component every time the button is clicked,
here is the fiddle! fiddle
OK, so a lot going on here and I think it may be easier to break down some of the points in isolation for you to play with and learn.
To add inputs, I think it makes more sense to have the values being in an array. Using Vue, you can iterate through that array to let each array element have its own <input/> while also simply adding another array element to add a new input:
<template>
<div>
<div v-for="(tax, index) in taxes" :key="index">
<input v-model="taxes[index]" />
</div>
<button type="number" #click="add">Add</button>
<p>Count: {{taxes.length}}</p>
</div>
</template>
<script>
export default {
data(): {
return {
taxes: [0]
}
},
methods: {
add() {
this.taxes.push(0);
}
}
});
</script>
Now with regards to the counter, I don't know what you mean validate on the backend. You could add a watcher on the taxes array and process changes there? Watchers are used sparingly, with computed properties being much preferred, but they may make sense if you need to be sending data to the backend instead of into the DOM.
The counter prop you registered in your code is not really going to work for the pattern I showed. Generally, props are for parent components to pass data to child components. The preferred pattern when sending data from child to parent is to use $emit. Read more here.

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>