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.
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>
here's what I did, I added a transition to the router link. and everything works as it is, but when I enter contact, the modal works because it is modal, but when I switch to other pages again, it does not switch.
App.vue
<template>
<header class="bg-white dark:bg-black">
<router-link to="/">Home</router-link>
<router-link to="/about">About</router-link>
<router-link to="/contact">Contact</router-link>
</header>
<div class="flex flex-row justify-center items-center mx-auto w-full">
<router-view v-slot="{ Component, route }">
<transition name="route" mode="out-in">
<component :is="Component" :key="route.path"/>
</transition>
</router-view>
</div>
</template>
<style>
.route-enter-from,
.route-leave-to {
opacity: 0;
transform: translateY(-30px);
}
.route-enter-active,
.route-leave-active {
transition: all 0.3s ease;
}
.route-enter-to,
.route-leave-from {
transform: translateY(0);
}
</style>
contact.vue
<template>
<button id="show-modal" #click="showModal = true">Show Modal</button>
<Teleport to="body">
<modal :show="showModal" #close="showModal = false">
<template #header>
<h3>custom header</h3>
</template>
</modal>
</Teleport>
</template>
<script>
import Modal from './Modal.vue'
export default {
components: {
Modal
},
data() {
return {
showModal: false
}
}
}
</script>
modal.vue
<script>
export default {
props: {
show: Boolean
}
}
</script>
<template>
<Transition name="modal">
<div v-if="show" class="modal-mask">
<div class="modal-wrapper">
<div class="modal-container">
<div class="modal-header">
<slot name="header">default header</slot>
</div>
<div class="modal-body">
<slot name="body">default body</slot>
</div>
<div class="modal-footer">
<slot name="footer">
default footer
<button
class="modal-default-button"
#click="$emit('close')"
>OK</button>
</slot>
</div>
</div>
</div>
</div>
</Transition>
</template>
<style>
.modal-mask {
position: fixed;
z-index: 9998;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
display: table;
transition: opacity 0.3s ease;
}
.modal-wrapper {
display: table-cell;
vertical-align: middle;
}
.modal-container {
width: 300px;
margin: 0px auto;
padding: 20px 30px;
background-color: #fff;
border-radius: 2px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.33);
transition: all 0.3s ease;
}
.modal-header h3 {
margin-top: 0;
color: #42b983;
}
.modal-body {
margin: 20px 0;
}
.modal-default-button {
float: right;
}
/*
* The following styles are auto-applied to elements with
* transition="modal" when their visibility is toggled
* by Vue.js.
*
* You can easily play with the modal transition by editing
* these styles.
*/
.modal-enter-from {
opacity: 0;
}
.modal-leave-to {
opacity: 0;
}
.modal-enter-from .modal-container,
.modal-leave-to .modal-container {
-webkit-transform: scale(1.1);
transform: scale(1.1);
}
</style>
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>
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>