Vue.js - Dynamically generate v-model name - javascript

this is my first use of Vue.js so please bear with me. I have a section in my app where users can dynamically add (up to 5) entries and also remove entries. Each entry consists of four input tags that correspond to product id, description, quantity, and unit price. There is also an "X" icon at the end so that users can choose whether or not to remove that entry row before saving it. So visually, it would look something like this:
1 Tomatoes 40 $2.50 X
2 Pears 50 $1.39 X
3 Celery 12 $1.60 X
I am unsure how to dynamically generate v-model names that correspond to each piece of data that I want to save. In other words, I need four input tags and the X icon for each entry that a user wants to enter. Therefore, I'd want the Vue.js state to look something like:
data: {
numEntries: 2,
entries: [
{
productId: "",
description: "",
qty: "",
price: "",
},
{
productId: "",
description: "",
qty: "",
price: "",
},
// There will be 'n' of these objects depending on how many entries there are.
]
}
And I would like the v-model to be something like "productId1" to refer to entries[0].productId and "productId2" to refer to entries[1].productId, etc. My code is shown below:
HTML
<div id="app">
...
<div v-for="n in numEntries" class="inventory-section">
<input type="text" class="id-input" placeholder="Product Id" v-model="productId" />
<input type="text" class="description-input" placeholder="Description" v-model="description" />
<input type="text" class="qty-input" placeholder="Qty" v-model="qty" />
<input type="text" class="price-input" placeholder="Price" v-model="price" />
<span class="x-sign" v-on:click="removeEntry">X</span>
</div>
...
</div>
Vue JS
var app = new Vue({
el: '#app',
data: {
numEntries: 1,
entries: [
{
productId: "",
description: "",
qty: "",
price: "",
}
]
},
methods: {
addEntry: function () {
if (this.numEntries < 12)
this.numEntries += 1;
},
removeEntry: function () {
if (this.numEntries > 1)
this.numEntries -= 1;
}
}
})
In addition, when clicking the X on a row, how do I determine which row to remove? Currently my removeEntry function is very bare bones.
Any help would be greatly appreciated.

Vue loop code:
<div v-for="(itm,ind) in entries" class="inventory-section">
<input type="text" class="id-input" placeholder="Product Id" v-model="itm.productId" />
<input type="text" class="description-input" placeholder="Description" v-model="itm.description" />
<input type="text" class="qty-input" placeholder="Qty" v-model="itm.qty" />
<input type="text" class="price-input" placeholder="Price" v-model="itm.price" />
<span class="x-sign" #click="removeEntry(ind)">X</span>
</div>
And remove item from array
removeEntry: function (i) {
this.entries.splice(i,1)
}

Instead of using v-for="n in numEntries" use it as v-for="entry in entries".
in this way, "entry" will be your scoped object in that div. and you can use v-model="entry.productId"

you can loop through entries using v-for="(entry, index) in entries" and you can use v-model="entry.productId" and so on
<div id="app">
...
<div v-for="(entry, index) in entries" class="inventory-section">
<input type="text" class="id-input" placeholder="Product Id" v-model="entry.productId" />
<input type="text" class="description-input" placeholder="Description" v-model="entry.description" />
<input type="text" class="qty-input" placeholder="Qty" v-model="entry.qty" />
<input type="text" class="price-input" placeholder="Price" v-model="entry.price" />
<span class="x-sign" v-on:click="removeEntry(index)>X</span>
</div>
...
</div>

Related

Vue jS: input auto filled

