I am trying to sort the following v-for
list alphabetically (i.e., A-Z). I am aware of the following computed property method used to sort v-for
lists alphabetically;
sortedList() {
const res = []
this.itemtwo.song.forEach(data => {
data.albums.forEach(album => {
album.songsinalbum.forEach((song) => {
res.push(song)
})
})
})
return res.sort((a, b) => {
if (a.song > b.song) return 1
else return -1
})
}
However, I need this to be done on a v-for
within a v-for
, since I need the data from the first v-for
for vue-router
use. If I use the above computed property, I cannot access the corresponding data from higher up in my data structure hierarchy;
Here is my data structure;
export const datatwo = [
{id: "1", artist: "Yaakov Shwekey", dateadded: "07/04/2022", artistroute: "/yaakovshwekey",
albums: [{album:'Shomati', songsinalbum: [
{ song : 'MaMaMa', keys: [
{key: "Am"},
{key: "Em"}
]},
{ song : 'Al Naharos Bavel', keys: [
{key: "Am"},
{key: "Em"}
]},
{ song : 'Levinyomin', keys: [
{key: "Am"},
{key: "Em"}
]}, ]},
{album : 'Simcha', songsinalbum: [
{ song : 'Kolot', keys: [
{key: "Am"},
{key: "Em"}
]},
{ song : 'Kalu Kol Hakotzim', keys: [
{key: "Am"},
{key: "Em"}
]},
{ song : 'Vehi Sheamdah', keys: [
{key: "Am"},
{key: "Em"}
]}, ]}, ]
},]
Here is my current v-for
;
<div v-for="album in datanew" :key="album.id">
<div v-for="(item, i) in album.albums" :key="i">
<div v-for="(itemtwo, i) in item.songsinalbum" :key="i">
<router-link v-if="albumname == item.album" :key="item.songsinalbum" :to="'/albumselected/' item.album '/artist/' album.artist">
<div >{{itemtwo.song}}</div>
</router-link>
</div>
</div>
</div>
Not sure if this is possible, would really appreciate help. Thank you.
CodePudding user response:
One could theoretically use
<div
v-for="(itemtwo, i) in item.songsinalbum.sort((a, b) =>
a.song > b.song ? 1 : -1
)"
:key="i"
>
<router-link
:key="item.songsinalbum"
:to="'/albumselected/' item.album '/artist/' album.artist"
>
<div >{{ itemtwo.song }}</div>
</router-link>
</div>
Directly in the template. Or extract the sort function into a method like so: Template:
<div
v-for="(itemtwo, i) in sortFunction(item.songsinalbum)"
:key="i"
>
Script (I would use typescript here to be safe):
const sortFunction = (arr) => {
return arr.sort((a, b) => {
if (a.song > b.song) return 1;
else return -1;
});
};
Lastly, and probably the best way would be to mimic the v-for you're looking for into the computed method, as you were attempting. Computed caches so it only changes when the input changes. I'm not exactly sure how often using a regular method solution would rerender. I came up with this solution using computed functions:
You need to create a couple wrapper components to drill down to get the correct items you want, namely the songs. I created a wrapper component for each step down. This may not be the best solution, but I can't think of a better solution In each situation you need the context of each array in order to sort correctly. I believe this solution may be the most performant, but not elegant. Rather than display each of the wrappers on stackoverflow, I submit this sandbox. https://codesandbox.io/s/quirky-cartwright-wfo1ot In each, you can see that I drill down into each own context. You can style how you see fit, but at the end of it, each albums' album will correctly sort the songs alphabetically. However, even this solution might be beat out by simply sorting in the template, I'd rather just clean up the sort function and use it in the template if it's too long of a process.