I'm creating a list of (thumbnail) 2 images, and #click each image should be expanded to max-width, by adding said class ('max-w-full') to the classes. The class is added by setting the array entry with the same index nr. as the image (imgclicked[0], imgclicked[1]) in the list to 1.
data:() {
imgclicked=[0,0], //by default both are set to 'small'/not 'max-w-full'
},
the template part looks like this:
<template v-for="(image,index) in images>
<a #click="zoomImgClicked(index)">
<img :src="image.filename" :class={'max-w-full' : imgclicked[index]==1,'w-12' : imgclicked[index]==0}">
</a> // using this.imgclicked[index] gives me the error 'this.imgclicked[0] is undefined'
</template>
on click the method zoomImgClicked() ist launched:
zoomImgClicked: function(i){
if(this.imgclicked[i]==0){
this.imgclicked[i]=1
}else{
this.imgclicked[i]=0
}
},
But this is not working. If I open the vue console and change the values of imgclicked[0] manually from 0 to 1 it works (images ae being resized). Also I see the method doing it's work, when logging in the console, the values are changed from 0 to 1 and vice versa. But it's not reflected on the page #click.
Why is that?
Please read Change Detection Caveats in the Vue docs.
Vue cannot detect the following changes to an array:
When you directly set an item with the index, e.g. vm.items[indexOfItem] = newValue
When you modify the length of the array, e.g. vm.items.length = newLength
You should use the $set method for this:
this.$set(this.imgclicked, i, 1)
try with array syntax:
<img
:src="image.filename"
:class="[
{
'max-w-full': imgclicked[index]==1,
'w-12': imgclicked[index]==0
},
'static class if need'
]"
>
Related
I am encountering this strange behavior in my code in Vue 2
What I am trying to achieve here is when I clicked on one of the checkbox, other checkbox(es) are checked as well. I only want the selected checkbox to be checked only. Is there a way to do so?
this is my component
<div v-for="(item,index) in api[0].choice" :key="index">
<input type="checkbox" #change="detect" v-model="option[index].checked">
<label>{{item}}</label>
</div>
data(){}
option: null,
mounted(){}
this.option = Array(this.api[0].choice.length).fill({checked: false})
Array.prototype.fill() passes a reference and not a new instance. So essentially all of your items inside of your array are mapped together. Thus when index n changes so do all the other values.
The easiest way to fix this, is to fill with undefined, by not passing an argument to fill, and then update the array, mapping the same object over and over again. Thus creating new instances of the object, instead of referencing the original object.
this.option = Array(5)
.fill()
.map((x) => ({ checked: false }));
Example on codesandbox
If you are expecting to have really large arrays .map() will be inefficient. Use a for loop instead
this.option = new Array(5);
for (let i = 0; i < 5; i++) {
this.option[i] = { checked: false };
}
In vuejs the elements are assigned an attribute starting 'data-v-***'
I could not find any docs about fetching this value so ended up using refs and grabbing the attributes of the main node:
<template>
<div class="m-colour-picker" ref="thisContainer">
...
</div>
</template>
const attributes = this.$refs.thisContainer.getAttributeNames();
let dataAttribute = '';
attributes.forEach((attribute: string) => {
if (attribute.substring(0, 5) === 'data-') {
dataAttribute = attribute;
}
});
But it feels a little forced.. is there a method in vue to fetch this already built in?
That has little to do with Vue.js. Data attributes for any element are automatically synced with it's internal dataset object.
Example:
console.log(foobar.dataset);
console.log(foobar.dataset.vFoo);
console.log(foobar.dataset.vBar);
// notice how data attributes containing more than the initial data- dash
// are automatically transformed to camel case:
// data-v-foo-bar ===> dataset.vFooBar
console.log(foobar.dataset.vFooBar);
// if all you care about is the names of the attributes:
console.log(Object.keys(foobar.dataset));
<div id="foobar" data-v-foo="bar" data-v-bar="baz" data-v-foo-bar="foobaz"></div>
I'd like to do v-for loop for creating a div. I'm doing a minesweeper game and here is my code :
<div class="grid">
<div class="square"
v-for="(square, index) in squares"
:id="index"
:key="index"
:class="squares[index]"
#click="clicked(square, index)"
>
</div>
</div>
'Squares' in this code is an array with shuffled classes 'bomb' or 'empty'. I know that it's wrong because after I click on random square I get only this class from te 'squares' array. What should be there instead of this 'squares' array in v-for. I want to get whole with classes, attributes etc. because later I have to use 'classList' 'contains' etc.
Sorry, maybe I'm completly wrong and talking bullshit, but I started with vue 3 weeks ago.
Here is the method clicked which I want to use
clicked(square) {
if(this.isGameOver) return;
if(square.classList.contains('chechked') || square.classList.contains('flag')) return
if(square.classList.contains('bomb')) {
this.gameOver(square);
} else {
let total = square.getAttribute('data');
if(total != 0) {
square.classList.add('checked');
square.innerHTML = total;
return
}
}
square.classList.add('checked');
}
You want to access the div element but you are passing the object in the method and you are asking for classList into the object (that does not have it). You should query the element instead.
Change the #click handler in your component to:
#click="clicked"
and your method to:
clicked(event) {
let square = event.target;
console.log(square);
console.log(square.classList);
...
Currently I display a list of hotels for each city in a Vue.js / Buefy form using:
<option
:value="h['#attributes'].Name"
v-for="h in cities[form.cities[i].index].Hotels.Hotel"
:key="cities[form.cities[i].index].Hotels.Hotel.Name"
v-if="isArray(form.cities[i].index)"
v-text="h['#attributes'].Name"></option>
What should I add to sort them alphabetically? I'm at loss, as I don't know Vue / Buefy so well and I'm modifying a code somebody else wrote.
Thanks!
It is important to understand what your code is doing so that you know where you need to make changes.
Your loop v-for is iterating over your array cities[form.cities[i].index].Hotels.Hotel (the naming seems odd to me).
Within this array, there is a key #attributes which holds an object with a key Name, which is probably what you want to use for sorting.
Normally I would go with computed properties for these things but since you have the array based on a parameter (form.cities[i].index) I am not sure that would work so easily. So instead you can use a method to get a sorted version of your array. In your Vue instance, add the following to the "methods" property:
methods: {
sortedHotels: function(hotels) {
tmp = this.hotels.slice(0);
tmp.sort(function(a,b) {
return (a['#attributes'].Name > b['#attributes'].Name) ? 1 : ((b['#attributes'].Name> a['#attributes'].Name) ? -1 : 0);
});
return tmp;
},
},
Then, instead of looping through the normal array, you loop through the result of the function call of that array:
<option
:value="h['#attributes'].Name"
v-for="h in sortedHotels(cities[form.cities[i].index].Hotels.Hotel)"
:key="cities[form.cities[i].index].Hotels.Hotel.Name"
v-if="isArray(form.cities[i].index)"
v-text="h['#attributes'].Name"></option>
I have this html element:
Link text
I want to add data-tooltip and title attributes dynamically by condition:
Link text
Is there any way in VueJS to add multiple dynamic attributes at same time:
<!-- instead of this: -->
Link text
<!-- something like this: -->
<a href="javascript:" ...tooltipAttributes >Link text</a>
You could take advantage of v-bind on the DOM element you wish to apply multiple attributes to based on some dynamically changing condition.
Here's a Plunker example demonstrating how you might go about it.
Take note of the object returned:
computed: {
multiAttrs() {
return this.showAttrs ? {
'data-toggle': 'tooltip',
title: 'Some tooltip text',
} : null;
}
}
You should be able to use v-bind="tooltipAttributes"
the docs here https://v2.vuejs.org/v2/api/#v-bind have more info, but the key part is under usage
Dynamically bind one or more attributes, or a component prop to an expression.
From the Docs:
1. You can dynamically bind multiple attributes/props to a single element by using v-bind:
(no colon, no extra attribute, just v-bind)
<a href="#" v-bind="tooltipAttributes" >Link text</a>
2. And then declare the variable in the computed section:
(you can also declare it in the data section, but that would require manual direct value changes)
computed() {
return {
tooltipAttributes: {
title: 'Title',
'data-toggle': this.toggle === true && !disabled
}
}
}
Note: Attributes with dashes/hyphens - in them (e.g. data-toggle) need to be a string because Javascript doesn't recognize - as a valid symbol in variable naming.
This is THE SAME AS:
<a href="#" title="Title" :data-toggle="this.toggle === true && !disabled" >Link text</a>