I wanted to check for a condition if a particular property of an object is same as the very next object's property in an array-of-objects, iterated through a v-for loop.
Sample JSON object:
[
{ Date: '21-July-2017', ...},
{ Date: '21-July-2017', ...},
{ Date: '25-July-2017', ...},
...
]
The requirement is to check if each consecutive Date value is same, so that we will hide the Date header in the UI.
<div v-for="(everyDay, dayIndex) in eachPost.values" v-bind:key="dayIndex">
<div v-if="everyDay['Date'] !== eachPost.values[dayIndex+1].Date">
THIS DOESN'T WORK
</div>
</div>
Is there an alternative to accomplish this req?
Your problem is when you get to your last item in the array your dayIndex+1 object does not exist. It is undefined. What you need to do is in your template determine if your object is defined and go from there.
<div v-for="(everyDay, dayIndex) in eachPost.values" v-bind:key="dayIndex">
<template v-if="eachPost.values[dayIndex+1]">
<div v-if="everyDay['Date'] !== eachPost.values[dayIndex+1].Date">
THIS WORKS
</div>
</template>
</div>
Here is a jsFiddle of my working example
Related
i'm trying to use data property commentsToShow in my html template to limit the amount of data that displays on my webpage
this is my template
<div v-if="index < products.length" v-for="(commentIndex, index) in computedProduct">
<div class="title pt-4 pb-1">{{products[index].title}}</div>
</div>
if i add commentsToShow in my for loop i get one product but the computed products doesn't work same way the other way round
this my script tag
<script>
export default {
data() {
return {
commentsToShow: 1,
totalComments: 0,
};
},
computed: {
computedProduct() {
let tempRecipes = this.products;
if (this.filterPrice !== "true");
}
};
</script>
if i change computed property to commentsToShow this the error i get in my console
The computed property "commentsToShow" is already defined in data.
please how can i get the value of commentToShow in my template
according to vue's official docs it's not recommended to use v-for and v-if on the same element
try using v-if on a wrapper div or template element
<div v-for="(commentIndex, index) in computedProduct">
<template v-if="index < products.length">
<div class="title pt-4 pb-1">{{products[index].title}}</div>
</template>
</div>
v-if has higher priority so it's executed first and index will not be defined yet.
also you have to return something on your computed property function in order to use it
You can use the slice method on a computed property, like this:
<script>
export default {
data() {
return {
commentsToShow: 1,
allComments: []
};
},
computed: {
listComments() {
return allComments.slice(0, commentsToShow);
}
};
</script>
You can also use pages to show the comments, in this case you can return like this:
return allComments.slice((currentPage - 1) * commentToShow, commentsToShow);
The first argument of slice is the start index, the second is the number of elements to get
The computed property "commentsToShow" is already defined in data.
Equivalently how you cannot have more than one variable with the same name defined in a scope. A computed property cannot have the same name as an existing data property. Essentially, they co-exist in the same namespace, thus they have to be unique.
You have a name clash, and that is what the error is saying.
I am printing some data on some condition by looping It is working fine but i feel like my approach is not correct as i am doing calculation based work inside the script tag (javascript portion below )
My for loop
<div v-for="row in cars.honda" v-if="cars.id == row.car_id" >
**<span v-show="txt=='show'">{{ cars.id == row.car_id?txt="sizes":txt="showerror"}}</span>**
<p v-if="cars.id == row.car_id" >
{{ row.car_name}}
</p>
</div>
Is it okay or good practice to assign value to txt variable inside tag as i am unable to do the same thing when I create a function in script tag It doesnt works that way as the txt variable value is not updated
No, it not recommended even the first line of code is not recommended. Using v-for and v-if together is not a good idea. You can read more about it vuejs doc
Assigning a new txt variable, that also you should generally avoid, it will hard to track if your template have more code.
Here is sample you can do it in a simple way.
<div v-for="row in cars.honda" :key="row.id">
<div v-if="cars.id == row.car_id">
<span v-if="somecondition">Show Valid Data</span>
<span v-else>Show Error</span>
<p>
{{ row.car_name}}
</p>
</div>
</div>
Generally its good practice to avoid multiple computation in the template, template are mean to be represent the data with help of directive like v-for v-if etc. They are not much responsible for computation of logic. Also use :key with v-for for better performance.
The reason why we don't use ternary operations like you have used is because it's a recipe for bugs. Everything inside the Double brackets "{{ }}" is escaped which means if you include any html tags they would be removed. Your example should be working fine but it's best practice to only stick with v-if & v-else
<span v-if="somethingIsTrue">{{showSomething}}</span>
Well, you although you could use v-if with v-for, it's not a recommended approach. Read this for more.
When used together with v-if, v-for has a higher priority than v-if. See the list rendering guide for details.
Therefore, you should always use v-for independently and then use v-if inside it to show/hide content based on some conditions.
Now coming to your question on whether it is safe to assign values in templates, the answer is no it's not, because that's just a bad syntax which is difficult to read. Ternary operator is not used to assign values like the way you have used.
Correct syntax for assigning using a ternary operator is:
let a = b=="something" ? "Hello" : "world";
Which in turn should be encapsulated within a computed property or a method to be called everywhere in vuejs.
Showing a sample approach below
Vue.config.productionTip = false
Vue.config.devtools = false
new Vue({
el: "#app",
data: {
cars: {
id: 1,
honda: [
{car_id: 1, car_name: "Honda City"},
{car_id: 2, car_name: "Honda Civic"},
],
}
},
methods: {
isSameCar(carID){
return this.cars.id ===carID ? true : false;
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div v-for="row in cars.honda" :key="row.id">
<div v-if="isSameCar(row.car_id)">
<p>{{ row.car_name}}</p>
</div>
<div v-else>
<p>Not the same car, so show error here</p>
</div>
</div>
</div>
I have an array of environments and I want to use v-btn-toggle to create a button of each environment, the problem is that when I console.log(this.envSelected), it prints out the index, not the actual environment.. My goal is to make the button look like it's clicked.
I already tried :value="envText", value=${environments[envText]}
<template lang="pug">
v-btn-toggle(
v-model="envSelected"
v-on:change='updateSelectedEnv()'
)
v-btn(
v-for="(envText, index) in environments"
:key="index"
vulue={envText}
) {{ envText }}
</template>
<script>
data() {
return {
envSelected:"QA",
environments: ['DEV', 'IDEV', 'QA', 'STAGE', 'UAT', 'PROD', 'IDEV2', 'QA2'],
}
}
</script>
v-btn-toggle's value (and thus v-model) is the index of the selected item. To resolve the value, use the index (envSelected) as a lookup into environments[]:
console.log(this.environments[this.envSelected])
Sometimes it is needed to comment out some element attribute without having to remember it in order to restore it quickly after some tests.
Commenting out whole element is achievable with HTML commenting syntax
<div>
<!-- <h2>Hello</h2> -->
<span>hi</span>
</div>
However this won't work with a single attribute (causes rendering error)
<my-comp id="my_comp_1"
v-model="value"
<!-- :disabled="!isValid" -->
#click="handleClick">
</my-comp>
The best approach I could see and used before was to make a tag backup by copying whole element and settings v-if="false" for it (or comment out whole copied element) and continue to experiment with original one
I don't think you can put an HTML comment inside a component tag, for much the same reason you can't put comments inside an HTML element opening tag. It's not valid markup in either situation. I think the closest you could come would be to place the comment in the quotes:
:disabled="// !isValid"
Which would have the same effect as:
:disabled=""
Depending on whether your component can handle that value being missing, that might fit your needs.
Prefix the attribute value with data- or Wrap with data attribute.
<my-comp id="my_comp_1"
v-model="value"
data-:disabled="!isValid"
data-_click="handleClick"> # `#` could not be used
</my-comp>
or
<my-comp id="my_comp_1"
v-model="value"
data='
:disabled="!isValid"
#click="handleClick">
'>
</my-comp>
I'll with the attribute set to something like data-FIXME.
I got these solutions to work. I thought of solution 1.
Starting code:
<div
v-for="foo in foos"
:key="foo.id"
#click="foo.on = !foo.on /* JavaScript comment. */"
>
<label>
{{ foo.name }} {{foo.on}}
</label>
</div>
The Vue directive HTML attribute that needs to be disabled: #click="foo.on = !foo.on"
How the final div tag will run:
<div
v-for="foo in foos"
:key="foo.id"
>
Solutions 1 and 2 keep the disabled attribute inside its tag. I didn't find a good way to make a "raw string". To keep the attribute in the tag, the outer and inner quotes must be different.
sol. 1: I made a new v-bind attribute (:lang) to put the disabled attribute in.
:lang='en /* #click="foo.on = !foo.on" */'
Sol. 2: Pick a Vue directive. Put the attribute in.
v-for="foo in foos /* #click='foo.on = !foo.on' */"
Solutions 3 and 4 put the attribute outside the tag.
Sol. 3:
<div v-if="false">
#click="foo.on = !foo.on"
</div>
Sol. 4: <!-- #click="foo.on = !foo.on" -->
One way to remove/hide component attributes is to create a custom directive for it.
Let's say you create a directive called v-hide and put it in your component as:
<my-comp v-model="value" #click="handleClick" v-hide :disable='true'></my-comp>
And the output would be:
<my-comp v-model="value" #click="handleClick"></my-comp>
Here is a working example:
Vue.component ('my-component', {
template: `<p> A custom template </p>`
})
Vue.directive('hide', {
inserted: function (el) {
console.log('el before hide', el)
while(el.attributes.length > 0)
el.removeAttribute(el.attributes[0].name);
console.log('el after hide', el)
}
})
new Vue({
el: '#app',
data () {
return {
someValue: 'Hello'
}
}
})
<script src="https://unpkg.com/vue#2.5.3/dist/vue.js"></script>
<div id="app">
<my-component v-model='someValue' v-hide :disable='true'></my-component>
</div>
This question already has answers here:
How to limit iteration of elements in `v-for`
(7 answers)
Closed 2 years ago.
Simple problem, i have a members list where i iterate through it with a v-for.
How can i limit the results and only show the first 2 ?
members = [ {id : 1, name: 'Franck'}, {id : 2, name: 'Sophie'}, {id : 3, name: 'Bob'}]
<div v-for="member in members" :key="member.id">
<p>{{ name }}</p>
</div>
Just want to know if it's feasible from template ? Otherwise i know that i can use a computed properties that filtered it and i just have to loop through the results of my filtered array
You can use slice() in the template if you prefer to not use a computed property. I would though choose to have a computed property, if for nothing else, I like to handle all logic in script instead of template. But as said, you can use slice:
v-for="(member, index) in members.slice(0, 2)"
A forked fiddle that was provided by #Raffobaffo in a comment.
After some discussions here, lets divide the answer in two:
A fast, not super correct way but still referred inside vue docs
Use a v-if and set and index in the v-for.
<div v-for="(member, index) in members" :key="member.id" v-if="index < 3">
<p>{{ name }}</p>
</div>
else, the most correct way is to create a computed property returning just the elements you need:
<template>
<div v-for="member in justTwoElements" :key="member.id">
<p>{{ name }}</p>
</div>
<template>
....
computed: {
justTwoElements: function(){
return this.members.slice(0,2);
}
}
This latest solution is more indicated when your data entry is huge, the benefits of using it instead of the first solution are well explained here.
I hope this helps to give you the correct path to follow