I have a v-for like so:
<p> User Responses(s):</p>
<template v-for="item in UserResponses">
<ol v-if="item.some_condition==item._is_true">
<li :name="item.id + '__UR'"> [[ item.some_variable_to_render ] ]] </li>
</ol>
</template>
Works great. However, what I would like to do is say No user responses when the some_condition_is_true fails, but if I add an v-else the message (No user responses) will be printed in every loop, which is not desired ofcourse. How does one solve this problem?
To this end, I wondered if I could test if the element item.id + '__UR'" is present and if not add an element with text sayingNo user responses`
I am not sure however that this is the correct way to go about this.
EDIT
Just to reiterate: using v-if before v-for is not an option since the object being iterated on is a nesed JSON which then is filtered through some vuejs filters, so yea, this is not an option.
note that boolVar corresponds to your actual object key that contains the bools
let model.flag = true
function foo(val) => {
model.flag = val
return val
}
<p> User Responses(s):</p>
<template>
<div v-if="model.flag">
<div v-for="item in UserResponses" >
<ol v-if="foo(item.Boolvar)" >
<li :name="item.id + '__UR'"> [[ item.some_variable_to_render ] ]] </li>
</ol>
</div>
</div>
<h1 v-else>No user responses 😢</h1>
</template>
It is possible you may be able to implement this using CSS:
ol + .no-responses {
display: none;
}
There's a full example below but the idea is simply to hide the message if it comes after an <ol>. Tweak the selector to reflect your real use case.
I have tried to keep the template in my example exactly the same as the original code, with the addition of the relevant message and a couple of buttons for demo purposes.
new Vue({
el: '#app',
delimiters: ['[[', ']]'],
data () {
return {
UserResponses: []
}
},
methods: {
add () {
this.UserResponses.push(
{ some_condition: 3, _is_true: 3, id: 3, some_variable_to_render: 'A' },
{ some_condition: 3, _is_true: 4, id: 4, some_variable_to_render: 'B' },
{ some_condition: 4, _is_true: 4, id: 5, some_variable_to_render: 'C' },
{ some_condition: 5, _is_true: 5, id: 6, some_variable_to_render: 'D' }
)
},
clear () {
this.UserResponses.splice(0)
}
}
})
ol + .no-responses {
display: none;
}
<script src="https://unpkg.com/vue#2.6.11/dist/vue.js"></script>
<div id="app">
<p>User Responses(s):</p>
<template v-for="item in UserResponses">
<ol v-if="item.some_condition==item._is_true">
<li :name="item.id + '__UR'"> [[ item.some_variable_to_render ]] </li>
</ol>
</template>
<p class="no-responses">No user responses</p>
<button #click="add">Add values</button>
<button #click="clear">Clear values</button>
</div>
Related
I am new in vue js. I want to show hide elements on click.
So that What I can do -
make a property in data object like
isHidden: false
and add a mehtod like
showHide(){
return this.isHidden = !this.isHidden;
}
And bind the showHide method on click event like
<li #click="showHide">Some text</a>
But the problem is- if there are several list items binding the same click event,
all lists will be shown on click, or all will hide. But I want the
target element will only show or hide by click.
How can I do this, please?
You can use some data to determine what element you want show/hide. For example: id, index of each element
<button v-for="(i, ind) in listElement" #click="toggleElement(ind)" :key="ind">
Toggle {{ i }}
</button>
<p v-for="(i, ind) in listElementShow" :key="ind">
Element: {{ i }}
</p>
Logic:
data() {
return {
listElement: [ { name: 1, isShow: true}, { name: 2, isShow: true}],
}
},
computed: {
listElementShow() {
return this.listElement.filter(e => e.isShow);
}
}
methods: {
toggleElement(index) {
this.listElement[index].isShow = !this.listElement[index].isShow;
}
}
You can do something like this:
<template>
<ul class="list-with-hidden-items">
<li
v-for="item in list"
:key="item.id"
#click="showHide(item.id)"
>
{{ item.text }}
</li>
</ul>
</template>
<script>
export default {
data: () => ({
hidden: {}
}),
methods: {
showHide(id) {
this.$set(this.hidden, id, !this.hidden[id]);
}
}
}
</script>
and then you can render where you want your item to be shown like this:
<div v-if="!this.hidden.item1">I am shown because of item1 is not hidden</div>
To elaborate on some of these answers, you want to keep track of what's hidden and not hidden in your data object using a isHidden flag or similar.
Then when you loop through your object using v-for, conditionally set the isHidden property for the current item when it's clicked.
new Vue({
el: "#app",
data: {
itemList: [{
"id": 1,
"text": "first item",
"isHidden": false
},
{
"id": 2,
"text": "second item",
"isHidden": true,
},
{
"id": 3,
"text": "third item",
"isHidden": false,
},
],
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<ul>
<li v-for="item in itemList" :key="item.id">
<button #click="item.isHidden = !item.isHidden">
<span v-if="item.isHidden">Show</span>
<span v-else>Hide</span>
</button>
<span v-if="!item.isHidden">{{ item.text }}</span>
</li>
</ul>
</div>
You can use a dynamically bound classObject to do such a thing.
See Class and Style Bindings
Sandbox
<template>
<div #click="showHide()" :class="{ isHidden: isHidden }">
<p>This is a thing</p>
</div>
</template>
<script>
export default {
name: "HelloWorld",
data() {
return {
isHidden: false,
};
},
methods: {
showHide() {
this.isHidden = !this.isHidden;
},
},
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.isHidden {
background-color: grey;
}
div {
background-color: #fff;
}
</style>
I am looking for a way to group objects in different DIV's with a header text.
The following allows me to group 'category 'bla1' but I'd like to have categories 'bla2' and 'bla3' to be grouped as well. The number of categories isn't fixed and can be less or more.
This is what I have so far: JSFIddle
<div id="app">
<h2>Todos:</h2>
<ol>
<div v-for="todo in todos">
<p v-if="todo.category == 'bla1'">
{{ todo.text }}
</p>
</div>
</ol>
</div>
new Vue({
el: "#app",
data: {
todos: [
{ text: "Learn JavaScript", category: "bla1" },
{ text: "Learn Vue", category: "bla2" },
{ text: "Play around in JSFiddle", category: "bla1" },
{ text: "Build something awesome", category: "bla3" }
]
}
})
resulting is:
Todos:
Learn JavaScript
Play around in JSFiddle
But the desired result is:
Todos:
bla1
Learn JavaScript
Play around in JSFiddle
bla2
Learn Vue
bla3
Build something awesome
Create a computed property to group you list by category, and then do a nested loop in your template:
computed: {
groupedToDos() {
let groupedToDos = {};
this.todos.forEach(todo => {
if (todo.category in groupedToDos) {
groupedToDos[todo.category].push(todo)
}
else {
groupedToDos[todo.category] = [todo]
}
})
return groupedToDos
}
}
And then in your template:
<div id="app">
<h2>Todos:</h2>
<ol>
<div v-for="(todos, key) in groupedToDos">
<h3>{{ key }}</h3>
<p v-for="todo in todos">
{{ todo.text }}
</p>
<br />
</div>
</ol>
</div>
jsfiddle
The computed property approach is the best IMHO.
computed:{
sampleComputed(){
if (this.sampleData.value === "Sample 1") return "Sample 1 Value"
if (this.sampleData.value === "Sample 2") return "Sample 2 Value"
return "Sample 0 Value"
}
}
Above sample code can be used with Switch case also if you have more than 5 conditions, or even using object literals to fetch returning data to improve time complexity.
However, if you want you can add more v-if inside the template itself, but it is not advisable to do so since you should focus on separating complex logic from template as a best practice.
References: Proper way to express switch statement with Vue data bindings
This a simple to-do list and I am trying to add an id in each <li> that is rendered. Each id should be named different (obviously) but similar name: "id0","id1","id2"...etc
The number comes from the directive v-for="i in items"
I do it like this:
<li :id="`id${i.id}`" v-for="i in items" :key="`id${i.id}`">
Complete code:
<ul class="item-list-ul">
<li :id="`id${i.id}`" v-for="i in items" :key="`id${i.id}`">{{ i }}
<div class="item-butons">
<b-button class="done-btn" #click="strikeItem(i)" size="sm" variant="outline-dark">DONE!</b-button>
<b-button class="delete-btn" #click="deleteItem(i)" size="sm" variant="warning">Delete</b-button>
</div>
</li>
</ul>
My items array:
data () {
return {
items: ["five", "<li>", "should","be","rendered"]
}
}
But when I check in the console the names of the new dynamically created id´s of the <li> they just appear idundefined when in the case of having for example 3 <li> they should appear like this:
id0
id1
id2
However in the console there aren't any errors. It seems that the vue-html simply does not read a number in ${i.id} but just undefined. Why?
If items is just an array of strings, then there is no id property available. The standard idiom in this case is to use the index like so:
<li v-for="(i, index) in items" :id="`id${index}`" :key="index">
Alternatively, you could reshape your data to have ids like:
[{ id: 0, text: 'foo' }, { id: 1, text: 'bar' }, { id: 2, text: 'baz' }]
That could be done as part of a computed property, for example. Note this would also require a few changes to your template.
I'm showing a list of release versions, but the part I'm stuck on is I want to be able to click the release version and show the job_execs for that version. I'm not sure how to do this other than using a ternary expression and binding it to click event. Anyway, what I'm trying to do is not working because nothing changes when I click the versions.
<template>
<td>
<div v-for="release in release_versions" :key="release">
<li>
<span #click="showRelease = showRelease === release.version ? '' : release">
Release {{ release.version }}
</span>
<ul v-if="showRelease === release.version">
{{ release.job_execs }}
</ul>
</li>
</div>
</td>
</template>
<script>
export default {
name: 'LatestBuilds',
data() {
return {
release_versions: [
{ version: '1', job_execs: [] },
{ version: '2', job_execs: [] },
],
showRelease: false,
}
},
}
</script>
Important things to note:
job_execs is populated with data, I'm just not showing it in the OP;
the numbers of versions and job_execs are always changing;
I'd rather not use a ternary expression if at all possible, just not
sure how else to do this.
I would recommend you use a Method here, instead of adding this logic inside the #click attr.
To actually output the selected release's job_execs, you'll need another v-for inside the ul.
Something like the following should work:
<template>
<td>
<div v-for="release in release_versions" :key="release">
<li>
<span #click="selectRelease(release.version)">
Release {{ release.version }}
</span>
<ul v-if="selectedVersion === release.version">
<li v-for="job_exec in release.job_execs">
{{ job_exec }} <!-- use job_exec data if this is an object -->
</li>
</ul>
</li>
</div>
</td>
</template>
<script>
export default {
name: 'LatestBuilds',
data() {
return {
release_versions: [
{ version: '1', job_execs: [] },
{ version: '2', job_execs: [] },
],
selectedVersion: null,
}
},
methods: {
selectRelease(version) {
this.selectedVersion = version
}
},
}
</script>
Well I hope to explain, I'm generating this data from a component, when I click the checkbox changes in the generated data are not reflected, but when clicking the button with a data already in the instance changes are made, I appreciate if you explain Why or do they have a solution?
this my code
js
Vue.component('fighters', {
template: '#fighters-template',
data() {
return {
items: [
{ name: 'Ryu' },
{ name: 'Ken' },
{ name: 'Akuma' }
],
msg: 'hi'
}
},
methods: {
newData() {
this.items.forEach(item => {
item.id = Math.random().toString(36).substr(2, 9);
item.game = 'Street Figther';
item.show = false;
});
this.items.push()
},
greeting() {
this.msg = 'hola'
}
}
});
new Vue({
el: '#app'
})
html
<main id="app">
<fighters></fighters>
</main>
<template id="fighters-template">
<div class="o-container--sm u-my1">
<ul>
<li v-for="item in items">
<input type="checkbox" v-model="item.show">
{{ item.name }}</li>
</ul>
<button class="c-button c-button--primary" #click="newData()">New Data</button>
<h2>{{ msg }}</h2>
<button class="c-button c-button--primary" #click="greeting()">Greeting</button>
<hr>
<pre>{{ items }}</pre>
</div>
</template>
this live code
https://jsfiddle.net/cqx12a00/1/
Thanks for you help
You don't declare the show variables that your checkboxes are bound to, so they are not reactive – Vue is not aware when one is updated.
It should be initialized like so:
items: [
{ name: 'Ryu', show: false },
{ name: 'Ken', show: false },
{ name: 'Akuma', show: false }
]
You might think that newData would fix it, since it assigns a show member to each item, but Vue cannot detect added properties so they're still not reactive. If you initialized the data as I show above, then the assignment would be reactive.
If you want to add a new reactive property to an object, you should use Vue.set.