in Vue I am using vuex state for generating elements, it works when it runs for the first time but when I update Vuex state which I am using that for generating elements in v-for it doesnt generate more than one element!
this is my code:
<div v-for="(group, index) in $store.state.eventManagement.groupCount" v-bind:key="index" class="match">
<div class="groupTitle">{{group}}</div>
<div v-for="(team, teamIndex) in groupTeamCount" v-bind:key="teamIndex" class="teamName">
(#{{ (groupTeamCount * index) + teamIndex+1 }}) <span v-if="seeds[(groupTeamCount * index) + teamIndex] !== ''">{{seeds[(groupTeamCount * index) + teamIndex].username}}</span>
<div class="addSeed" v-on:click="changeSeedNumber((groupTeamCount * index) + teamIndex)">+</div>
</div>
</div>
What is wrong? What do you think?
$store.state.eventManagement.groupCount in the outer v-for reads like it is the count in a collection. If correct, then this is a single value.
Do you need to loop over $store.state.eventManagement (or whatever the correct name for the collection in the store is)?
Solved:
The problem was because of input that is changing the value of groupCount state. Its a number type input, so I thought that it passes integer but it doesn't, it passes a string, thus v-for generates just one element.
I used parseInt to solve this problem
Related
I have a grid created for videos. It makes a call to an api to return back videos. I have a state isFetching that is set to true when the page loads and tries to retrieve the list from the API. After the videos are retrieved, isFetching is set to false and the data is stored as videoList.
I am using a grid with a placeholder component for each item if the API takes a while to fetch. The VideoGrid expects a items because inside of it, it iterates through it. I want to have 16 grid items but I don't want to have to do :items=[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]. I tried :items="[new Array(16).keys()] but that seems to create a nested array. Is there a way to accomplish this without having to write it out like that?
Main.vue
<VideoGrid v-if="isFetching && !videoList" :items="[1,2,3,4,5,6]">
<template>
<ItemPlaceholder />
</template>
</VideoGrid>
<VideoGrid v-else :items="videoList">
<template v-slot="slotProps>
<VideoComponent :title="slotProps.title" />
</template>
</VideoGrid>
You should just be able to remove the brackets from your items declaration like so:
<VideoGrid v-if="isFetching && !videoList" :items="Array(16).fill(1)">
<template>
<ItemPlaceholder />
</template>
</VideoGrid>
<VideoGrid v-else :items="videoList">
<template v-slot="slotProps>
<VideoComponent :title="slotProps.title" />
</template>
</VideoGrid>
Array(16).fill(1) will create an array with 16 number 1's. If you want to have incrementing values in your array you can use Array.from({length: 16}, (v, i) => i) or see How to initialize an array's length in JavaScript?.
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 made a list that each item could be removable and my code looked like this:
Template
<template v-for="(timeFrame, index) in form.timeFrames">
<el-form-item >
<el-button #click="removeTimeFrame(index)">
<i class="el-icon-remove"></i>
</el-button>
</el-form-item>
</template>
Js:
removeTimeFrame(index = 0) {
this.$set(this.form, 'timeFrames', this.form.timeFrames.filter((_, i) => index !== i));
}
Somehow the list doesn't rerender until I add an new item to the list. Does anybody know what's wrong with my code?
templates in Vue need a top level element wrapping their content, so you shouldn't assign the v-for directly to the template tag, but instead create a div inside the template tag and either add v-for to the el-form-item component or wrap it in another div-tag.
Additionally, every element in a v-for loop should contain a key. If you do not plan to reorder or delete elements from the loop, the index-value of each element is fine for this. Seeing your example I suspect that a unique identifier, such as a randomly generated unique id, might work better for your use case.
I am creating components in a loop in my vue app. However, I need those components to have an id value like "board-1" etc. using the index of the loop. (Just like I did it with v-bind:key="component-${block._uid}".)
How can I achieve this?
<div
class = "col-4"
v-for="(block, index) in layouts"
v-bind:key="`component-${block._uid}`"
>
<Board
id="`board-${block._uid}`"
class="droparea"
#dropped-component="$emit('dropped-component', $event, index)"
:acceptsDrop=true
draggable="true"
>
Layout {{index + 1}}
</Board>
</div>
You need to bind the value for JS code to get executed, otherwise you are attributing a string, not JS code.
:id="`board-${block._uid}`"
You can also use v-bind, actually : is just a shorthand
v-bind:id="`board-${block._uid}`"
Hopefully this replica isn't too alien to the Vue.js world.
I have a list of profiles. Inside these profile instances there is an array that represents their socialMedia accounts. However, due to legacy database reasons, let's say that for some of the artists the socialMedia account comes through into the Vue.js application as null.
So, imagine I have something like:
<a
v-if="artwork.artist.socialMedia"
v-for="(social, index) in artwork.artist.socialMedia.filter(social => social.url.length > 0)"
v-bind:key="index"
v-bind:href="parseSocialMediaURL(social.url, social.platform)"
>
</a>
However, the preceding v-if statement, doesn't seem to be skipping past those values for artwork.artist.socialMedia when this is null. For most, this value is just a plain old Array, so how do I go about handling both cases?
Your v-if should work, but if your condition is more complex than a simple if you can consider using a computed method for this. It could also handle the filter in it. You can also use isArray for a better check.
computed: {
artistSocialMedia () {
if( !Array.isArray(this.artwork.artist.socialMedia) ) {
return []
}
return this.artwork.artist.socialMedia.filter(social => social.url.length > 0)
}
}
and then use the computed method
<a
v-for="(social, index) in artistSocialMedia"
v-bind:key="index"
v-bind:href="parseSocialMediaURL(social.url, social.platform)"
>
</a>
As part of the vue documentation, you should not be using them together https://v2.vuejs.org/v2/guide/conditional.html#v-if-with-v-for.
To render conditionally without adding addition elements wrap it in a template
<template v-if="artwork.artist.socialMedia">
<a
v-for="(social, index) in artwork.artist.socialMedia.filter(social => social.url.length > 0)"
v-bind:key="index"
v-bind:href="parseSocialMediaURL(social.url, social.platform)"
>
</a>
</template>