I have a literal object like so:
export default { pizze:{ type:"pizze", typeOne:{ title: "Pizze Rosse", data:[ {id:1,type:"pizza",name:"Margherita",price:4,ingredients:["pomodoro","mozzarella"],quantity:0,inventory:100}, {id:2,type:"pizza",name:"Marinara",price:3.5,ingredients:["aglio","pomodoro"],quantity:0,inventory:100}, {id:3,type:"pizza",name:"Salsiccia e funghi",price:6,ingredients:["salsiccia","funghi","mozzarella","pomodoro"],quantity:0,inventory:100}, {id:4,type:"pizza",name:"Carciofi",price:4,ingredients:["mozzarella","carciofi","pomodoro"],quantity:0,inventory:100}, ] }, typeTwo:{ title:"Pizze Bianche", data:[ {id:5,type:"pizza",name:"Gorgonzola e noci",price:5.50,ingredients:["gorgonzola","noci","mozzarella"],quantity:0,inventory:100}, {id:6,type:"pizza",name:"Stracchino e rucola",price:4.50,ingredients:["stracchino","rucola","basilico"],quantity:0,inventory:100}, {id:7,type:"pizza",name:"Tartufo e salsiccia",price:8,ingredients:["tartufo","salsiccia","mozzarella"],quantity:0,inventory:100}, {id:8,type:"pizza",name:"Zucchine e Gamberi",price:5,ingredients:["zucchine","gamberi","pomodorini"],quantity:0,inventory:100}, ] } }, primiPiatti:{ type:"primiPiatti", typeOne:{ title: "Primi di carne", data:[ {id:12,type:"primiPiatti",name:"Lasagne alla Bolognese",price:9,quantity:0,inventory:100}, {id:13,type:"primiPiatti",name:"Spaghetti alla carbonara",price:14,quantity:0,inventory:100}, {id:14,type:"primiPiatti",name:"pennette all'amatriciana",price:10,quantity:0,inventory:100}, {id:15,type:"primiPiatti",name:"paccheri salsiccia e funghi",price:12,quantity:0,inventory:100}, ] }, typeTwo:{ title:"Primi di pesce", data:[ {id:16,type:"primiPiatti",name:"spaghetti allo scoglio",price:8.50,quantity:0,inventory:100}, {id:17,type:"primiPiatti",name:"zuppa di cozze",price:14.50,quantity:0,inventory:100}, {id:18,type:"primiPiatti",name:"pappardelle asparagi e gamberi",price:18,quantity:0,inventory:100}, {id:19,type:"primiPiatti",name:"lasagne al salmone",price:15,quantity:0,inventory:100}, ] }, ...
I was not aware that it was bad to use v-if inside a v-for loop and my first approach to display the code in the view was this:
<div class="menu__container"> <div v-if="menuVoice!=='type'"class="menu__container--box" v-for="menuVoice in Object.keys(menuData)" :key="menuVoice"> <div class="title">{{menuData[menuVoice].title}}</div> <item-record v-for="data in menuData[menuVoice].data" :key="data.id" :data="data"></item-record> </div> </div>
What I was trying to do is to avoid to display a div block related to the type
property of the object MenuVoice
. Since I need it for other purpose (retrieving the right object for instance). So I refactored the code in this way:
<div class="menu__container"> <div :style="[menuVoice === 'type' ? {display:'none'} : {display:'block'}]" class="menu__container--box" v-for="menuVoice in Object.keys(menuData)" :key="menuVoice"> <div class="title">{{menuData[menuVoice].title}}</div> <item-record v-for="data in menuData[menuVoice].data" :key="data.id" :data="data"></item-record> </div> </div>
I’m not sure it is ok because there is always an if statement there. The code works in both cases, but I’m wondering if there is a better way to do something like that.
I tried also moving the v-if down on a child div, like so:
<div class="menu__container"> <div"class="menu__container--box" v-for="menuVoice in Object.keys(menuData)" :key="menuVoice"> <div v-if="menuVoice !=='type'> <div class="title">{{menuData[menuVoice].title}}</div> <item-record v-for="data in menuData[menuVoice].data" :key="data.id" :data="data"></item-record> </div> </div> </div>
but it doesn’t works because I will still get with three divs with class '.menu__container--box'
.
Advertisement
Answer
The trick to avoid using v-if
with v-for
is to filter your dataset with a computed property.
Here I created a computed named menuList
which filters menuData
, returning a filtered shallow clone.
computed: { menuList() { return Object.keys(this.menuData) .filter(key => key !== 'type') .reduce((res, key) => (res[key] = this.menuData[key], res), {}); } }
So your template uses just the right list out of the box. v-for="(item, key) in menuList"
<div class="menu__container"> <div class="menu__container--box" v-for="(item, key) in menuList" :key="key"> <div class="title">{{item.title}}</div> <item-record v-for="data in item.data" :key="data.id" :data="data"></item-record> </div> </div>
Templates logic should be kept to a minimum. Anything that’s more than a reference to a value, or a simple assignment, should be moved into computed and methods.