v-model an array using vue map state - vue.js

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.

Related

Vue js dynamic v-model for form which is rendered with v-for

I have 1 check box group and 9 radio button groups, and i need v-model for each group but i do not know how to put 9 differed data properties there.
My data looks like this:
data() {
return{
servicesId: [],
personId: '',
incomeId:'',
extraIncomeId: '',
pdvId: '',
paymentId: '',
clientId: '',
cashRegisterId: '',
eBankingId: '',
}
}
In template is like this:
<div v-for="data in formData" :key="data.id">
<h5>{{data.question_text}}</h5>
<div class="form-check" v-for="text_o in data.question_options" :key="text_o.id">
<input class="form-check-input"
:type="data.question_type.type"
:value="text_o.id" :id="text_o.id"
v-model="[HERE GOES "9 "data props"]">
<label class="form-check-label" :key="text_o.id" :for="text_o.id"> {{text_o.option_text}
</label>
</div>
</div>
Here is part of (because it is long, i can post everything from it if you want)formData array:
{
"id": 1,
"question_type_id": 1,
"title": "new ent",
"question_text": "Planirate da se bavite:",
"created_at": "2021-10-12T13:42:17.000000Z",
"updated_at": "2021-10-12T13:42:17.000000Z",
"question_options": [
{
"id": 1,
"question_id": 1,
"option_text": "Uslugama",
"price": 40,
"created_at": "2021-10-12T13:44:40.000000Z",
"updated_at": "2021-10-12T13:44:40.000000Z"
},
{
"id": 2,
"question_id": 1,
"option_text": "Trgovinom",
"price": 60,
"created_at": "2021-10-12T13:46:53.000000Z",
"updated_at": "2021-10-12T13:46:53.000000Z"
},
{
"id": 3,
"question_id": 1,
"option_text": "Proizvodnjom",
"price": 80,
"created_at": "2021-10-12T13:47:22.000000Z",
"updated_at": "2021-10-12T13:47:22.000000Z"
}
],
"question_type": {
"id": 1,
"type": "checkbox",
"created_at": "2021-10-12T13:40:17.000000Z",
"updated_at": "2021-10-12T13:40:17.000000Z"
}
}
Thanks.
If you want your values to be a part of data, you can create an object that will contain your form data, like this:
data() {
return {
formValue: {
servicesId: [],
personId: "",
incomeId: "",
extraIncomeId: "",
pdvId: "",
paymentId: "",
clientId: "",
cashRegisterId: "",
eBankingId: "",
},
}
}
after adding that object, just add a name to your form data object like this:
formData: [
{
id: 1,
question_type_id: 1,
title: "new ent",
question_text: "Planirate da se bavite:",
created_at: "2021-10-12T13:42:17.000000Z",
updated_at: "2021-10-12T13:42:17.000000Z",
name: "servicesId", <-- modified part
question_options: [...]
}
]
and now you can use that name to dynamically access your formValue object which is a part of data:
<div v-for="data in formData" :key="data.id">
<h5>{{ data.question_text }}</h5>
<div
class="form-check"
v-for="text_o in data.question_options"
:key="text_o.id"
>
<input
class="form-check-input"
:type="data.question_type.type"
:value="text_o.id"
:id="text_o.id"
v-model="formValue[data.name]" <-- modified part
/>
<label class="form-check-label" :key="text_o.id" :for="text_o.id">
{{ text_o.option_text }}
</label>
</div>
</div>
Hope this approach helps you, you will have your values in data, they won't be there as separate properties, but they will be a part of a wrapper object named formValue

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>

Hierarchical dynamic loaded components with children distributed along dynamic named slots

We're creating an app where the interface is dynamically mounted based on a JSON returned by an API. It looks like this:
{
"path": "Container",
"children": [
{
"slot": "default",
"path": "Banner",
"props": {
"items": [ "image1.jpg", "image2.jpg", "image3.jpg" ]
}
},
{
"slot": "header",
"path": "Flex",
"props": {
"flow": "row"
},
"children": [
{
"slot": "default",
"path": "Icon",
"props": {
"name": "mdi-forum"
}
},
{
"slot": "default",
"text": "Example of title"
}
]
}
]
}
So I created a dynamic ComponentLoader with a computed doing a dynamic import, then I also inject more dynamic component loaders recursively as needed, using a v-for through the children list:
<template>
<component v-if="component" :is="component" v-bind="$attrs">
<template v-for="(child, i) of children">
<ComponentLoader
v-if="child.path"
v-bind="child.props"
:key="`${child.path}-${i}`"
:path="child.path"
:children="child.children"
/>
<template v-else>{{ child.text || '' }}</template>
</template>
</component>
</template>
<script>
import Error from '~/components/ComponentLoaderError.vue'
export default {
name: 'ComponentLoader',
components: { Error },
props: {
path: { type: String, required: true },
children: { type: Array, default: () => [] },
},
computed: {
component() {
if (!this.path) return null
return () => import(`~/components/${this.path}`).then((m) => m || m.default).catch(() => Error)
},
},
}
</script>
It's almost working, but all children gone injected to the default slot of each loaded component, which makes all sense since I'm not informing the desired slot during the loop through the children.
Inspired in this answer, I added a v-slot bind on the <template> with the v-for, using Dynamic Slot Names to inject on the right slot based in the child.slot property already received from the JSON:
<template v-for="(child, i) of children" #[child.slot]>
For nodes with only one child to be distributed on each slot, it's working as expected. But when I have more children to be distributed in the same slot (like the last children array in that JSON), only the last child is injected, overriding others before.
So, how to inject many children to dynamic named slots inside a loop?

return unique values from array in vuejs

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))]
}

Vue,js Display data from json

How to fetch the product name from the below result. I tried
<div class="card-divider">
{{ post.basic_info.prod_name }}
</div>
But it is not working
Many thanks
{
"name":"Test ProductCategory",
"updated":"2018-07-16 15:00:03",
"12":{
"basic_info":{
"prod_name":"Product name 1",
"status":"Active",
"image":""
},
},
"13":{
"basic_info":{
"prod_name":"Product name 2",
"status":"Active",
"image":""
},
}
}
Try this:
{{ post["12"].basic_info.prod_name }}
Your JSON have two same keys, so you will receive Product name 2 only, because, latter will replace previous key "12"
My suggestion, modify data to receive something like:
{
"name": "Test ProductCategory",
"updated": "2018-07-16 15:00:03",
"data": [
{
"basic_info": {
"prod_name": "Product name 1",
"status": "Active",
"image": ""
},
}, {
"basic_info": {
"prod_name": "Product name 2",
"status": "Active",
"image": ""
},
}
]
}
you can loop over data and get basic_info.prod_name then your html will be like:
<div class="card-divider" v-for="obj in post.data">
{{ obj.basic_info.prod_name }}
</div>