Why does my class not show in vue but my conditional class does? - vue.js

I want to show two classes in my button. One that is conditional on a Boolean data value like "hideLeftArrow" and another class that shows up as the default like arrowBtn. The conditional class shows but the default class doesn't. What is wrong with my syntax in the following:
:class="[{ hideArrow: hideLeftArrow }, arrowBtn]"
My full code for reference
<template>
<div class="showcase-container">
<button
#click="showRightArrow"
:class="[{ hideArrow: hideLeftArrow }, arrowBtn]"
>
<img alt="left arrow" src="../assets/leftArrow.svg" class="arrow" />
</button>
<a
v-for="game in games"
:key="game.id"
:class="{ hideGame: game.hide }"
:href="game.link"
target="_blank"
>{{ game.name }}</a
>
<button
#click="showLeftArrow"
:class="[{ hideArrow: hideRightArrow }, arrowBtn]"
>
<img alt="right arrow" src="../assets/rightArrow.svg" class="arrow" />
</button>
</div>
</template>
<script>
export default {
data() {
return {
games: [
{
name: "Tryangle",
link: "https://google.com",
id: 1,
hide: false,
},
{
name: "BagRPG",
link: "https://youtube.com",
id: 2,
hide: true,
},
],
hideLeftArrow: true,
hideRightArrow: false,
};
},
methods: {
showLeftArrow() {
this.hideLeftArrow = !this.hideLeftArrow;
this.hideRightArrow = !this.hideRightArrow;
this.games[0].hide = true;
this.games[1].hide = false;
},
showRightArrow() {
this.hideLeftArrow = !this.hideLeftArrow;
this.hideRightArrow = !this.hideRightArrow;
this.games[0].hide = false;
this.games[1].hide = true;
},
},
};
</script>
<style lang="scss">
#import "../styles.scss";
.showcase-container {
.hideArrow {
visibility: hidden;
}
.arrowBtn {
background: none;
border: 0;
}
.hideGame {
display: none;
}
}
</style>

I think you're aiming for this, with quotes around arrowBtn:
:class="[{ hideArrow: hideLeftArrow }, 'arrowBtn']"
Otherwise arrowBtn will be treated as a property name and not a string..
That said, I'd probably do it this way instead:
class="arrowBtn"
:class="{ hideArrow: hideLeftArrow }"
class allows you to have both a static and a bound version on the same element.

Related

How to fix this error: "v-model cannot be used on a prop, because local prop bindings are not writable?"

