How to use v-show inside a loop - javascript

Thanks #zero298, the dup doesn't apply in important ways. I want to show all of the items in the object array, just conditionally add some UI to each based on user signal. Furthermore, v-if and v-show are very different (as noted here and elsewhere). vue q/a on this site seems to be pretty light -- because it's new -- seems like a mistake to aggressively close on such a new topic.
I can make show work this way...
(NOTE... Please run the snippets in "Expand Snippet" mode to see the behavior over the console stuff. Not sure how to suppress the vue messages in console)
const app = new Vue({
el: '#app',
data: {
show: false
},
methods: {
}
});
.demo{
width: 100px;
height: 30px;
background-color: green;
margin: 10px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.14/vue.js"></script>
<div id="app">
<h3>Hi there</h3>
<div class="demo" #click="show = !show">Click me</div>
<div v-show="show">Show or hide me</div>
</div>
But why can't I make it work this way...
const app = new Vue({
el: '#app',
data: {
objects: [ { name: 'a' }, { name: 'b' }, { name: 'c' } ],
show: [false, false, false]
},
methods: {
}
});
.demo{
width: 100px;
height: 30px;
background-color: green;
margin: 10px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.14/vue.js"></script>
<div id="app">
<div v-for="(object, i) in objects">
<h3>Hi there {{ object.name }} show status is {{ show[i] }}</h3>
<div class="demo" #click="show[i] = !show[i]">Click me</div>
<div v-show="show[i]">Show or hide me</div>
</div>
</div>
I've seen doc content saying not to use v-if in a loop, but what's wrong with v-show? There's evidence that the #click expression assigning to the show array isn't running (see the variable state next to the "hello" message), but why not?
I've tried moving that logic to a method, and moving the v-show check to a method, but with the same results.
Also, my array of objects will appear async and have an unknown (but small) length. I don't want to add a "show" property to those objects because user can save them back to the server. What's the right time and place to allocate a show array of bools that matches the objects array?

This because of vue's change detection. Vue cannot detect that the array is changing.
You have to do something like this to detect it:
<div class="demo" #click="$set(show, i, !show[i])">Click me</div>
Helpful: https://v2.vuejs.org/v2/guide/list.html#Array-Change-Detection

Related

Vue multiselect template slot clear does not work

i copied the code from async part of the documentation, because that's the 'X' to remove value i want.
here is my general component that i use in other vue components
<template>
<div>
<multiselect
v-model="items"
:options="filteredList"
:multiple="multiple"
:close-on-select="multiple ? false : true"
:show-labels="false"
:placeholder="placeholder"
track-by="id"
:label="label"
#input="inputChanged"
:internal-search="false"
#search-change="searchItems"
>
<template slot="clear" slot-scope="props">
<div class="multiselect__clear" v-if="items.length" #mousedown.prevent.stop="clearAll(props.search)"></div>
</template>
</multiselect>
</div>
</template>
<script>
export default {
model: {
prop: 'parentItems',
event: 'change',
},
props: ['multiple', 'list', 'placeholder', 'label', 'parentItems'],
data() {
return {
items: this.parentItems,
filteredList: this.list,
}
},
methods: {
searchItems(query) {
let q = latinize(query.toLowerCase().replace(/\s/g,''))
this.filteredList = this.list.filter(li => latinize(li[this.label].toLowerCase().replace(/\s/g,'')).includes(q))
},
inputChanged() {
this.$emit('change', this.items);
},
clearAll() {
this.items = this.multiple ? [] : null
},
},
}
</script>
everything works as desired, except the X to clear selection is never displayed.
i found the clear element in console, it has width of 255 and height of 0. i tried to put X between the div tags, like this
<template slot="clear" slot-scope="props">
<div class="multiselect__clear" v-if="items.length"
#mousedown.prevent.stop="clearAll(props.search)"
>
X
</div>
</template>
but it would display above the select input field. also changing the height attribute in dev console just made clear space above input field.
what am i missing?
vue-multiselect does nothing special with the clear slot other than render it before the input tags. It leaves the styling and behavior/implementation entirely up to the end user.
In addition, the example from the docs you linked poorly implements the slot, as the provided slot has no contents, so the div won't be visible or clickable, making it effectively useless. Not to mention, it uses the obsolete (pre-2.6) slot syntax of Vue, which would cause warnings on the browser console if using the development build of Vue.
Solution
The clear slot should look like this:
<multiselect v-model="value"
:options="options"
multiple
taggable
#tag="addTag">
<template v-slot:clear>
<button v-if="value.length" class="multiselect__clear" #click="clearSelectedTags">
Ⓧ Clear selection
</button>
</template>
</multiselect>
demo
Thanks to tony19 I went and inspected the part of documentation I mentioned in the question.
I found out that they use different code for the example, so to attain desired effect, I need to add following code to my css.
.multiselect__clear {
position: absolute;
right: 41px;
height: 40px;
width: 40px;
display: block;
cursor: pointer;
/*z-index: 2;*/
}
.multiselect__clear:after, .multiselect__clear:before {
content: "";
display: block;
position: absolute;
width: 3px;
height: 16px;
background: #aaa;
top: 12px;
right: 4px;
cursor: pointer;
}
.multiselect__clear:before {
transform: rotate(45deg);
}
.multiselect__clear:after {
transform: rotate(-45deg);
}

Vue js text add animation

When I click on a component, the text changes. Is it possible to smoothly animate this addition of text and increase the height of the block?
Some example:
new Vue({
el: '#app',
data: {
message: 'Hello, i need smooth animation when text added(click me please)'
}
})
new Vue({
el: '#app2',
data: {
message: 'Hello, i need smooth animation when text added(click me please)'
},
methods: {
addText() {
console.log('1');
TweenLite.to(document.getElementById('test'), 2, {text:"This is animation with GSAP (vue plugin to animate anything) and i don't know how to animate it smooth", ease:Linear.easeNone});
}
}
})
#app, #app2 {
border: 1px solid black;
width: 150px;
text-align: center;
}
<script src="https://unpkg.com/vue"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/2.1.3/TweenLite.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/2.1.3/plugins/TextPlugin.min.js"></script>
<div id="app" #click="message = message+message+message">
<p>{{ message }}</p>
</div>
<div id="app2" #click="addText">
<p id="test">{{ message }}</p>
</div>
Yes! In fact Vue provides a very robust engine for performing pretty much any kind of web GUI animation you could imagine, using a concept called "transitions". All you need to know is basic CSS! Here's the main documentation for it, and there are plenty of other examples to be found on Google.
https://v2.vuejs.org/v2/guide/transitions.html
U can watch the text value. like this doc.

Dynamic image is not loaded via :src

I am working on a simple Vue app, using vue-cli and webpack for that purpose.
So basicly i have 2 components, a parent and a child component ~
like this:
<template>
<div class="triPeaks__wrapper">
<div class="triPeaks">
<tri-tower class="tower"></tri-tower>
<tri-tower class="tower"></tri-tower>
<tri-tower class="tower"></tri-tower>
</div>
<div class="triPeaks__line">
<tower-line :towerLine="towerLineCards" />
</div>
<tri-pack />
</div>
</template>
the towerLineCards is the important thing there, it is a prop that is passed to the tower-line component, it is basicly a array with 10 elements, it is a array with 10 numbers that are shuffled, so it can be something like that:
[1,5,2,6,8,9,16,25,40,32]
this array is create via beforeMount on the lifecycle.
On the child component:
<template>
<div class="towerLine-wrapper">
<div class="towerLine">
<img v-for="index in 10" :key="index" class="towerLine__image" :src="getImage(index)" alt="">
</div>
</div>
</template>
<script>
export default {
props: {
towerLine: {
type: Array,
required: true
}
},
method: {
getImage (index) {
return '#/assets/images/cards/1.png'
}
}
}
</script>
<style lang="scss">
.towerLine {
display: flex;
position: relative;
top: -90px;
left: -40px;
&__image {
width: 80px;
height: 100px;
&:not(:first-child) {
margin-left: 3px;
}
}
}
</style>
the issue is with the :src image that i am returning via the getImage(), this way it is not working. If i change to just src it works just fine, i did this way just to test, because the number in the path should be dynamic when i got this to work.
What is wrong with this approach? any help?
Thanks
Firstly, you should use a computed property instead of a method for getImage().
And to solve the other problem, you could add require(YOUR_IMAGE_PATH) when you call your specific image or put it inside /static/your_image.png instead of #/assets/images/cards/1.png.

Hide and show elements with v-if

Here is my fiddle : DEMO
new Vue({
el: '#app',
data: {
modules: ['abc', 'def']
}
})
How can I hide/show with v-if based on the values found in an array ( modules[] ) ?
Any help would be appreciated.
Thank you.
You have at least a couple of options to manage this, which I'll describe here. The first is a simple check inside of v-if to see if the item is contained within modules. For example:
<div class="abc box" v-if="modules.indexOf('abc') >= 0">abc</div>
<div class="def box" v-if="modules.indexOf('def') >= 0">def</div>
...
This is a little bit crude and not great from a maintenance or performance perspective. Another option would be to use the modules array as the source for your collection of divs, using v-for. For example:
<div class="box" v-for="module in modules" :class="module">{{ module }}</div>
There's a few things to note here:
v-for looks directly at the modules array to determine the number of divs to render.
The value in the array is used both as the text for the div ({{ module }}) and as one of the classes (:class="module").
The class box is always applied, using the standard class attribute. The combination of this and the :class binding end up with e.g. abc box as the class list.
Here's a working example demonstrating the v-for approach.
new Vue({
el: '#app',
data: {
modules: ['abc', 'def']
}
});
.box {
height: 75px;
width: 75px;
background: #444;
padding: 10px;
margin: 5px;
color: #fff;
}
<script src="https://unpkg.com/vue#2.5.6/dist/vue.js"></script>
<div id="app">
<div class="box" v-for="module in modules" :class="module">{{ module }}</div>
</div>

How do I pass on checkbox id using v-on in Vue.js when checkbox is clicked?

this is my code:
<input type="checkbox" id="checklist-1-checkbox-1" v-on:click="addGreenClass(this)" name="checkbox1-1">
vue.js
new Vue({
el: '#admin-dashboard',
methods: {
addGreenClass(element) {
console.log(element);
},
}
});
In my console, I get the object referring to the whole document rather than referring to the input tag that the checkbox is sourcing the event from.
I'm sure I have the code wrong, but I can't seem to find my answer in similar questions that are asking for the code in pure Javascript in. Could someone help me here? Thank you.
Passing this from the context of an inline statement in the Vue instance's template is going to pass a reference to the Window object.
If you want to access an element from a method in your Vue instance, you can use a ref. No need to pass anything.
Here's the documentation on the ref attribute.
Here's an example:
new Vue({
el: '#app',
methods: {
addGreenClass() {
console.log(this.$refs.input)
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.4/vue.min.js"></script>
<div id="app">
<input ref="input" #click="addGreenClass">
</div>
If you're trying to add a class to the element, you don't need to reference the element at all in your addGreenClass method. Simply bind an array of class names (say inputClasses) to your input using :class="inputClasses", and then push the name of the class you want to the array in your addGreenClass method. Vue will update the DOM automatically:
new Vue({
el: '#app',
data() {
return {
inputClasses: ['large'],
}
},
methods: {
addGreenClass() {
this.inputClasses.push('green');
}
}
})
.large {
height: 40px;
}
.green {
border: 3px solid green;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.4/vue.min.js"></script>
<div id="app">
<input :class="inputClasses" #click="addGreenClass">
</div>
Since you are using checkboxes, I'm stealing a little code from #thanksd and showing how it might be done without even using a method. This highlights Vue's data driven approach.
new Vue({
el: '#app',
data() {
return {
classes: [],
}
}
})
.large {
height: 40px;
}
.green {
border: 3px solid green;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.4/vue.min.js"></script>
<div id="app">
<div :class="classes">Styled DIV</div>
<label>
<input type="checkbox" value="green" v-model="classes">
Green
</label>
<label>
<input type="checkbox" value="large" v-model="classes">
Large
</label>
</div>

Categories