Hide and show elements with v-if - javascript

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>

Related

VueJS quick render template

Is there a way, similar to KnockoutJS to quick and easy render content from a template by Id?
<script type="text/html" id="template-example"><span>Hello world!</span></script>
<div data-bind="template: '#template-example'"></div>
I've read the documentation but did not find anything, and also tried to create a component called quick-template where the template attribute used a vale from props, but I did not render anything. Probably because the template is bound before props are populated
Version 2.6.12
When it comes to Vue, the only reputable source is the documentation.
In theory:
The template is a core part of each vue component. In fact, a vue component won't render without one (or without a render function - which I consider just another way of providing a template, since that's what it returns).
It can can be provided as:
template: rawHTML (HTML string)
template: '#some-id' (where #some-id points to a <template>, <script type="text/x-template"> or plain DOM element).
<template>content</template> - in SFCs
render function (returns an HTML string). Overrides any other form of providing a template.
el: '#some-id', the template consists of the .innerHTML of the DOM element with that id 1, 2.
I won't cover render functions, they're not relevant for your case. Vue parses both HTML and JSX. Vue loader accepts pre-processors, which makes it compatible with PUG, using a plugin.
In practice:
Register the templates as components, with the desired template attribute, alongside anything else they might need (props, data, computed, methods, components, directives, filters, emits, watch, expose, compilerOptions, inheritAttrs, mixins, extends and/or lifecycle hooks).
Use them in your app.
Demo 3:
Vue.component('some-list', {
template: '#some-list',
props: ['powers']
})
Vue.component('some-item', {
template: '#some-item',
props: ['value']
})
Vue.component('svg-example', {
template: '#svg-example'
})
new Vue({
el: '#app',
data: () => ({
myComponents: ['some-list', 'svg-example'],
powers: 8
}),
methods: {
getPowers(comp) {
return (comp === 'some-list') && Number(this.powers)
}
}
})
#app {
display: flex;
justify-content: space-evenly;
}
#app input {
height: min-content;
}
#app div > div {
padding: 3px 7px;
}
<script src="https://unpkg.com/vue#2.6.14/dist/vue.min.js"></script>
<div id="app">
<span v-text="powers"></span>
<input type="range" v-model="powers" min="2" max="12">
<component v-for="component in myComponents"
:powers="getPowers(component)"
:key="component"
:is="component" />
</div>
<template id="some-list">
<div>
<some-item v-for="(item, key) in powers" :key="key" :value="item" />
</div>
</template>
<template id="some-item">
<div v-text="Math.pow(2, value)" />
</template>
<template id="svg-example">
<svg version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 300 200"
width="300">
<image href="https://picsum.photos/300/200" />
</svg>
</template>
Same example 4, 5, registering the components on the app, not globally on Vue:
new Vue({
el: '#app',
components: {
SomeList: {
template: '#some-list',
props: ['powers'],
components: {
SomeItem: {
template: '<div v-text="Math.pow(2, value)" />',
props: ['value']
}
}
},
SvgExample: {
template: '#svg-example'
}
},
data: () => ({
powers: 8
})
})
#app {
display: flex;
justify-content: space-evenly;
}
#app input {
height: min-content;
}
#app div > div {
padding: 3px 7px;
}
<script src="https://unpkg.com/vue#2.6.14/dist/vue.min.js"></script>
<div id="app">
<span>{{powers}}</span>
<input type="range" v-model="powers" min="2" max="12">
<some-list :powers="Number(powers)"></some-list>
<svg-example></svg-example>
</div>
<script type="text/x-template" id="some-list">
<div>
<some-item v-for="(item, key) in powers" :key="key" :value="item" />
</div>
</script>
<!-- innerHTML of this hidden div is used as template -->
<div id="svg-example" style="display: none">
<svg version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 300 200"
width="300">
<image href="https://picsum.photos/id/4/300/200" />
</svg>
</div>
Notes:
1 - Only works inside new Vue({}).
2 - The placeholder (initial DOM element) will be replaced with the result of Vue's mounting process, so any event registered on the element prior to mounting will be lost.
3 - The template can be anywhere in DOM, not necessarily inside the Vue app.
4 - Less flexible (SomeItem component needs to be declared inside SomeList component, if declared in App, <SomeList /> can't use it - not sure why, I'd have expected it to be available to child components).
5 - I've tweaked a few things with alternate syntaxes between the two examples, so you could compare them
You could register a custom component and use the content of the script tag as template.
Vue.component('my-component', '#template-example');
Then somewhere in your Vue app:
<div id="app">
<my-component></my-component>
</div>

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.

How to use v-show inside a loop

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

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.

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