How to create input fields dynamicaly in vuejs (vuetify) - vue.js

I am new to vuejs. I'd wanted to create input fields dynamically by the click of a button using vuetify. My attempt to implement is shown below and commented line wise to show what I was trying to achieve.
I there a way this can be done? Any suggestion is welcome.
<template>
<v-container>
<v-row>
<v-col cols="12" sm="12">
<v-btn color="success">Add Input</v-btn>
</v-col>
<v-col cols="12" sm="12" ref="mount">
<!-- inputs fields are appended here -->
</v-col>
</v-row>
</v-container>
</template>
<script>
export default {
data() {
return {
items: {},
};
},
methods: {
addField() {
// create element
let textField = document.createElement("v-text-field");
// add it to the data property
this.$data.items["firstName"] = "";
// add the vmodel attribute to the element
textField.setAttribute("v-model", "firstName");
// finally mount it in the templates
this.$refs.mount.appendChild(textField);
},
},
};
</script>

You should use an array of objects like so basic: [{ name: "", time: "" }]. Then you can use a loop to bind inputfields to those properties and add items to that array. You can see a working example here
<div v-for="item in basic" :key="item.id">
<button #click="addRow">Add row</button>
<input type="text" v-model="item.name" />
<input type="text" v-model="item.time" />
{{ item.name }} - {{ item.time }}
</div>
data () {
return {
id: 1,
basic: [{ name: "", time: "" }]
};
}
addRow () {
console.log("added");
this.id += 1;
this.basic.push({
name: "",
time: ""
});
}

Related

Dynamic fields repeat the same data in text-field v-model

How could I improve this code, I'm creating dynamic fields that are created depending on the user input, but I have a problem when inserting the information in one field it replicates in the other fields, I want every new field fills independently and send that data to points.compartments_detail
I know the problem is in v-model="points.compartments_detail", because when I delete it lets me write every field independently.
points.compartments_detail is set as an empty array
</v-row>
<v-text-field
v-model="points.number_compartments"
label="COMPARTIMIENTOS"
outlined
:disabled="actionQuotation === 'VER'"
type="number"
#change="logSelect(), addCompartmentCapacity(points.number_compartments),deleteComparmentCapacity(points.number_compartments) "
class="mx-3"
dense
/>
</v-row>
<v-row v-for="(compartment, index) in comparment_capacity" :key="index" class="mx-0">
<v-row v-if="points.number_compartments > 1">
<v-text-field
v-model="points.compartments_detail"
label="ESPECIFICAR CAPACIDAD DE COMPARTIMIENTO"
outlined
:disabled="actionQuotation === 'VER'"
class="mx-3"
dense
hint="Ejemplo: 1000 galones / Click en boton agregar para aƱadir mas campos"
/>
</v-row>
</v-row>
This is my data :
comparment_capacity : [{comparment : ""}],
The functions to create and delete the fields
addCompartmentCapacity (item) {
while(this.comparment_capacity.length < item){
this.comparment_capacity.push({
compartment : ""
})
}
},
deleteComparmentCapacity (item){
while(this.comparment_capacity.length > item ) {
if(this.comparment_capacity.length > 1){
this.comparment_capacity.pop()
}
}
}
Any ideas will be of great help.
for test this sample here >>> Sample.
Is this what you looking for?
Paragraph is for default value you can delete it if not needed.
Entered.num is the important for push more input.
<template>
<div class="hello">
<div v-for="(x,index) in Paragraph" :key="x">
<input
placeholder="Type here"
v-model="x.Text1"
#keyup="GetText($event,index)"
/>
</div>
<input
placeholder="Add Paragraph"
v-model="Entered.num"
#keyup="Get(Entered.num)"
/>
</div>
</template>
data() {
return {
Entered: [{ num: 0 }],
Paragraph: [
{
Text1: "Hello World1",
},
{
Text1: "Hello World2",
},
],
};
},
methods: {
Get(num) {
while (num--) {
this.Paragraph.push({ Text1: "" });
}
},
GetText(event) {
console.log(event.target.value , 'Index: ',index);
},
},

How to use v-form inside a v-for and perform validation for a specific form?

