I want to apply mini-variant to the v-navigation-drawer when the screen size is smaller so for that I have the following so far:
<template >
<v-app id="inpire">
<div class="back">
<v-app-bar app>
<v-app-bar-nav-icon #click="drawer = !drawer"></v-app-bar-nav-icon>
<v-spacer></v-spacer>
<h1>{{selectedMethod}}</h1>
</v-app-bar>
<v-navigation-drawer v-model="drawer" src="./assets/bg.png" green app :mini-variant="mini">
<v-list-item
class="y"
v-for="item in items"
:key="item.unidade"
:to="item.link"
#click="change(item.unidade)"
>{{item.unidade}}</v-list-item>
</v-navigation-drawer>
<v-content id="inspire">
<router-view :dcsi="dcsi" :ipe="ipe" :rt="rt" :key="compKey"></router-view>
</v-content>
</div>
</v-app>
</template>
<script>
export default {
name: "App",
data: () => ({
items: [
{ unidade: "IPE", link: "/ipe" },
{ unidade: "DCSI", link: "/dcsi" },
{ unidade: "RT", link: "/rt" }
],
drawer: false,
selectedMethod: "",
unidade: "",
compKey: 0,
methods: {
change(val) {
this.selectedMethod = val;
this.cKey();
},
cKey() {
this.compKey += 1;
console.log(this.compKey);
}
},
watch: {
"$route.params.uni": function() {
this.cKey();
console.log(this.$route.params.uni);
console.log(this.props);
}
},
computed: {
mini() {
switch (this.$vuetify.breakpoint.name) {
case "xs":
return true;
case "sm":
return true;
case "md":
return true;
case "lg":
return false;
case "xl":
return false;
}
}
}
};
</script>
<style lang="stylus" scoped>
#inspire {
background: require('./assets/mg1.jpg') no-repeat center center;
}
.y {
color: green;
}
</style>
As a computed property I wrote mini() where it returns true or false depending on screen size.
But I am getting the following error "162:9 error Expected to return a value in "mini" computed property".
I don't understand why since it is returning a boolean.
I also tryed adding as a prop "mini-variant-md-and-down" to the navigation-drawer which didn't return any error but also didn't work.
Any hints in making the v-navigation-drawer become mini on smartphone are welcome.
mounted() {
if (
this.$vuetify.breakpoint.name === "md" ||
this.$vuetify.breakpoint.name === "sm" ||
this.$vuetify.breakpoint.name === "xs"
) {
this.mini = true;
}
Solved it
Related
I have created dynamic buttons in vuejs where each button represents a different answer to a question.
My goal is: when I get the answer wrong, the correct option is highlighted in green until the next question is shown.
Is it also possible to change other settings of these "BaseButtons" with CSS? How can I do this?
<template>
<div class="container-botoes">
<BaseButton class="optionsButtons"
v-for="options in optionsAnswers"
:key="options.id" #click="handleAnswer(options)">
{{options.ans}}
</BaseButton>
</div>
</template>
methods:{
handleAnswer(options){
if (options.id === this.correctAnswer){
this.playerHit = true;
}
else {
this.opponentHit = true;
}
this.nextquestion();
},
One option is to create css classes with styles you need and append them to BaseButton component depending on your conditions
Have a look at this one:
HTML block:
<template>
<div class="container-botoes">
<BaseButton
v-for="(options, index) in optionsAnswers"
:key="options.id"
class="optionsButtons"
:class="correctAnsIndex === index ? 'green-button' : 'red-button'"
#click="handleAnswer(options, index)"
>
{{ options.ans }}
</BaseButton>
</div>
</template>
JavaScript block:
<script>
export default {
data() {
return {
correctAnsIndex: null,
}
},
methods: {
handleAnswer(options, index) {
if (options.id === this.correctAnswer) {
this.playerHit = true
this.correctAnsIndex = index
} else {
this.opponentHit = true
this.correctAnsIndex = null
}
this.nextquestion()
},
},
}
</script>
CSS block:
<style>
.red-button {
background: red;
color: white;
font-weight: 700;
}
.green-button {
background: green;
color: white;
font-weight: 700;
}
</style>
Code explanation:
We have passed the index of the loop in the handleAnswer method, where the value of the index will be assigned to the correctAnsIndex variable if options.id === this.correctAnswer and in the else part we will assign null value to the correctAnsIndex variable.
Now, we have applied conditional classes in HTML block, where if the index and correctAnsIndex matches then it would apply green-button class or else it will apple red-button class.
Eventually getting your expected result.
Try this :
Vue.component('basebutton', {
data() {
return {
isCorrect: false
}
},
props: ['answerobj'],
template: `<button :class="{ 'green': isCorrect, 'white': !isCorrect}" #click="handleAnswer(answerobj)">{{ answerobj.answer }}</button>`,
methods: {
handleAnswer(answerobj) {
if (answerobj.correct) {
this.isCorrect = true
} else {
this.isCorrect = false
}
}
}
});
var app = new Vue({
el: '#app',
data: {
list: [{
question: 'Who is the tallest animal ?',
optionsAnswers: [{
answer: 'Elephant',
correct: false
}, {
answer: 'Jirafe',
correct: true
}, {
answer: 'Lion',
correct: false
}, {
answer: 'Zebra',
correct: false
}]
}]
}
});
.green {
background-color: green;
}
.white {
background-color: white;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div v-for="(item, index) in list" :key="index">
<p><strong>Question : </strong>{{ item.question }}</p>
<p><strong>Answers :</strong></p>
<BaseButton v-for="(options, i) in item.optionsAnswers" :key="i" :answerobj="options">
</BaseButton>
</div>
</div>
customFilter (item, queryText) {
const textOne = item.designation.toLowerCase()
const textTwo = item.reference.toLowerCase()
const searchText = queryText.toLowerCase()
return textOne.indexOf(searchText) > -1 ||
textTwo.indexOf(searchText) > -1
}
<v-autocomplete
dense=""
v-model="selectedProduct"
prepend-inner-icon="mdi-magnify"
rounded=""
:items="products_references_desginations"
:filter="customFilter"
item-text="designation"
filled
label="Recherche"
single-line=""
return-object=""
clearable=""
>
This works most of the time but it happens that I get this error :
I guess that it is the item variable in my custom filter that gets null sometimes but I have no idea why this happens. When it happens I have to reload the server and then it works fine and after some time, same error occurs.
FULL COMPONENT:
import {mapGetters, mapMutations} from 'vuex'
export default {
data: () => ({
selectedProduct: null,
}),
watch: {
selectedProduct(prod) {
console.log(prod)
if(prod != null) {
this.$emit('selectedProductFromSearchBar', prod)
}
}
},
methods: {
...mapMutations([]),
toProductDetailsPage(product){
console.log(product)
},
customFilter (item, queryText) {
const textOne = item.designation.toLowerCase()
const textTwo = item.reference.toLowerCase()
const searchText = queryText.toLowerCase()
return textOne.indexOf(searchText) > -1 ||
textTwo.indexOf(searchText) > -1
},
},
computed: {
...mapGetters(['products_references_desginations']),
},
mounted () {
},
}
<template>
<div class="">
<v-autocomplete
dense=""
v-model="selectedProduct"
prepend-inner-icon="mdi-magnify"
class="pa-0 search-bar"
rounded=""
:items="products_references_desginations"
:filter="customFilter"
item-text="designation"
filled
color="blue-grey lighten-2"
label="Recherche"
single-line=""
return-object=""
clearable=""
>
<template v-slot:item="data">
<template>
<v-list-item-content style=" overflow: hidden">
<v-list-item-title v-html="data.item.designation"></v-list-item-title>
<v-list-item-subtitle v-html="data.item.reference"></v-list-item-subtitle>
</v-list-item-content>
</template>
</template>
</v-autocomplete>
</div>
</template>
Structure of my data :
The problem was in my data, a null product was created I don't know how yet. But the problem is not from v-autocomplete as I thought.
The issue I am facing here is, I am not able to figure out how can I retain the values in the form on page refresh. Each time I refresh the page all the filled values in the form are gone.
Help me resolve this issue. I was thinking of using localStorage but not sure how I can implement it.
<template>
<v-card class="mb-12">
<v-form :model='user' class="content-padding" ref='pdfInputs'>
<div class="section-header">
User
</div>
<v-container fluid>
<ul>
<li v-for="(input, index) in user.inputs">
<input type="text" v-model="input.one"> - {{ input.one }}
<input type="text" v-model="input.two"> - {{ input.two }}
<button type="button" #click="deleteRow(index)">Delete</button>
</li>
</ul>
</v-container>
</v-form>
</v-card>
</template>
<script>
export default {
data () {
return {
user: {
inputs: []
}
}
}
methods: {
addRow() {
this.user.inputs.push({
one: '',
two: ''
})
},
deleteRow(index) {
this.user.inputs.splice(index,1)
}
}
}
</script>
There is watch functionality in vue
export default {
data () {
return {
user: {
inputs: []
}
}
},
mounted() {
this.user.inputs = JSON.parse(localStorage.getItem('form')) || [];
},
watch: {
user: {
handler: function() {
localStorage.setItem('form', JSON.stringify(this.user.inputs));
},
deep: true
}
},
methods: {
addRow() {
this.user.inputs.push({
one: '',
two: ''
})
},
deleteRow(index) {
this.user.inputs.splice(index,1)
}
}
}
I am having one root component know as App.vue. and another component named as FileUploaderParent.vue. I am calling a dispatch promise to store the data in the store. I am calling dispatch under mounted lifecycle hook.
On the other side, I am trying to access stored user data in the mounted function of a different component. It shows the error of undefined
I know, a work around can be to call same dispatch promise on the mounted function. But, It feels like a hack and the dispatch call is getting redundant.
Here is the code for App.vue:
<template>
<v-app>
<v-navigation-drawer v-model="drawer" app temporary >
<v-img :aspect-ratio="16/9" src="https://image.freepik.com/free-vector/spot-light-background_1284-4685.jpg">
<v-layout pa-2 column fill-height class="lightbox gray--text">
<v-spacer></v-spacer>
<v-flex shrink>
<!-- <div class="subheading">{{this.$store.getters.userData.name}}</div> -->
<!-- <div class="body-1">{{this.$store.getters.userData.email}}</div> -->
</v-flex>
</v-layout>
</v-img>
<v-list>
<template v-for="(item, index) in items">
<v-list-tile :href="item.href" :to="{name: item.href}" :key="index">
<v-list-tile-action>
<v-icon light v-html="item.icon"></v-icon>
</v-list-tile-action>
<v-list-tile-content>
<v-list-tile-title v-html="item.title"></v-list-tile-title>
</v-list-tile-content>
</v-list-tile>
</template>
</v-list>
</v-navigation-drawer>
<v-toolbar color="white" light :fixed=true>
<v-toolbar-side-icon #click.stop="drawer = !drawer"></v-toolbar-side-icon>
<v-img :src="require('#/assets/mad_logo.png')" max-width="80px" max-height="41px" />
<v-toolbar-title class="black--text justify-center" > <h1>MeshApp</h1></v-toolbar-title>
<v-spacer></v-spacer>
<v-avatar color="primary">
<!-- <span class="white--text headline">{{this.avatar.slice(0,2)}}</span> -->
</v-avatar>
</v-toolbar>
<v-content style="margin-top: 60px;">
<v-fade-transition mode="out-in">
<router-view></router-view>
</v-fade-transition>
</v-content>
</v-app>
</template>
<script>
export default {
name: 'app',
components: {
},
data() {
return {
avatar: '',
drawer: false,
items: [
{
href: 'home',
router: true,
title: 'home',
icon: 'grid_on',
},
{
href: 'view_volunteers',
router: true,
title: 'View Volunteer',
icon: 'group'
},
{
href: 'profile',
router: true,
title: 'profile',
icon: 'account_circle',
},
{
href: 'logout',
router: true,
title: 'Logout',
icon: 'toggle_off',
}
]
}
},
props: [],
mounted() {
this.$store.dispatch('getUserData').
then(() => {
let findAvatar = this.$store.getters.userData.name.split(" ")
let createAvatar = ''
for(let i = 0; i < findAvatar.length; i++) {
createAvatar = createAvatar + findAvatar[i].slice(0,1)
}
this.avatar = createAvatar
console.log(this.avatar)
// this.$store.dispatch('getUserId', id)
})
}
}
</script>
<style scoped>
v-content {
margin-top: 60px !important;
}
</style>
Here is the code for FileUploaderParent.vue:
<template>
<v-layout class="text-xs-center ">
<v-flex>
<image-input v-model="avatar">
<div slot="activator">
<v-avatar color = "primary" size="150px" v-ripple v-if="!avatar" class=" mb-3">
<h1 class="white--text"><span>{{this.defaultAvatar}}</span></h1>
</v-avatar>
<v-avatar size="150px" v-ripple v-else class="mb-3">
<img :src="avatar.imageURL" alt="avatar">
</v-avatar>
</div>
</image-input>
<v-slide-x-transition>
<div v-if="avatar && saved == false">
<!-- Stores the Image and changes the loader -->
<v-btn class="primary" #click="uploadImage" :loading="saving">Save Avatar</v-btn>
</div>
</v-slide-x-transition>
</v-flex>
</v-layout>
</template>
<script>
import ImageInput from './FileUploaderChild.vue'
export default {
name: 'app',
data () {
return {
defaultAvatar: '',
avatar: null,
saving: false,
saved: false
}
},
mounted() {
this.$store.dispatch('getUserData').
then(() => {
let findAvatar = this.$store.getters.userData.name.split(" ")
let createAvatar = ''
for(let i = 0; i < findAvatar.length; i++) {
createAvatar = createAvatar + findAvatar[i].slice(0,1)
}
this.defaultAvatar = createAvatar
})
},
components: {
ImageInput: ImageInput
},
watch:{
avatar: {
handler: function() {
this.saved = false
},
deep: true
}
},
methods: {
uploadImage() {
this.saving = true
setTimeout(() => this.savedAvatar(), 1000)
},
savedAvatar() {
this.saving = false
this.saved = true
}
}
}
</script>
<style>
</style>
This is how the store looks like:
store.js
actions: {
getUserData(context) {
return new Promise((resolve, reject) => {
axios.get('http://someurl.in/api/v1/users/'+this.state.userId, {
headers: {
'Content-Type': 'application/json'
},
auth: {
username: 'someusername',
password: 'pass'
}
}).
then(response => {
context.commit('storeUserData', response.data.data.users)
resolve(response); // Let the calling function know that http is done. You may send some data back
}).catch(error => {
reject(error);
})
})
}
},
mutations: {
storeUserData(state, data) {
state.userData = data
}
}
This is how the error looks like:
How do i access store data in FileUploaderParent.vue under mounted function?
The code you have written seems right, but if you still need an answer you can use watch on store
this.$store.watch(() => this.$store.state.userData, async () => {
//do your code here
});
How about adding a check that determines if an API call is required:
actions: {
getUserData(context) {
return new Promise((resolve, reject) => {
// Be sure to set the default value to `undefined` under the `state` object.
if (typeof this.state.userData === 'undefined') {
axios
.get('http://someurl.in/api/v1/users/' + this.state.userId, {
headers: {
'Content-Type': 'application/json'
},
auth: {
username: 'someusername',
password: 'pass'
}
})
.then(response => {
context.commit('storeUserData', response.data.data.users);
resolve(response.data.data.users);
})
.catch(error => {
reject(error);
});
}
else {
resolve(this.state.userData);
}
})
}
}
The this.$store.getters refers to a getter in your Vuex store. You have no getters in your Vuex store - at least none can be seen in your sample. (https://vuex.vuejs.org/guide/getters.html)
I'm implementing Vue paper dashboard sidebar. So I have something like this:
Into Index I have
<template>
<div>
AdminIndex
<side-bar>
</side-bar>
</div>
</template>
<script>
import { faBox, faImages } from '#fortawesome/fontawesome-free-solid';
import Sidebar from '#/components/sidebar/SideBar';
export default {
name: 'admin-index-view',
components: {
SideBar,
},
data() {
return {
showSidebar: false,
sidebarLinks: [
{
name: 'admin.menu.products',
icon: faBoxes,
route: { name: 'adminProducts' },
},
{
name: 'admin.menu.sliders',
icon: faImages,
route: { name: '/admin/stats' },
},
],
};
},
methods: {
displaySidebar(value) {
this.showSidebar = value;
},
},
};
</script>
SideBar component:
<template>
<div :class="sidebarClasses"
:data-background-color="backgroundColor"
:data-active-color="activeColor">
<!--
Tip 1: you can change the color of the sidebar's background using: data-background-color="white | black | darkblue"
Tip 2: you can change the color of the active button using the data-active-color="primary | info | success | warning | danger"
-->
<!-- -->
<div class="sidebar-wrapper"
id="style-3">
<div class="logo">
<a href="#"
class="simple-text">
<div class="logo-img">
<img src="static/img/vue-logo.png"
alt="">
</div>
Paper Dashboard
</a>
</div>
<slot>
</slot>
<ul :class="navClasses">
<!--By default vue-router adds an active class to each route link. This way the links are colored when clicked-->
<router-link v-for="(link,index) in sidebarLinks"
:key="index"
:to="link.route"
tag="li"
:ref="link.name">
<a>
<font-awesome-icon :icon="link.icon" />
<p v-t="link.name" />
</a>
</router-link>
</ul>
<moving-arrow :move-y="arrowMovePx">
</moving-arrow>
</div>
</div>
</template>
<script>
import FontAwesomeIcon from '#fortawesome/vue-fontawesome';
import MovingArrow from './MovingArrow';
export default {
name: 'side-bar',
components: {
MovingArrow,
FontAwesomeIcon,
},
props: {
type: {
type: String,
default: 'sidebar',
validator: value => {
const acceptedValues = ['sidebar', 'navbar'];
return acceptedValues.indexOf(value) !== -1;
},
},
backgroundColor: {
type: String,
default: 'black',
validator: value => {
const acceptedValues = ['white', 'black', 'darkblue'];
return acceptedValues.indexOf(value) !== -1;
},
},
activeColor: {
type: String,
default: 'success',
validator: value => {
const acceptedValues = [
'primary',
'info',
'success',
'warning',
'danger',
];
return acceptedValues.indexOf(value) !== -1;
},
},
sidebarLinks: {
type: Array,
default: () => [],
},
},
data() {
return {
linkHeight: 60,
activeLinkIndex: 0,
windowWidth: 0,
isWindows: false,
hasAutoHeight: false,
};
},
computed: {
sidebarClasses() {
if (this.type === 'sidebar') {
return 'sidebar';
}
return 'collapse navbar-collapse off-canvas-sidebar';
},
navClasses() {
if (this.type === 'sidebar') {
return 'nav';
}
return 'nav navbar-nav';
},
/**
* Styles to animate the arrow near the current active sidebar link
* #returns {{transform: string}}
*/
arrowMovePx() {
return this.linkHeight * this.activeLinkIndex;
},
},
watch: {
$route() {
this.findActiveLink();
},
},
methods: {
findActiveLink() {
this.sidebarLinks.find((element, index) => {
const found = element.path === this.$route.path;
if (found) {
this.activeLinkIndex = index;
}
return found;
});
},
},
mounted() {
this.findActiveLink();
},
};
</script>
I dont receive any issues or vue errors, sidebar just don't display. In Chrome console just return empty: <side-bar data-v-66018f3c=""></side-bar> Someone knows why sidebar is not binded? What I need to do to get correctly implementation of it? Regards
Chrome console error:
[Vue warn]: Unknown custom element: - did you register the
component correctly? For recursive components, make sure to provide
the "name" option.