I'm trying to make a dropdown sort and I get this error:
VueCompilerError: v-model cannot be used on a prop, because local prop bindings are not writable. Use a v-bind binding combined with a v-on listener that emits update:x event instead.
Here are 2 components App and MySelect:
<template>
<!-- App Component -->
<div class="app">
<h1>Страница с постами</h1>
<div class="app__btns">
<my-button #click="showDialog">Cоздать пост</my-button>
<my-select v-model="selectedSort" :options="sortOptions" />
</div>
<my-dialog v-model:show="dialogVisible">
<post-form #create="createPost" />
</my-dialog>
<post-list :posts="posts" #remove="removePost" v-if="!isPostsLoading" />
<div v-else>Идет загрузка...</div>
</div>
</template>
<script>
import axios from 'axios'
import PostForm from './components/PostForm.vue'
import PostList from './components/PostList.vue'
export default {
components: { PostList, PostForm },
data() {
return {
posts: [],
dialogVisible: false,
isPostsLoading: false,
selectedSort: '',
sortOptions: [
{ value: 'title', name: 'По названию' },
{ value: 'body', name: 'По содержанию' },
],
}
},
methods: {
createPost(post) {
this.posts.push(post)
this.dialogVisible = false
},
removePost(post) {
this.posts = this.posts.filter((p) => p.id !== post.id)
},
showDialog() {
this.dialogVisible = true
},
async fetchPosts() {
try {
this.isPostsLoading = true
const res = await axios.get(
'https://jsonplaceholder.typicode.com/posts?_limit=10'
)
this.posts = res.data
} catch (error) {
alert('ошибка')
} finally {
this.isPostsLoading = false
}
},
},
mounted() {
this.fetchPosts()
},
}
</script>
<!-- флаг scoped - значит, что стили будут применяться только к этому комопненту -->
<style>
.app {
padding: 20px;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.app__btns {
margin: 15px 0;
display: flex;
justify-content: space-between;
}
</style>
<template>
<!-- MySelect component -->
<select v-model="modelValue" #change="changeOption">
<option disabled value="">Выберите из списка</option>
<option v-for="option in options" :key="option.value" :value="option.value">
{{ option.name }}
</option>
</select>
</template>
<script>
export default {
name: 'my-select',
props: {
modelValue: {
type: String,
},
options: {
type: Array,
default: () => [],
},
},
methods: {
changeOption(event) {
this.$emit('update:modelValue', event.target.value)
},
},
}
</script>
<style lang="scss" scoped></style>
I need to update modelValue, so I tried to add
:value="modelValue"
instead of
v-model="modelValue"
and it works, but I'm not sure if this is the correct solution.
If anyone else is encountering this issue when updating their vue version. Please note that this error started to appear on version 3.2.45.
For the implementation pattern, as noted on the documentation, props should be considered readonly within the component. Vue did not enforce it enough prior to version 3.2.45.
Documentation with links to good implementation patterns : https://vuejs.org/guide/components/props.html#one-way-data-flow

nuxtjs add and remove class on click on elements

I am new in vue and nuxt and here is my code I need to update
<template>
<div class="dashContent">
<div class="dashContent_item dashContent_item--active">
<p class="dashContent_text">123</p>
</div>
<div class="dashContent_item">
<p class="dashContent_text">456</p>
</div>
<div class="dashContent_item">
<p class="dashContent_text">789</p>
</div>
</div>
</template>
<style lang="scss">
.dashContent {
&_item {
display: flex;
align-items: center;
}
&_text {
color: #8e8f93;
font-size: 14px;
}
}
.dashContent_item--active {
.dashContent_text{
color:#fff;
font-size: 14px;
}
}
</style>
I tried something like this:
<div #click="onClick">
methods: {
onClick () {
document.body.classList.toggle('dashContent_item--active');
},
},
but it changed all elements and I need style change only on element I clicked and remove when click on another
also this code add active class to body not to element I clicked
This is how to get a togglable list of fruits, with a specific class tied to each one of them.
<template>
<section>
<div v-for="(fruit, index) in fruits" :key="fruit.id" #click="toggleEat(index)">
<span :class="{ 'was-eaten': fruit.eaten }">{{ fruit.name }}</span>
</div>
</section>
</template>
<script>
export default {
name: 'ToggleFruits',
data() {
return {
fruits: [
{ id: 1, name: 'banana', eaten: false },
{ id: 2, name: 'apple', eaten: true },
{ id: 3, name: 'watermelon', eaten: false },
],
}
},
methods: {
toggleEat(clickedFruitIndex) {
this.fruits = this.fruits.map((fruit) => ({
...fruit,
eaten: false,
}))
return this.$set(this.fruits, clickedFruitIndex, {
...this.fruits[clickedFruitIndex],
eaten: true,
})
},
},
}
</script>
<style scoped>
.was-eaten {
color: hsl(24, 81.7%, 49.2%);
}
</style>
In Vue2, we need to use this.$set otherwise, the changed element in a specific position of the array will not be detected. More info available in the official documentation.

Vue-MultiSelect Checkbox binding

The data properties of the multi-select component does not update on change. Check-boxes doesn't update on the front-end.
Expected Behavior: The check-boxes should get ticked, when clicked.
Link to code: https://jsfiddle.net/bzqd19nt/3/
<div id="app">
<multiselect
select-Label=""
selected-Label=""
deselect-Label=""
v-model="value"
:options="options"
:multiple="true"
track-by="library"
:custom-label="customLabel"
:close-on-select="false"
#select=onSelect($event)
#remove=onRemove($event)
>
<span class="checkbox-label" slot="option" slot-scope="scope" #click.self="select(scope.option)">
{{ scope.option.library }}
<input class="test" type="checkbox" v-model="scope.option.checked" #focus.prevent/>
</span>
</multiselect>
<pre>{{ value }}</pre>
</div>
new Vue({
components: {
Multiselect: window.VueMultiselect.default
},
data: {
value: [],
options: [
{ language: 'JavaScript', library: 'Vue.js', checked: false },
{ language: 'JavaScript', library: 'Vue-Multiselect', checked: false },
{ language: 'JavaScript', library: 'Vuelidate', checked: false }
]
},
methods: {
customLabel(option) {
return `${option.library} - ${option.language}`;
},
onSelect(option) {
console.log('Added');
option.checked = true;
console.log(`${option.library} Clicked!! ${option.checked}`);
},
onRemove(option) {
console.log('Removed');
option.checked = false;
console.log(`${option.library} Removed!! ${option.checked}`);
}
}
}).$mount('#app');
In your code you call onSelect and try to change the option argument of this function inside the function:
option.checked = true;
This affects only the local variable option (the function argument). And doesn't affect objects in options array in the data of the Vue instance, the objects bound with checkboxes. That's why nothing happens when you click on an option in the list.
To fix it find the appropriate element in options array and change it:
let index = this.options.findIndex(item => item.library==option.library);
this.options[index].checked = true;
Here is the code snippet with fix:
new Vue({
components: {
Multiselect: window.VueMultiselect.default
},
data: {
value: [],
options: [
{ language: 'JavaScript', library: 'Vue.js', checked: false },
{ language: 'JavaScript', library: 'Vue-Multiselect', checked: false },
{ language: 'JavaScript', library: 'Vuelidate', checked: false }
]
},
methods: {
customLabel (option) {
return `${option.library} - ${option.language}`
},
onSelect (option) {
console.log("Added");
let index = this.options.findIndex(item => item.library==option.library);
this.options[index].checked = true;
console.log(option.library + " Clicked!! " + option.checked);
},
onRemove (option) {
console.log("Removed");
let index = this.options.findIndex(item => item.library==option.library);
this.options[index].checked = false;
console.log(option.library + " Removed!! " + option.checked);
}
}
}).$mount('#app')
* {
font-family: 'Lato', 'Avenir', sans-serif;
}
.checkbox-label {
display: block;
}
.test {
position: absolute;
right: 1vw;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<link href="https://unpkg.com/vue-multiselect#2.0.2/dist/vue-multiselect.min.css" rel="stylesheet"/>
<script src="https://unpkg.com/vue-multiselect#2.0.3/dist/vue-multiselect.min.js"></script>
<div id="app">
<multiselect
select-Label=""
selected-Label=""
deselect-Label=""
v-model="value"
:options="options"
:multiple="true"
track-by="library"
:custom-label="customLabel"
:close-on-select="false"
#select=onSelect($event)
#remove=onRemove($event)
>
<span class="checkbox-label" slot="option" slot-scope="scope" #click.self="select(scope.option)">
{{ scope.option.library }}
<input class="test" type="checkbox" v-model="scope.option.checked" #focus.prevent/>
</span>
</multiselect>
<pre>{{ value }}</pre>
</div>

Repeated data in vue

i`m having a problem with my vue, the problem is im trying to print 2 words, that is 'A.2' and 'B.3', but when im printing it, it just show 'B.3' and 'B.3'. here is my code
this is a simple quiz project, so everytime a user choose option a with true status it should be adding 1 point to the score, i haven`t made that yet.
<template>
<div class="hello">
<h1 v-if="show">hai</h1>
<h1 v-else>hehe</h1>
<p>{{ nama}}</p>
<input type="text" v-model="nama">
<button type="button" v-on:click="hideTitle">Click Me</button>
<h3> 1.Yang Dipakai Di sepatu adalah </h3>
<p>{{ nama}}</p>
<h3 v-for="j in jawaban">
<input type="radio">
{{j}}
</h3>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
},
data : function() {
return{
nama: 'Luthfi',
show: true
},
{
jawaban: 'A.2',
correct: true
},
{
jawaban: 'B.3',
correct: false
},
{
jawaban: 'C.4',
correct: false
}
},
methods: {
hideTitle() {
this.show = !this.show
}
},
mounted: function () {
this.nama = 'Fitra'
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>
i expect there is 4 output from option A to D, but it kept showing me same option
In your code, data() returns only one object that contains
{
nama: 'Luthfi',
show: true
}
You must change this like:
data : function() {
return{
nama: 'Luthfi',
show: true,
jawaban: 'A.22',
correct: true,
jawabann: 'B.3'
}
}

Vue do not save clicked element style

I am generating small list of items. After click on every single item it should change style. Only one item can be selected. If you click on another item first item reverse to default value.
I have follow code:
<div class="LngList">
<div class="lng" v-for="item in languages">
<button :class="[ isLangSelected ? 'ui inverted basic button' : 'ui inverted red basic button' ]" #click=LangSelect(item.lang)>{{item.lang}}</button>
</div>
</div>
My method:
data: function (){
return {
isLangSelected: false,
mycode: "",
languages: [
{lang:'D'},
{lang:'C#'},
{lang:'Python'}
],
selectedLanguage: ""
}
},
methods: {
LangSelect(lang)
{
this.selectedLanguage = lang;
if(this.selectedLanguage.length != "")
{
this.isLangSelected = !this.isLangSelected;
}
}
}
But when I am clicking outside the button I am losing selected style.
I did small gif to show the problem:
This is possible of course with buttons, but why don't you use a radio input instead? Having only one item selected, that's what they are done for.
new Vue({
el: '#app',
data() {
return {
languages: [{
lang: 'D'
},
{
lang: 'C#'
},
{
lang: 'Python'
}
],
selectedLanguage: ''
};
},
computed: {
isLangSelected() {
return this.selectedLanguage !== '';
}
}
});
input[type=radio] {
visibility: hidden;
width: 0;
}
input[type=radio]:checked + span {
color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.min.js"></script>
<div id="app">
<label v-for="lang in languages">
<input type="radio" name="languages"
v-model="selectedLanguage" :value="lang.lang">
<span>{{lang.lang}}</span>
</label>
<div v-if="isLangSelected">
Selected language is: {{ selectedLanguage }}
</div>
</div>