In my Vue JS code below i have input total price and two inputs paid and percentage, i wanted to do an algorithm that what ever user wrote inside paid input an auto percentage should be filled in percentage input and ofc this percentage is from total price input, also when user write in percentage input the paid should be auto filled and so on.
Is there a way to do this in Vue js?
P.S: I wrote v-model=instpaid and v-model=instprice that posts data to API
<input type="number" v-model="instPrice" class="price-input mt-3" placeholder=" total price" required />
<br />
<input type="number" v-model="instPaid" class="price-input mt-3" placeholder=" paid " required />
<input type="number" class="price-input mt-3" placeholder=" percenatge" required />
Maybe something like following snippet:
new Vue({
el: "#demo",
data() {
return {
instPrice: 0,
instPaid: 0,
pct: 0
}
},
methods: {
calc(pct) {
pct === true ?
this.instPaid = (this.instPrice / 100 * this.pct).toFixed(2)
:
this.pct = (this.instPaid / this.instPrice * 100).toFixed(2)
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="demo">
<input type="number" v-model="instPrice" placeholder=" total price" required />
<br />
<input type="number" v-model="instPaid" placeholder=" paid" #keyup="calc(false)" required />
<input type="number" placeholder=" percenatge" #keyup="calc(true)" v-model="pct" required />
</div>

Vue - dynamically registered models?

Inside a Vue-component, I'm making a list, but the number of entries in the list varies.
So it could be like this:
<form>
<input type="number" value="15" />
<input type="number" value="10" />
<input type="number" value="5" />
<input name="total" :value="summedTotal" /> <!-- (calculated dynamically) -->
</form>
Or like this:
<form>
<input type="number" value="15" />
<input type="number" value="10" />
<input type="number" value="5" />
<input type="number" value="17" />
<input type="number" value="20" />
<input name="total" :value="summedTotal" /> <!-- (calculated dynamically) -->
</form>
If I had a fixed set of input fields, then I would have solved it using v-model. But now the number of 'models' are dynamic.
Can I somehow still use v-model to solve this?
Currently I'm adding an #keypress-event, finding the input ( document.getElementById( '....' ) and finding the value from that. But I need to set a delay for it to work. I could use keyup or some other event-watcher, but it all becomes really hacky, really quick.
The actual code is (an advanced version of) this:
<form>
<input
type="number"
v-for="(entry, index) in list"
name="entry.id"
value="entry.initial_value"
:id="'entry-id__' + entry.id" #keypress="calculateSum()"
/>
<input name="total" :value="summedTotal" /> <!-- (calculated dynamically) -->
</form>
You should be able to use a v-model that will allow your input fields to change the value in the list :
<input
type="number"
v-for="(entry, index) in list"
:key="'input-field-'+entry.id"
name="entry.id"
v-model="entry.initial_value"
:id="'entry-id__' + entry.id"
/>
When a new value is set by the user, it should modify this precise value in your list object.
Then keep your last line as is:
<input name="total" :value="summedTotal" />
with summedTotal being a computed value summing the values of your list.
If you don't want to change your original list, you can make a deep copy first and then use copiedList for your v-model:
data {
copiedList : JSON.parse(JSON.stringify(this.list))
}
Here is a Snippet that works:
new Vue({
el: '#app',
data: {
list: [{
id: 1,
initial_value: 3
},
{
id: 2,
initial_value: 1
},
{
id: 3,
initial_value: 7
},
{
id: 4,
initial_value: 2
},
]
},
computed: {
summedValued() {
return this.list.reduce((acc, c) => {
return acc + parseInt(c.initial_value || 0);
}, 0)
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div v-for="(entry, index) in list" :key="'input-field-'+entry.id">
ID {{entry.id}}: <input type="number" name="entry.id" v-model="entry.initial_value" :id="'entry-id__' + entry.id" />
</div>
TOTAL :{{summedValued}}
</div>

Why is my form not populating the data using ngModelGroup?

I have a below form, where i am using ngModelGroup to group the inputs.
<form #form="ngForm" (ngSubmit)="submit(form.value)">
<fieldset ngModelGroup="user">
<div>
<label>Firstname:</label>
<input type="text" name="firstname" [(ngModel)]="firstname">
</div>
<div>
<label>Lastname:</label>
<input type="text" name="lastname" [(ngModel)]="lastname">
</div>
</fieldset>
<fieldset ngModelGroup="address">
<div>
<label>Street:</label>
<input type="text" name="street" [(ngModel)]="street">
</div>
<div>
<label>Zip:</label>
<input type="text" name="zip" [(ngModel)]="zip">
</div>
<div>
<label>City:</label>
<input type="text" name="city" [(ngModel)]="city">
</div>
</fieldset>
<button type="submit">Submit</button>
</form>
When i map my [(ngModel)] = "user.firstname" or "user.lastname" or "user.address.street" it works? If i do in the above way, i don't see a reason why i need ngModelGroup in total.
I am not sure how to properly use ngModelGroup for nested objects.
Below is the plunkr:
https://plnkr.co/edit/Y4bjFh6sjtvdzkUWciid?p=preview
ngModelGroup lets you shape the data received from the form by introducing "subproperties".
With ngModelGroup="user" in your template, here's what form.value will look like:
{
"user": {
"firstname": "foo",
"lastname": "bar"
},
// ...
}
Without ngModelGroup="user" in your template, form.value will be:
{
"firstname": "foo",
"lastname": "bar",
// ...
}
ngModelGroup can be useful to give the form data the same shape as your data models. But it has no impact on the properties that you bind [(ngModel)] to (these properties can be whatever you want).

How to dynamically create a form using angularjs

I am trying to figure out how to dynamically create a form based on an array of fields returned from the server.
Something like this
[{
id: "title",
type: "text", /* Map to Input type=text */
regex: "/^[a-zA-Z]+/"
},{
id: "summary",
type: "memo", /* Map to Textarea */
regex: "/^[a-zA-Z]+/"
},{
id: "priority",
type: "list", /* Map to Select */
options: [1,2,3,4,5]
}]
I cant figure out a nice way of doing this, even with ng-repeat. Once the form has about 30 fields, and 15+ different input types, it gets very ugly.
Whats the 'Angular' way to do this, or should i generate the controller.js and template.html dynamically on the server?
Try this. It is not the answer, just an approach.Hope It may help You.
HTML:
<div ng-repeat="person in person">
<form class="form-group" name="signinForm">
<label>Name:
<input class="form-control" ng-model="person.name" type="text">
</label>
<label>Male:
<input class="form-control" ng-model="person.gender" name="gender{{$index}}" type="radio" value="male">
</label>
<label>Female:
<input class="form-control" ng-model="person.gender" name="gender{{$index}}" type="radio" value="female">
</label><br>
<label>Age
<input class="form-control" ng-model="person.age" type="number">
</label>
</form>
<button type="button" ng-click="addPerson()">Add More</button>
</div>
JS:
$scope.initialize = [{}];
$scope.person = {};
$scope.addPerson = function(){
$scope.initialize.push({});// adding more similar fields.
}

Posting form using javascript and json with nested objects

I have an html form that has the following structure:
<input type="text" name="title" />
<input type="text" name="persons[0].name" />
<input type="text" name="persons[0].color" />
<input type="text" name="persons[1].name" />
<input type="text" name="persons[1].color" />
I would like to serialize this into the following json:
{
"title": "titlecontent",
"persons": [{
"name": "person0name",
"color": "red"
},
{
"name": "person1name",
"color": "blue"
}
]
}
Notice that the number of persons can vary from case to case.
The structure of the html form can change, but the structure of the submitted json can't.
How is this done the easiest?
Dependencies:
locutus (npm install locutus)
jQuery
Code:
var parse_str = require('locutus/php/strings/parse_str');
parse_str($("#form ID").serialize(), var result = {});
console.log(result);
Hey man using jQuery and the
jQuery JSON plugin, I was able to do the following:
I had to change your html a bit:
<div id="Directory">
<input type="text" class="title" />
<span class="person 0">
<input type="text" class="name" />
<input type="text" class="color" />
</span>
<span class="person 1">
<input type="text" class="name" />
<input type="text" class="color" />
</span>
</div>
And then I was able to use the following code to create your JSON array:
var Directory = {};
Directory.title = $("div#Directory input.title").val();
Directory.persons = [];
$("div#Directory span.person").each(function() {
var index = $(this).attr("class").split(" ")[1];
Directory.persons.push({
name: "person" + index + $(this).children("input.name").val(),
color: $(this).children("input.color").val(),
});
});
alert($.toJSON(Directory));
This http ://stackoverflow.com/questions/492833/getting-correct-index-from-input-array-in-jquery is also somewhat helpful. Unfortunately I couldn't replicate their selection method with your particular set of attributes.
Hope I'm not doing your homework ;) There's a space in the URL above because I don't have enough reputation points to put more than one hyperlink.
This is your final JSON:
{
"title": "abc",
"persons": [{
"name": "person0englishman",
"color": "pink"
}, {
"name": "person1indian",
"color": "brown"
}]
}

Categories

Resources