I'm using vue select. In the dropdown, there are the labels (not only text). Is it possible to have also the label for the selected value?
<div class="form-group row">
<label for="project_status_id" class="col-sm-3 col-form-label">Projekt Status</label>
<div class="col-sm-9">
<v-select :options="resources.activeProjectStatus" :reduce="project_status_id => project_status_id.id" v-model="form.project_status_id" label="name" id="project_status_id" placeholder="Projekt Status" :class="$vSelectStyle($v.form.project_status_id)">
<template v-slot:option="option" >
<div v-html="option.status_label" class="mb-1">
</div>
</template>
</v-select>
<template v-if="$v.form.project_status_id.$error">
<p class="text-danger" v-if="!$v.form.project_status_id.required">
Projekt - Status ist erforderlich!
</p>
</template>
</div>
</div>
Assuming you want the HTML of the status_label, also assuming that status_label is a template string or similar, then use the selected-option slot with the slot's content being the same as your option slot without the class attached.
The key part in the example below is, as mentioned, the selected-option slot:
<!-- Using OP's `option` key -->
<template v-slot:selected-option="option">
<div v-html="option.status_label"></div>
</template>
The example below is a fork of Vue-Select's Codepen example with modifications for the answer.
Vue.config.productionTip = false;
Vue.component('v-select', VueSelect.VueSelect);
new Vue({
el: '#app',
data: {
options: [
{
name: `<span style="padding: 4px; background: green; border-radius: 0.25rem; color: white;">Foo</span>`
},
{
name: `<span style="padding: 4px; background: orange; border-radius: 0.25rem; color: white;">Bar</span>`
},
{
name: `<span style="padding: 4px; background: red; border-radius: 0.25rem; color: white;">Baz</span>`
}
]
}
});
body {
font-family: 'Source Sans Pro', 'Helvetica Neue', Arial, sans-serif;
}
h1 {
font-size: 26px;
font-weight: 600;
color: #2c3e5099;
text-rendering: optimizelegibility;
-moz-osx-font-smoothing: grayscale;
-moz-text-size-adjust: none;
}
#app {
max-width: 30em;
margin: 1em auto;
}
<script src="https://unpkg.com/vue#latest"></script>
<script src="https://unpkg.com/vue-select#latest"></script>
<link rel="stylesheet" href="https://unpkg.com/vue-select#latest/dist/vue-select.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:600">
<div id="app">
<h1>Vue Select</h1>
<v-select :options="options" label="label">
<template v-slot:option="option" >
<div v-html="option.name" style="padding: 2px 0;"></div>
</template>
<template v-slot:selected-option="option">
<div v-html="option.name"></div>
</template>
</v-select>
</div>
Related
Here is my code but it has not been added logic.
I'm trying to click on the label to upload the file instead of using the input tag.
Apply.vue
<div class="line">
<h6>Upload CV:</h6>
<div class="up-cv">
<button #click="onFileChange" type="button" id="custom-button">
<img src="../../assets/recruit/taicv.svg" alt="" />Upload
</button>
<input id="real-file" type="file" style="display: none" name="image" />
<div class="name-cv">
<h5 id="custom-text">you have not selected the file</h5>
<img onclick="deleteFile()" src="../../assets/recruit/delete.svg" alt="" />
</div>
</div>
</div>
Looking forward to your help, thanks a lot...
I have created one Demo as you want on stackblitz.
link - https://vue-j4h4a6.stackblitz.io/
also I have attached source code here
<template>
<div id="app">
<div class="line">
<h6>Upload CV:</h6>
<div class="up-cv">
<button
#click="onFileChange"
type="button"
id="custom-button"
>
<!-- <img src="../../assets/recruit/taicv.svg" alt="" /> -->
<span class="material-symbols-outlined"> file_upload </span>
Upload
</button>
<input
id="real-file"
type="file"
style="display: none"
name="image"
#change="fileName"
/>
<div class="name-cv">
<h5 v-if="uploadedFileName">{{ uploadedFileName }}</h5>
<h5 v-else id="custom-text">you have not selected the file</h5>
<!-- <img
onclick="deleteFile()"
src="../../assets/recruit/delete.svg"
alt=""
/> -->
<span
v-if="uploadedFileName"
class="material-symbols-outlined deleteBtn"
#click="deleteFile"
>
delete
</span>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {
uploadedFileName: null,
};
},
methods: {
onFileChange() {
document.getElementById('real-file').click();
},
fileName(e) {
this.uploadedFileName = e.target.value;
},
deleteFile() {
this.uploadedFileName = null;
},
},
};
</script>
<style>
#custom-button {
display: inline-block;
border: 1px solid grey;
padding: 10px;
cursor: pointer;
}
#custom-button span {
display: inline-block;
vertical-align: bottom;
}
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
.deleteBtn {
cursor: pointer;
color: red;
}
</style>
app.vue
<template>
<div id="nav">
<router-link :class="{active: $route.name === 'Home'}" to="/">Home</router-link>
<router-link :class="{active: $route.name === 'Cart'}" to="/cart">Cart</router-link>
</div>
<router-view/>
</template>
<script>
export default {
mounted() {
this.$store.commit('updateCartFromLocalStorage')
}
}
</script>
<style lang="scss">
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
}
html, body {
margin: 0;
padding: 0;
overflow-x: hidden;
}
body {
background-color: rgb(245, 245, 245);
}
#nav {
padding: 10px;
width: 100%;
height: 30px;
background-color: white;
line-height: 30px;
}
a {
font-weight: bold;
color: darkgray;
text-decoration: none;
margin: 0 5px 0 5px;
font-size: 1.25rem;
&.active {
color: #2c3e50;
}
}
.text-center {
text-align: center;
}
</style>
I'm getting an error:
Failed to compile.
./src/App.vue
Module Error (from ./node_modules/eslint-loader/index.js)
The template root requires exactly one element vue/valid-template-root
How can I fix it? I tried adding more div but it is still not working.
There are two roots in your component:
<template>
<div id="nav"> 1️⃣
<router-link :class="{active: $route.name === 'Home'}" to="/">Home</router-link>
<router-link :class="{active: $route.name === 'Cart'}" to="/cart">Cart</router-link>
</div>
<router-view/> 2️⃣
</template>
To create a single root, wrap the entire template in another element, such as a div:
<template>
<div>
<div id="nav">
<router-link :class="{active: $route.name === 'Home'}" to="/">Home</router-link>
<router-link :class="{active: $route.name === 'Cart'}" to="/cart">Cart</router-link>
</div>
<router-view/>
</div>
</template>
You need to add one root level element for vue2
<template>
<div id="nav">
<router-link :class="{active: $route.name === 'Home'}" to="/">Home</router-link>
<router-link :class="{active: $route.name === 'Cart'}" to="/cart">Cart</router-link>
<router-view/>
</div>
</template>
Here div is root-level element.
However, if you are using vue3, you don't need one root level element.
I am looking to extend a vue-toggle component I found to add a boolean value. I am trying to add ability to hide/show an element based on the boolean prop.
Here is my html code:
<div class="container" id="app">
<div class="row">
<div class="col-xs-3 col-sm-4 col-md-5 col-lg-6">
<img src="https://images-na.ssl-images-amazon.com/images/I/81UxhkSlBjL._UX466_.jpg" alt="" class="img-responsive" />
</div>
<div class="col-xs-9 col-sm-8 col-md-7 col-lg-6 options">
<fieldset class="form-group">
<legend class="sr-only">Style</legend>
<div class="form-group">
<vue-toggle :values="styles" :boolean="showVNeck" :selected.sync="style" default="crew"></vue-toggle>
</div>
</fieldset>
<div><span v-if="showVNeck">V Neck</span><span v-else>Crew Neck</span> Selected</div>
</div>
</div>
</div>
<template id="vue-toggle">
<div class="btn-group">
<button type="button"
v-for="(val, key) in values"
#click="changeSelectVal(key, boolean)"
:class="['btn', { 'btn-primary': selected === key, 'btn-default': selected !== key }]"
>{{ val }}</button>
</div>
</template>
Here is my javascript:
var Toggle = Vue.extend({
template: '#vue-toggle',
props: ['values','selected','boolean','default'],
mounted: function () {
this.selected = this.default;
},
methods: {
changeSelectVal: function(val, boolean) {
this.selected = val;
this.boolean = !this.boolean;
}
}
});
Vue.component('vue-toggle', Toggle);
new Vue({
el: '#app',
data: {
style: '',
showVNeck: false,
styles: {
'crew': 'Crew Neck',
'vneck': 'V-Neck',
}
}
});
Here is a codepen to share:
https://codepen.io/mujaji/pen/YzXVqaL
I think I am just overlooking something and looking for assistance.
Cheers!
The .sync modifier requires that your child emits events of the type update:myPropName instead of directly mutating the prop - look at the documentation:
var Toggle = Vue.extend({
template: '#vue-toggle',
props: ['values','selected','boolean','default'],
mounted: function () {
this.$emit('update:selected',this.default);
},
methods: {
changeSelectVal: function(val, boolean) {
this.$emit('update:selected',val);
this.$emit('update:boolean',!this.boolean);
}
}
});
Vue.component('vue-toggle', Toggle);
new Vue({
el: '#app',
data: {
style: '',
showVNeck: false,
styles: {
'crew': 'Crew Neck',
'vneck': 'V-Neck',
},
},
methods:
{
updateVNeck(newValue)
{
this.showVNeck = newValue;
}
}
});
body {
background-color: #5c4084;
padding: 50px;
}
.container {
background-color: #fff;
padding-top: 40px;
padding-bottom: 80px;
border-radius: 8px;
}
.heading
h1 {
color: #fff;
font-size: 4rem;
font-weight: 900;
margin: 0 0 5px 0;
background: -webkit-linear-gradient(#fff, #999);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
text-align: center;
}
.heading h4 {
color: #5c3d86;
font-size: 24px;
font-weight: 400;
text-align: center;
margin: 0 0 35px 0;
}
.options {
padding-top: 80px;
}
.btn{
outline: none !important;
}
.btn.btn-primary {
background-color: #5c4084;
border-color: #5c4084;
outline: none;
}
.btn.btn-primary:hover {
background-color: #5c4084;
border-color: #5c4084;
}
.btn.btn-primary:active, .btn.btn-primary:focus {
background-color: #5c4084;
border-color: #5c4084;
}
.btn.btn-default:hover {
background-color: #5c4084;
border-color: #5c4084;
color: #fff;
}
.btn.btn-default:active, .btn.btn-default:focus {
background-color: #5c4084;
border-color: #5c4084;
color: #fff;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.6.3/css/font-awesome.min.css">
<div class="container" id="app">
<div class="row">
<div class="col-xs-3 col-sm-4 col-md-5 col-lg-6">
<img src="https://images-na.ssl-images-amazon.com/images/I/81UxhkSlBjL._UX466_.jpg" alt="" class="img-responsive" />
</div>
<div class="col-xs-9 col-sm-8 col-md-7 col-lg-6 options">
<fieldset class="form-group">
<legend class="sr-only">Style</legend>
<div class="form-group">
<vue-toggle :values="styles" :boolean.sync="showVNeck" :selected.sync="style" default="crew"></vue-toggle>
</div>
</fieldset>
<div><span v-if="showVNeck">V Neck</span><span v-else>Crew Neck</span> Selected</div>
</div>
</div>
</div>
<template id="vue-toggle">
<div class="btn-group">
<button type="button"
v-for="(val, key) in values"
#click="changeSelectVal(key, boolean)"
:class="['btn', { 'btn-primary': selected === key, 'btn-default': selected !== key }]"
>{{ val }}</button>
</div>
</template>
Im getting used to Vue, but before I installed Bootstrap, my modal worked completely fine. Now it is a black screen. Also when I have my button inside of a div, the button does not render. When I take the button outside of it's div, the modal does not even toggle.
here is the products component i'm importing my modal into:
<template>
<div class="products">
<h1>All Products</h1>
<div v-for="product in products" class="single-product">
<h2 class="title">{{product.title}} - ${{product.price}}</h2>
<img class="images" :src="product.photo_url"></img>
<div class="modal">
<button
type="button"
class="btn btn-primary"
#click="showModal"
>
Show Product Info
</button>
<modal
v-show="isModalVisible"
#close="closeModal"
/>
</div>
</div>
</div>
</template>
<script>
//imports
import Vue from 'vue'
import Modal from './Modal.vue'
//import so I can use vue resource for an http request
import VueResource from 'vue-resource'
Vue.use(VueResource)
export default {
components: {
'modal': Modal
},
data() {
return {
//empty products array to be filled after get request
products: [],
//set modal visibility to false by default
isModalVisible: false,
}
},
methods: {
//show modal when user clicks view product info
showModal() {
this.isModalVisible = true;
},
closeModal() {
//close modal when user clicks the close button inside the modal
this.isModalVisible = false;
}
},
//request to retrieve all products from API using Created
created() {
//http request using vue resource from dependencies
this.$http.get('https://tap-on-it-exercise-backend.herokuapp.com/products').then(function(data) {
//fill products array by grabbing the data, slicing ot, then setting it to products array
this.products = data.body.slice(0, data.body.length)
})
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.images {
width: 100px;
height: 100px;
}
</style>
Here is my modal component that is exported and then imported into products component:
<script>
export default {
name: 'Modal',
methods: {
close() {
this.$emit('close');
},
},
};
</script>
<template>
<transition name="modal-fade">
<div class="modal-backdrop">
<div class="modal" role="dialog" aria-labelledby="productTitle" aria-describedby="productDescription">
<header class="modal-header" id="productTitle">
<slot name="header">
Product Title
</slot>
</header>
<section class="product-photo" id="productPhoto">
<slot name="photo">
Product Photo
</slot>
</section>
<section class="product-description" id="productDescription">
<slot name="body">
Product Description
</slot>
</section>
<footer class="modal-footer">
<slot name="footer">
<button type="button" class="btn-green" aria-label="Close modal">
Like
</button>
<button type="button" class="btn-green" #click="close" aria-label="Close modal">
Close
</button>
</slot>
</footer>
</div>
</div>
</transition>
</template>
<style>
.modal-backdrop {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-color: rgba(0, 0, 0, 0.3);
display: flex;
justify-content: center;
align-items: center;
}
.modal {
background: #FFFFFF;
box-shadow: 2px 2px 20px 1px;
overflow-x: auto;
display: flex;
flex-direction: column;
}
.modal-header,
.modal-footer {
padding: 15px;
display: flex;
}
.modal-header {
border-bottom: 1px solid #eeeeee;
color: #4AAE9B;
justify-content: space-between;
}
.modal-footer {
border-top: 1px solid #eeeeee;
justify-content: flex-end;
}
.product-description {
position: relative;
padding: 20px 10px;
}
.btn-close {
border: none;
font-size: 20px;
padding: 20px;
cursor: pointer;
font-weight: bold;
color: #4AAE9B;
background: transparent;
}
.btn-green {
color: white;
background: #4AAE9B;
border: 1px solid #4AAE9B;
border-radius: 2px;
}
</style>
I know I can change the template for the option slot, but can we do the same for the label slot? Like for option:
<v-select inputId="originsId" :options="origins" label="city" placeholder="Search...">
<template slot="option" slot-scope="origin">
<div class="flex">
<div class="col">
<span>{{ origin.city }}</span>
</div>
<div class="col">
<span>{{ origin.country }}</span>
</div>
</div>
</template>
</v-select>
Is there some way I can style the label when the option is selected? Now it only shows the label="city" value. I need something like:
<v-select inputId="originsId" :options="origins" label="city" placeholder="Search...">
<template slot="label" slot-scope="origin">
<div class="flex">
<div class="col">
<span>Selected city: {{ origin.city }}</span>
</div>
<div class="col">
<span>Selected country: {{ origin.country }}</span>
</div>
</div>
</template>
<template slot="option" slot-scope="origin">
<div class="flex">
<div class="col">
<span>{{ origin.city }}</span>
</div>
<div class="col">
<span>{{ origin.country }}</span>
</div>
</div>
</template>
</v-select>
Basically I need some custom styling and additional info other then label="city" when the option is selected.
As Vue-select Github: L324 and Vue-select Github: L539, uses <slot name="selected-option"> will be one solution.
Updated: from Vue-select Github you will see there is one parent slot = selected-option-container, but I found it hasn't been deployed to the dist. In future, you should be able to use this slot to custom the whole container and the selected options.
Like below demo:
Vue.component('v-select', VueSelect.VueSelect)
new Vue({
el: '#app',
data: {
options: [
{
title: 'Read the Docs',
icon: 'fa-book',
url: 'https://codeclimate.com/github/sagalbot/vue-select'
},
{
title: 'View on GitHub',
icon: 'fa-github',
url: 'https://codeclimate.com/github/sagalbot/vue-select'
},
{
title: 'View on NPM',
icon: 'fa-database',
url: 'https://codeclimate.com/github/sagalbot/vue-select'
},
{
title: 'View Codepen Examples',
icon: 'fa-pencil',
url: 'https://codeclimate.com/github/sagalbot/vue-select'
}
]
}
})
body {
font-family: "Source Sans Pro", "Helvetica Neue", Arial, sans-serif;
}
h1,.muted {
color: #2c3e5099;
}
h1 {
font-size: 26px;
font-weight: 600;
text-rendering: optimizelegibility;
-moz-osx-font-smoothing: grayscale;
-moz-text-size-adjust: none;
}
#app {
max-width: 30em;
margin: 1em auto;
}
#app .dropdown li {
border-bottom: 1px solid rgba(112, 128, 144, 0.1)
}
#app .dropdown li:last-child {
border-bottom: none;
}
#app .dropdown li a {
padding: 10px 20px;
display: flex;
width: 100%;
align-items: center;
font-size: 1.25em;
}
#app .dropdown li a .fa {
padding-right: 0.5em;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<script src="https://unpkg.com/vue-select#latest"></script>
<div id="app">
<h1>Vue Select - Custom Option Templating</h1>
<v-select :options="options" label="title">
<template slot="selected-option" slot-scope="option">
<div class="flex">
<div class="col">
<span class="fa" :class="option.icon"></span>
<span>Selected item: {{ option.title }}</span>
</div>
</div>
</template>
<template slot="option" slot-scope="option">
<span class="fa" :class="option.icon"></span>
{{ option.title }}
</template>
</v-select>
</div>