return unique values from array in vuejs - arraylist

I am using Vuejs and Vuex to return an array of item objects. The items can be submitted to the database with the same name multiple times. I need to create a list of unique item names in an array in Vuejs.
from map getters below all Items returns an array of objects that looks like
[
{"name": "item one", "number": "001", "size": "4000kb"}
{"name": "item two", "number": "002", "size": "5000kb"}
{"name": "item three", "number": "003", "size": "6000kb"}
]
methods: {
...mapActions(["fetchItems"])
},
computed: {
...mapGetters(["allItems"]),
itemNames: function() {
return [...new Set(this.allItems.name)]
}
},
created() {
this.fetchItems(),
this.itemNames()
},
In computed properties itemNames if I take off the .name [..new Set(this.allItems)]
the array returns the complete objects - how can I just pull the name out to a list?
The v-for does not return the array
<v-list-item v-for="(itemName, index ) in itemNames" :key="index">
<v-list-item-content> {{ itemName }}</v-list-item-content>
</v-list-item>
Thanks for any help.

I solved this one in computed properties by mapping the items to a new array like so
itemNames: function() {
return [...new Set(this.allItems.map(x => x.item.Name))]
}

Related

two differents arrays inside one component VueJs

Hello i wan't to loop two differents arrays inside one component like this
operationMep= [
{
"id": 15525205,
"type": "mise_en_prep",
"orderOperationSkus": [
{
"id": 24339529,
"orderSku": {
"id": 11747818,
"referenceMep": "MB0153",
"tax": 20,
"size": "M"
}
}
}
]
operationInvoice= [
{
"id": 16525205,
"type": "invoice",
"orderOperationSkus": [
{
"id": 24339529,
"orderSku": {
"id": 11747818,
"referenceInvoice": "MB0153"
}
}
}
]
<div v-for="itemMep in operationMep">
<my-template
v-for="itemVoice in operationInvoice"
:size="itemMep.size"
:invoice="itemVoice.referenceInvoice">
</my-template>
</div>
it's possible to do that because i have 2 loop inside this component and i want to add a condition if operation invoice is null just loop operationMep. thank you
If I understand correctly, you want to avoid rendering the nested <my-template> if operationInvoice is null.
You could wrap the nested v-for loop with v-if="operationInvoice":
<div v-for="itemMep in operationMep">
<template v-if="operationInvoice">
<my-template
v-for="itemVoice in operationInvoice"
:size="itemMep.size"
:invoice="itemVoice.referenceInvoice">
</my-template>
</template>
<!-- other markup here -->
</div>

Adding and reading json data stored in vuex

I have a vuex store and i am adding some josn data and this is the format.
[
{
"id":1,
"firstname": "toto",
"lastname": "titi"
},
{ "id":2,
"firstname": "one",
"lastname": "two"
}
]
I am adding the data on an on click action and this is the action method
addLink: function() {
var dt = '[{"id":1,"firstname":"xx","lastname": "yy"},{"id":2,"firstname": "one","lastname": "two"}]';
this.ADD_LINK(dt)
this.newLink = '';
},
The data is getting added to the store and i can access it like this
computed: {
users(){
return this.countLinks;
}
}
I can display the data this way {{users}} and this is getting displayed. This is because i clicked twice and added the json twice.
[ "[{\"id\":1,\"firstname\":\"xx\",\"lastname\": \"yy\"},{\"id\":2,\"firstname\": \"one\",\"lastname\": \"two\"}]", "[{\"id\":1,\"firstname\":\"xx\",\"lastname\": \"yy\"},{\"id\":2,\"firstname\": \"one\",\"lastname\": \"two\"}]" ]
However, when i try to use v-for
<ul id="users">
<li v-for="user in users" :key="user.id">
{{ users.firstname}}
</li>
</ul>
i cannot display any data and i have no error. How can i display the data saved in vuex?.
You can create a computed property that returns the objects in one list parsed as JSON:
new Vue({
el:"#app",
data: () => ({
users: [ "[{\"id\":1,\"firstname\":\"xx\",\"lastname\": \"yy\"},{\"id\":2,\"firstname\": \"one\",\"lastname\": \"two\"}]", "[{\"id\":1,\"firstname\":\"xx\",\"lastname\": \"yy\"},{\"id\":2,\"firstname\": \"one\",\"lastname\": \"two\"}]" ]
}),
computed: {
usersList: function() {
return this.users.flatMap(userList => JSON.parse(userList));
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<ul id="users">
<li v-for="(user, index) in usersList" :key="index">
{{ user.firstname}}
</li>
</ul>
</div>
Note: Since ids are not unique in your example, you can use an index in v-for as the key. Also, to show the first name, you need to use the user object.
Another solution: Parse dt in the store and use Array#concat to add the elements as objects to the initial list:
let countLinks = [
{ "id":1, "firstname": "toto", "lastname": "titi" },
{ "id":2, "firstname": "one", "lastname": "two" }
];
function ADD_LINK(dt) {
countLinks = countLinks.concat(JSON.parse(dt));
}
const dt = '[{"id":1,"firstname":"xx","lastname": "yy"},{"id":2,"firstname": "one","lastname": "two"}]';
ADD_LINK(dt);
console.log(countLinks);
you have to store the data as is, rather than converting into string
addLink: function() {
var dt = [
{
"id":1,
"firstname": "xx",
"lastname": "yy"
},
{
"id":2,
"firstname": "one",
"lastname": "two"
}
];
// remove the single quote from the above array
this.ADD_LINK(dt)
this.newLink = '';
},
In case you are getting the var dt from external source then you should consider converting into valid js json format using this:
addLink: function() {
var dt = '[{"id":1,"firstname":"xx","lastname": "yy"},{"id":2,"firstname": "one","lastname": "two"}]';
// parse it to json format
var parsedDt = JSON.parse(dt);
// add the `parsedDt`
this.ADD_LINK(parsedDt)
this.newLink = '';
},

v-model an array using vue map state

I am working on creating custom fields in my application. I am allowing clients to define custom fields for various objects (ie. Initiative) and displaying them when updating or creating that object.
In my state I define the object being modified or added:
(initiatives.js)
addEditInitiative: {
name: '',
description: '',
product_name: '',
product_id: '',
quarter: '',
year: '',
custom_fields: []
},
the custom_fields array is filled with the custom fields defined for initiatives. For example, in the json response from the database, the custom fields array will include something like this...
"payload": [
{
"id": "5dc840c3d27a6e47b9baec33",
"cid": "5d8502a2a284b46f3621f389",
"name": "2",
"description": "",
"product_name": "Maps",
"product_id": "5d86509ee24692444d84b155",
"quarter": "Q2",
"year": "2019",
"custom_fields": [
{
"id": "5db8ec9fee8040e9b6dfad87",
"cid": "5d8502a2a284b46f3621f389",
"name": "Test",
"type": "text",
"form": "initiative",
"value": ""
},
{
"id": "5dba0bcedf9cbf185683ecca",
"cid": "5d8502a2a284b46f3621f389",
"name": "test2",
"type": "text",
"form": "initiative",
"value": ""
}
]
}
]
}
I am trying to edit the value for each of those custom fields through vuex map fields, or if that doesnt work, some other way that isn't causing the error I am getting now. I am not mutating the state. I am directly using v-model on the state.
(Vue Component)
<v-item v-for="(field, index) in initiativeFields"
v-bind:index="index"
v-bind:key="field.id">
<v-text-field v-if="field.type = 'text'"
:label="field.name"
type="text"
v-model="addEditInitiative.custom_fields[index].value">
</v-text-field>
</v-item>
I am not sure how to replace v-model="addEditInitiative.custom_fields[index].value" with something that will mutate the state. I have been using https://github.com/maoberlehner/vuex-map-fields for this for simple fields. For Example,
(Vue Component)
...mapFields('initiative', [
'addEditInitiative.name',
'addEditInitiative.description',
'addEditInitiative.product_name',
'addEditInitiative.product_id',
'addEditInitiative.quarter',
'addEditInitiative.year',
]),
Try using multiple row fields.
https://github.com/maoberlehner/vuex-map-fields#multi-row-fields
v-model="test" is actually just a shorthand for :value="test" #input="test = arguments[0]" (or :value="test" #input="test = $event.target.value" for native components like input).
You can use this to your advantage to refactor your code a bit to something like this:
<v-item v-for="(field, index) in initiativeFields"
v-bind:index="index"
v-bind:key="field.id">
<v-text-field v-if="field.type = 'text'"
:label="field.name"
type="text"
:value="addEditInitiative.custom_fields[index].value"
#input="updateValue($event, index)">
</v-text-field>
</v-item>
And in your updateValue method you could update the store with an action/mutation.

VueJS - Auto create a A-Z letters list from the data

Is it possible to create a A-Z letters list (like this) from the data from a API and Vue to be able to determine if a property in a data contains a name that starts with what letter. If the data doesn't contain a specific letter name then remove/disable the href attribute from the letter anchor tag.
In the linked example, letters K, X and Z are missing coz they don't have the data
JSON
[
{
"id": 77,
"link": "http://my-site/cosmoquotes/authors/anonymous/",
"name": "Anonymous",
"slug": "anonymous"
},
{
"id": 72,
"link": "http://my-site/authors/ferdinand-marcos/",
"name": "Ferdinand Marcos",
"slug": "ferdinand-marcos"
},
{
"id": 75,
"link": "http://my-site/authors/john-f-kennedy/",
"name": "John F. Kennedy",
"slug": "john-f-kennedy"
},
{
"id": 67,
"link": "http://my-site/authors/john-maxwell/",
"name": "John Maxwell",
"slug": "john-maxwell"
}
]
Component
export default {
data() {
return {
authorsRequest: {
type: 'authors',
params: {
per_page: 100
}
},
}
},
computed: {
authors () {
return this.$store.getters.requestedItems(this.authorsRequest)
},
},
methods: {
getAuthors() {
return this.$store.dispatch('getItems', this.authorsRequest)
},
},
created() {
this.getAuthors()
}
}
So as per the returned data, only the letters 'A', 'F' and 'J' should be clickable/displayed.
I managed to do it like this,
unfortunatly it needs the authors array and the conditionnal function to be outside of the Vue component because I couldn't find how to pass argument to computed values
But since I'm new to vue (didn't even finish reading the introduction) I'm sure there has to be a better solution
EDIT: found the way to have the function in the component with methods, I could then move the data in the component too
let a = new Vue({
el: "#selector",
data: {
authors: [{"id": 77,"link": "http://my-site/cosmoquotes/authors/anonymous/","name": "Anonymous","slug": "anonymous"},{"id": 72,"link": "http://my-site/authors/ferdinand-marcos/","name": "Ferdinand Marcos","slug": "ferdinand-marcos"},{"id": 75,"link": "http://my-site/authors/john-f-kennedy/","name": "John F. Kennedy","slug": "john-f-kennedy"},{"id": 67,"link": "http://my-site/authors/john-maxwell/","name": "John Maxwell","slug": "john-maxwell"}]
},
computed: {
// there have to be a way to get this array without doing it like this but I don't know it ^^
letters() {
let letters = []
for(let i = "A".charCodeAt(0); i <= "Z".charCodeAt(0); i++) {letters.push(String.fromCharCode([i]))}
return letters
}
},
methods: {
// you may add a toUpperCase()/toLowerCase() if you're not sure of the capitalisation of you datas
isALink(letter) {
return this.authors.some(aut => aut.name.startsWith(letter))
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="selector">
<template v-for="letter in letters">
<a v-if="isALink(letter)" :href="letter">{{ letter }}</a>
<a v-else>{{ letter }}</a>
</template>
</div>
you can set the unique name as the id of dom. when you click to letter X, just get the first name start with X , and use getElementById to match the dom and scroll to the dom.

Need to Access Item Index in Bootstrap Vue Table when doing a loop

I have a Bootstrap-Vue parent b-table running for loop on bids. Each bid also has items.
Initially it shows each bid on each row.
When that row is clicked it shows a child table that has every bid items.
So a child table inside <template slot="hidden-details> tries to
loop through bid.items to display those items for each bid.
Since I am unable to get earlier bid loop indexes I cannot run this
nested loop unless I manually set it like: :items="bid[0].items
Script
new Vue({
el: '#app',
data:{
bids: [{
id: "Bid Number One",
item: [{
item_number: 11,
item_text: "Item 1 Comment 1",
code: "1",
},
{
item_number: 22,
item_text: "Item 1 Comment 2",
code: "2",
}
]
},
{
id: "Bid Number Two",
item: [{
item_Number: 33,
item_text: "Item 2 Comment 3",
code: "3",
},
{
item_Number: 44,
item_text: "Item 2 Comment 4",
code: "4",
}
]
}
],
bidHeaderFields:[
{key:"id", label: "Header"},
],
bidIndex: 0,
},
methods: {
showBidItems(bids, index) { // Here I receive row index from #row-clicked
this.bids[index]._showDetails = !this.bids[index]._showDetails;
this.bidIndex = index; // I change the value of bidIndex to row-clicked index
},
}
})
This JSFiddle has the table code.
The complete row's item data is available in the scoped details slot. If the bid details are a sub-array of the item, you can just pass that array to the child b-table:
<b-table :items="bids" :fields="..." ... >
<template slot="row-details" slot-scope="{ item }"> // item === 'bid' row data
// item.item === the rows 'item' array
<b-table :items="item.item" :fields="..."></b-table>
</template>
</b-table>