I have an array of objects which I should loop through and show a form for each object's properties. The Save button is out of the for loop. In the attached sandbox, the 2nd object doesn't contain lastname. So, how do I perform validation on click of Save button only for the 2nd form? And is there any way to validate all the forms at once? Please refer to the sandbox for a better understanding.
https://codesandbox.io/s/jolly-kepler-m260fh?file=/src/components/Playground.vue
Check this codesandbox I made: https://codesandbox.io/s/stack-72356987-form-validation-example-4yv87x?file=/src/components/Playground.vue
You can validate all v-text-field at once if you move the v-form outside the for loop. All you need to do is give the form a ref value and a v-model to make use of the built in vuetify validation methods.
<template>
<v-container class="pl-10">
<v-form ref="formNames" v-model="validForm" lazy-validation>
<v-row v-for="(name, index) in names" :key="index">
<v-col cols="12">
<v-text-field
v-model="name.firstName"
outlined
dense
solo
:rules="rulesRequired"
/>
</v-col>
...
</v-row>
</v-form>
<v-btn type="submit" #click="submitForm" :disabled="!validForm">
Submit
</v-btn>
</v-container>
</template>
Then in the submit button all you need to do is call the validate() method of the form through the $refs object. You can also disable the submit button if any of the elements in the form don't pass the validation rules using the v-model of the form to disable the submit button.
<script>
export default {
name: "playground",
data: () => ({
validForm: true,
names: [
{
firstName: "john",
lastName: "doe",
age: 40,
},
{
firstName: "jack",
lastName: "",
age: 30,
},
],
rulesRequired: [(v) => !!v || "Required"],
}),
methods: {
submitForm() {
if (this.$refs.formNames.validate()) {
// Form pass validation
}
},
},
};
</script>
As submit button is outside of the forms. We can perform that validation on submit event by iterating the names array and check if any value is empty and then assign a valid flag value (true/false) against each object.
Demo :
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: () => ({
names: [
{
firstName: "john",
lastName: "doe",
age: 40,
valid: false
},
{
firstName: "jack",
lastName: "",
age: 30,
valid: false
},
],
requiredRule: [v => !!v || 'Value is required']
}),
methods: {
submitForm() {
this.names.forEach((obj, index) => {
if (!obj.lastName) {
obj.valid = false;
console.log(
`${index + 1}nd form is not valid as LastName is not available`
);
} else {
obj.valid = true;
}
});
// Now you can filter out the valid form objects based on the `valid=true`
}
}
})
<script src="https://unpkg.com/vue#2.x/dist/vue.js"></script>
<script src="https://unpkg.com/vuetify#2.6.6/dist/vuetify.min.js"></script>
<link rel="stylesheet" href="https://unpkg.com/vuetify#2.6.6/dist/vuetify.min.css"/>
<div id="app">
<v-app id="inspire">
<v-container class="pl-10">
<v-row v-for="(name, index) in names" :key="index">
<v-form v-model="name.valid">
<v-col cols="12">
<v-text-field v-model="name.firstName" outlined dense solo required :rules="requiredRule" />
</v-col>
<v-col cols="12">
<v-text-field v-model="name.lastName" outlined dense solo required :rules="requiredRule" />
</v-col>
<v-col cols="12">
<v-text-field v-model="name.age" outlined dense solo required :rules="requiredRule" />
</v-col>
</v-form>
</v-row>
<v-btn type="submit" #click="submitForm"> Submit </v-btn>
</v-container>
</v-app>
</div>

Vue - Dynamically add specific v-model name with v-for, not just an index

I want to loop over the colorMenus array and bind my v-model to the already defined data elements headerColor and checkboxColor
I have this simplified code:
<v-card
v-for="(colorMenu, index) in colorMenus"
:key="index"
>
<v-row>
<v-col>
<p class="font-weight-bold text-subtitle-2 mt-4">{{ colorMenu.title }}</p>
</v-col>
<v-col cols="8">
<v-text-field
v-model="myModels.color[index]"
v-mask="mask"
hide-details
class=""
solo
></text-field>
</v-col>
</v-row>
</v-card>
And my data looks like this:
export default {
data() {
return {
headerColor: '#1976D2FF',
checkboxColor: '#1976D2FF',
myModels: {
color: ['headerColor', 'checkboxColor']
},
colorMenus: [
{
title: 'HEADER:',
},
{
title: 'CHECKBOX:',
}
]
}
},
What's weird is I can get this, the model names, but they have # in front?
I think that's because you've defined strings inside of that color array. You should refer to these items like this:
myModels: {
color: [this.headerColor, this.checkboxColor]
},
I hope this helps. in a v-for for some reason I cannot access the name from an array, but I can if I access the data by key from ANOTHER object. No idea why, but it worked! Here is the fixed code:
<v-card
v-for="(colorMenu, index) in colorMenus"
:key="index"
>
<v-row>
<v-col>
<p class="font-weight-bold text-subtitle-2 mt-4">{{ colorMenu.title }}</p>
</v-col>
<v-col cols="8">
<v-text-field
v-model="myModels[colorMenu.type]"
v-mask="mask"
hide-details
class=""
solo
></text-field>
</v-col>
</v-row>
</v-card>
And then my data:
export default {
data() {
return {
headerColor: '#1976D2FF',
checkboxColor: '#1976D2FF',
myModels: {
headerColor: '#1976D2FF',
checkboxColor: '#1976D2FF',
},
colorMenus: [
{
title: 'HEADER:',
type: 'headerColor'
},
{
title: 'CHECKBOX:',
type: 'checkboxColor'
}
]
}
},
How about using computed property? This works, you can try it out here
https://codesandbox.io/s/optimistic-herschel-7q4u54?file=/src/App.vue
data() {
return {
headerColor: "#1976D2FF",
checkboxColor: "#1976D2FF",
};
},
computed: {
myModels() {
return [this.headerColor, this.checkboxColor];
},
},

How do I edit vue data passed as a property?

I have a Vuex data store with an array of contacts. I display that array in ContactList.vue, where I have an "Edit" button that navigates to CreateEditContact.vue (I am passing in the contact as a prop named contactProp). All of this works, but as we all know, you can't edit a prop directly. I tried checking to see if the prop is populated when the template is created, and copying it to contact in data but it isn't working. What's the right way to do this?
ContactList.vue
<template>
<!-- ...redacted for brevity -->
<!-- I'm reusing the same .vue for creating new and editing contacts -->
<!-- edit contact button -->
<v-btn text
:to="{name: 'createEditContact',
params: {
action: 'edit',
id: contact.id,
contactProp: contact
}}">Edit</v-btn>
<!-- new contact button -->
<v-btn
:to="{ name: 'createEditContact', params: { action: 'create' } }"
outlined
rounded>New</v-btn>
</template>
CreateEditContact.vue
<template>
<div>
<h3>Create or Edit a Contact</h3>
<v-form #submit.prevent="saveContact">
<v-container>
<v-row>
<v-col>
<v-text-field label="Name" v-model="contact.name" />
</v-col>
<v-col>
<v-text-field label="Phone Number" v-model="contact.phone" />
</v-col>
<v-col>
<v-text-field label="Extension" v-model="contact.extension" />
</v-col>
<v-col>
<v-btn type="submit">Save</v-btn>
</v-col>
</v-row>
</v-container>
</v-form>
</div>
</template>
<script>
import axios from "axios";
const options = {
headers: { "Content-Type": "application/json" }
};
export default {
name: "CreateEditContact",
props: {
action: {
type: String,
required: true,
validator: value => ["create", "edit"].indexOf(value) !== -1
},
id: {
required: false
},
// contactProp gets set correctly when editing...
contactProp: {}
},
data() {
return {
// the empty contact (when creating a new contact)
contact: {
id: 0,
name: "",
phone: "",
extension: "",
fullString: "",
telephoneUrl: ""
}
};
},
methods: {
saveContact() {
// redacted for brevity
},
created() {
// when contactProp is populated, I need to use that...
// but contact is always empty when I try to edit one.
// this next line isn't doing what I think it should.
if (this.contactProp) this.contact = this.contactProp;
}
}
};
</script>

Grabbing data from multiple child components Vue js

I'm breaking my head for a few days now, trying to figure out how to grab the data from child components.
Situation is like this.
I have one parent component called Post where user can select date, title, description and which can contain multiple instances of Event compontents.
Event component contains fields like title, description, attendees.
User should be able to add multiple Eventcomponents which means I have multiple components Event within the Post component.
So, I can't figure out how can I compose my components to have an array of Event objects inside my Post component which I can later on send to my API.
the structure of the post object I need is:
// Post.vue
{
"date": '',
"name": '',
"description": '',
"events": {
{
"title": '',
"description": '',
"attendees": ''
},
{
"title": '',
"description": '',
"attendees": ''
}
}
}
So, I don't know should and how I would use vuex for it. I've tried using $emit to pass the data but I couldn't find it fit to get the data into Post model.
Can someone point me where should I look for it?
EDIT #1: Added sample code
The code for the components:
<template>
<v-form>
<v-container>
<v-row>
<v-col
cols="12"
md="4"
>
<v-date-picker v-model="post.date" scrollable>
<v-spacer />
<v-btn text color="primary" #click="modal = false">
Cancel
</v-btn>
<v-btn text color="primary" #click="$refs.dialog.save(date)">
OK
</v-btn>
</v-date-picker>
</v-col>
<v-col
cols="12"
md="4"
>
<v-text-field
v-model="post.name"
label="name"
required
/>
</v-col>
<v-col
cols="12"
md="4"
>
<v-textarea
v-model="post.description"
name="description"
label="Description"
dense
value
rows="4"
hint
/>
</v-col>
</v-row>
<v-row>
<v-btn primary rounded #click="addLine">
Add Event
</v-btn>
<v-expansion-panels accordion>
<UserEvent
v-for="(line, index) in lines"
:key="index"
#addLine="addLine"
#removeLine="removeLine(index)"
/>
</v-expansion-panels>
</v-row>
</v-container>
</v-form>
</template>
<script>
import UserEvent from './partials/event'
export default {
name: 'Post',
components: { UserEvent },
data () {
return {
post: [],
lines: [],
blockRemoval: true
}
},
watch: {
lines () {
this.blockRemoval = this.lines.length <= 1
}
},
mounted () {
},
methods: {
addLine () {
const checkEmptyLines = this.lines.filter(line => line.number === null)
if (checkEmptyLines.length >= 1 && this.lines.length > 0) { return }
this.lines.push({
title: null,
description: null,
attendees: null
})
},
removeLine (lineId) {
if (!this.blockRemoval) { this.lines.splice(lineId, 1) }
}
}
}
</script>
And the child component UserEvent
// UserEvent.vue
<template>
<v-expansion-panel>
<v-expansion-panel-header>Event details</v-expansion-panel-header>
<v-expansion-panel-content>
<v-row>
<v-col cols="12" md="6">
<v-text-field
v-model="event.title"
label="Title"
required
/>
</v-col>
<v-col
cols="12"
md="6"
>
<v-text-field
v-model="event.atttendees"
label="Atendees"
required
/>
</v-col>
<v-col
cols="12"
md="12"
>
<v-textarea
v-model="event.description"
name="description"
label="Description"
dense
value
rows="4"
hint
/>
</v-col>
<v-col
cols="12"
md="3"
>
<div class="block float-right">
<v-btn #click="removeLine(index)" />
<v-btn v-if="index + 1 === lines.length" #click="addLine" />
</div>
</v-col>
</v-row>
</v-expansion-panel-content>
</v-expansion-panel>
</template>
<script>
export default {
name: 'UserEvent',
props: ['line', 'index'],
data () {
return {
event: []
}
},
methods: {
addLine () {
this.$emit('addLine')
},
removeLine (index) {
this.$emit('removeLine', index)
}
}
}
</script>
Here's an example with a similar structure what was posed in the question:
{
name: String,
events: [
title: String,
description: String,
],
}
This example allows the user to open a form to add a new event. When that form is submitted, the event data is added to the parent component's state.
Parent
<template>
<div>
<input v-model="name" />
<ul v-if="events.length">
<li v-for="(event, index) in events" :key="index">
<span>{{ event.title }}</span>
<span>{{ event.description }}</span>
</li>
</ul>
<Event v-if="isNewEventFormVisible" #submit="addEvent" />
<button v-else #click="showNewEventForm">add event</button>
</div>
</template>
import Event from '~/components/Event';
export default {
components: { Event },
data() {
return {
name: 'Example Post',
events: [],
isNewEventFormVisible: false,
};
},
methods: {
addEvent({ title, description }) {
this.isNewEventFormVisible = false;
this.events.push({ title, description });
// TODO: call you API here to update
},
showNewEventForm() {
this.isNewEventFormVisible = true;
},
},
};
Event
<template>
<form #submit.prevent="onSubmit">
<input v-model.trim="title" type="text" />
<br />
<textarea v-model.trim="description" />
<button type="submit">submit</button>
</form>
</template>
export default {
data() {
return {
title: '',
description: '',
};
},
methods: {
onSubmit() {
this.$emit('submit', {
title: this.title,
description: this.description,
});
},
},
};
You could imagine a more sophisticated version of this where events are editable. In that case, each Event could take props and bind them as values to its input instead of using v-models.