Vue use computed property in a form - javascript

I have a dynamic form that allows a user to add the item they need, the quantity and the cost per item
<div class="form-group" v-for="(input,k) in inputs" :key="k">
<input type="text" class="form-control" v-model="input.item">
<input type="text" class="form-control" v-model="input.quantity">
<input type="text" class="form-control" v-model="input.cost">
<span>
<i class="fas fa-minus-circle" #click="remove(k)" v-show="k || ( !k && inputs.length > 1)">Remove</i>
<i class="fas fa-plus-circle" #click="add(k)" v-show="k == inputs.length-1">Add fields</i>
</span>
</div>
<button>Submit</button>
</form>
I'm then calculating the total cost of all the materials combined using a computed property as follows:
computed: {
totalCost: function () {
let total = 0
for (let i = 0; i < this.inputs.length; i++){
total += this.inputs[i].cost * this.inputs[i].quantity
}
return total
}
},
What, I the want to do is pass the value of the total cost to a form using the data property as follows. However the total_cost field remains undefined.
data () {
return {
inputs: [{
item: '',
quantity: '',
cost: '',
maintenance_id: this.maintenance_id,
total_cost: this.totalCost,
}],
How do I pass a computed property to a form so that it can be submitted as part of that form?

you can handle the submit by yourself like this
<button #click.prevent="onSubmit">Submit</button>
and send all the data from here.
you can acess total as this.totalCost inside onSubmit function and you do not need that total_cost data property

Related

Duplicating forms with logic

I have created a dynamic form field that I add and remove through a callback function on buttons DelBtn() to remove item and AddBtn() to add item
Each of this form fields have a value input
I also have a totalval field. I expect the sum of values in all the value field not to exceed the totalval. If it does we display an a error message and if the value equals we make the reason form field appear.
Example:
If I have totalValue = 100 . Now lets say I have my first form field value = 90.
Then I duplicate the form and in the next field set value = 10 then the reason field should appear because 90 + 10 = 100.
As the totalValue has been reached the reason form field should appear and the add button should be disabled.
If in the second attempt if the user tries to enter a value more than 10 then an error message should be shown.
Below is my current code
In my TS File I have
ischecks: boolean = true;
formsArray = [""];
count: number = 0;
totalval: number = 100;
ngOnInit(): void {}
constructor() {}
clickCount(): void {
this.count++;
}
DelBtn = delIndex => this.formsArray.splice(delIndex, 1);
AddBtn = () => this.formsArray.push("");
HTML
<h2> Form</h2>
<pre style="font-weight: bolder;font-family:verdana;margin-left: 35px;">Total Value:{{totalval}} </pre>
<div *ngFor="let i of formsArray; let a = index">
<div>
<form>
<table>
<div>
<label for="fname">Value:</label><br>
<input type="text" id="fname" name="fname" ><br>
<tr>
<td>
<button type="button" class="btn btn-outline-success"
style="border-radius:40px;margin-left: 50px" (click)="DelBtn(a)" ><span class="fa fa-plus"></span>Delete</button>
</td>
</tr>
<tr>
</div>
</table>
</form>
</div>
</div>
<div *ngIf=ischecks style="margin-left:35%">
<label for="fname">Reason:</label><br>
<input type="text" id="fname" name="fname" ><br>
</div>
<br>
<button type="button" class="btn btn-outline-success"
style="border-radius:40px;margin-left: 50px;margin-bottom: 30%;" (click)="AddBtn()" ><span class="fa fa-plus"></span>Add</button>
https://stackblitz.com/edit/angular-ivy-3pbdwv?file=src%2Fapp%2Fapp.component.html
Note: If you do not understand the question feel free to ask me in comments adn I am working in angular(typescript)
This will be difficult to achieve with basic Javascript. Below approach uses ReactiveForm approach
We Follow the below steps
Add the ReactiveFormsModule to the imports array of the module
#NgModule({
imports:[ ReactiveFormsModule, ... ],
Inject the FormBuilder class
constructor(private fb: FormBuilder) {}
Define the form
myForm = this.fb.group({
totalVal: [100],
formsArray: this.fb.array([this.fb.control("", { validators: [Validators.required] })]),
reason: ["", [Validators.required]]
}, { validators: [sumMatches] });
We have added a cusom validator sumMatches. We will use this to check whether the sum of the total value has been matched
function sumMatches(control): ValidationErrors | undefined {
const totalVal = Number(control.get("totalVal").value);
const formsArrayTotal = control
.get("formsArray")
.value.reduce((a, b) => Number(a) + Number(b), 0);
if (formsArrayTotal !== totalVal) {
return {
sumMismatch: true
};
}
return;
}
Next we define helper getter functions to extract properties from the formGroup
get sumMismatch(): boolean {
return this.myForm.hasError('sumMismatch')
}
get arrayFullyFilled() {
return !this.formsArray.controls.some(item => item.errors)
}
get formsArray() {
return this.myForm.get("formsArray") as FormArray;
}
get totalVal() {
return this.myForm.get("totalVal") as FormControl;
}
We also need to amend the functions to add and remove items from the formArray
DelBtn = delIndex => this.formsArray.controls.splice(delIndex, 1);
AddBtn = () => this.formsArray.push(this.fb.control(""));
Finally we can implement the formGroup in the html
<h2> Form</h2>
<span class='totalVal'>Total Value:{{ totalVal.value }}</span>
<form [formGroup]='myForm'>
<ng-container formArrayName='formsArray'>
<table *ngFor="let item of formsArray.controls; let i = index">
<tr>
<td>
<div>
<label [attr.for]="'fname' + i">Value:</label><br>
<input type="number" [formControlName]="i" type="text" [id]="'fname' + i" name="fname" ><br>
</div>
</td>
<td>
<button type="button" class="btn btn-outline-success"
s (click)="DelBtn(i)" ><span class="fa fa-plus"></span>Delete</button></td>
<tr>
</table>
</ng-container>
<div *ngIf='!sumMismatch && arrayFullyFilled'>
<label for="fname">Reason:</label><br>
<input type="text" id="fname" name="fname" ><br>
</div>
<br>
<button type="button" class="btn btn-outline-success"
(click)="AddBtn()" ><span class="fa fa-plus"></span>Add</button>
<br>
<span class="error" *ngIf="sumMismatch && myForm.touched">Total Value Mismatch</span>
</form>
I have extracted css to own file
.totalVal {
font-weight: bolder;
font-family: verdana;
}
.btn-outline-success {
border-radius: 40px;
margin-left: 50px;
}
.error {
color: red;
}
See this Demo
Edit 1 - How Does the validator work?
To understand this we look at how we build our form group. We defined a structure that produces a value in the form
{
totalVal: 100,
formsArray: [''],
reason: ''
}
By defining our form group as this.fb.group({ ... }, {validators: [ sumMatches ] } the form group with the above value will be passed to the sumMatches function
In the sumMatches we will have a something like a formGroup with the value
{
totalVal: 100,
formsArray: ['50', '20', '10'],
reason: ''
}
In the above we simply extract the 100 from the formGroup using control.get('totalVal').value same to formArray. Since formArray value will be an array then we can use reduce function to sum this.. We finally compare this and return null if they match and an Object if they do not match.
With the above approach, angular reactive forms will update the value of the form valid status based on what is provided by the user. We can hence leverage this valid status to update the UI
arrayFullyFilled()
get arrayFullyFilled() {
return !this.formsArray.controls.some(item => item.errors)
}
The above code tries to find if the user has filled ALL the inputs in the array. In our array we get all the controls, check if some of them have errors and if any has error return false otherwise return true. This is made possible considering that in my formGroup I had made the formControls as required using Validators.required validation

Make calculation from v-for in vue js

i'm trying to make simple calculation from data given in vuejs. The calculation is just going well. But, when i started to make sum from calculation total from it, it keeps returning NaN value.
Here is the current code of it:
<div class="row" v-for="fields in data" :key="field.id_m_all_ded">
<div class="col-md-5">
<div class="form-group">
<label>{{ field.name }}</label>
<input type="field.type" class="form-control" #change="calculated(field)" v-model="field.value" />
</div>
</div>
<div class="col-md-7">
<label>Total Deductions</label>
<input type="number" class="form-control" v-model="field.total" disabled />
</div>
</div>
<div class="row">
<div class="col-md-5">
<label>Total Allowance</label>
<input type="number" class="form-control" v-model="grandTotal" id="total" disabled />
</div>
</div>
I retrieve the data from my API, and it saved in fields[]:
data() {
return {
model: {
nik: "",
name: "",
basic_salary: "",
period: "",
},
modals: {
modal: false,
},
fields: [{
total: ''
}],
totalData: 11,
page: 1,
mode: "",
};
},
And here's the function:
computed: {
grandTotal: function() {
let temp = 0
for (var i = 0; i < this.fields.length; i++) {
temp = temp + Number(this.fields[i].total)
console.log(temp)
}
console.log(temp)
return temp;
}
},
methods: {
calculated(field){
field.total = 4000000 * (field.value / 100)
console.log(field.total)
}
}
For addtional info, i get the data from the api, but it won't calculate automatic. Therefore, i tried it with manual input first to calculate the value.
What code should i fix from it, because i have no clue.
Thanks

Cannot create property '' on string '' angularjs

I need to add rows and inputs dynamically, in addition to filling each entry in these fields, but at the moment of wanting to fill the input I get a error, with this add the rows:
$scope.detalleTransCover = {
details: []
};
$scope.addDetail = function () {
$scope.detalleTransCover.details.push('');
};
$scope.submitTransactionCobver = function () {
angular.forEach($scope.detalleTransCover, function(obj)
{
console.log(obj.cuenta);
});
};
now in the html:
<tr ng-repeat="detail in detalleTransCover.details">
<td>
<input type="text" class="form-control" ng-model="detail.cuenta">
</td>
<td>
<input type="text" class="form-control" ng-model="detail.debeDolar">
</td>
<td>
<input type="text" class="form-control" ng-model="detail.haberDolar">
</td>
</tr>
<button ng-click="addDetail()" class="btn btn-block btn-primary">
Agregar fila
</button>
<button ng-click="submitTransactionCobver()" class="btn btn-block btn-primary">
Agregar
</button>
on the html when I try to fill the input example "cuenta" haver error:
TypeError: Cannot create property 'cuenta' on string ''
When you push a new input to your array, you need to push an object not a string:
// wrong
$scope.addDetail = function () {
$scope.detalleTransCover.details.push('');
};
// right
$scope.addDetail = function () {
$scope.detalleTransCover.details.push({})
});
Strings can't have properties the same way objects can.

Angularjs: input for ng-repeat elements

Sorry for my language skills, hope you understand all.
I need to make inputs for ng-repeat elements, to pass input data to http post.
For example: i have 4 elements, for all elements i make inputs, and f.e:
elem1- input: 1
elem2- input: 4
elem3- input: 1
elem4- input: 2
Here is my code:
.panel-body.note-link ng-repeat=("meal in mealsList track by $index")
a data-toggle="tab"
h4.pull-right.text-muted
| {{meal.price}} zł
h4.text-success
i.fa.fa-close.text-danger<> ng-click="removeMeal($index)" style="cursor: pointer;font-size: 15px;"
| {{meal.name}} {{$index}}
input.form-control type="number" ng-model="example"
| {{example}}
And i dont know, how to pass input data to controller.
Any tips,tricks?
angular.module('yourModule').controller('viewController', function($scope) {
$scope.mealList = [...];
$scope.example; //The input data will automatically bind to this variable.
});
If you want the input to change data within your meal object, then do this:
input.form-control type="number" ng-model="meal.example"
And then the property value of the meal object would bind to the input
Repeat through your mealList array and add the inputs.
Example: https://jsfiddle.net/ptzhL0uL/
Controller
function Controller1($scope) {
var vm = this;
vm.items_saved = false;
vm.mealList = [{
price: '2.99',
name: 'Pizza'
}, {
price: '1.99',
name: 'Chips'
}, {
price: '4.99',
name: 'Beer'
}];
vm.addNewItem = addNewItem;
vm.saveItems = saveItems;
function addNewItem() {
vm.mealList.push({});
}
function saveItems() {
vm.items_saved = true;
}
}
HTML
<button ng-click="ctrl.addNewItem()" class="btn btn-default">Add Item</button>
<div ng-repeat="item in ctrl.mealList">
<input type="text" ng-model="item.name">
<input type="text" ng-model="item.price">
<input type="number" ng-model="item.quantity">
</div>
<button ng-click="ctrl.saveItems()" class="btn btn-primary">Save</button>
<hr>
<div ng-if="ctrl.items_saved">
<h4>Here is your meal list:</h4>
<ul>
<li ng-repeat="item in ctrl.mealList">{{item.quantity}}{{item.name}} at ${{item.price}} each</li>
</ul>
</div>
Just attach it to the ngModel directive.
<div ng-repeat="item in array">
{{ item.output }}
<input ng-model="item.output" />
</div>

How to populate Table using entered value Angular Json?

I'm working on a clickButton function to populate a table based on the entered value from user. There are 3 fields to input data. First Name, Last Name, Zip. If I type firstName, click search I need to see the associated JSON data in the table. Same thing for lastName and zipCode. When I set $scope.results = data, the table is populated correctly based on entered value for firstName. I am trying to add conditions for the other two fields and then push the results to jsp. What am I doing wrong?
JS
$scope.results = [];
$scope.clickButton = function(enteredValue) {
$scope.items=this.result;
var url = 'http://localhost:8080/application/names/find?firstName='+enteredValue+'&lastName='+enteredValue+'&zipCode='+enteredValue
$http.get(url).success(function(data){
angular.forEach($scope.items, function (item) {
if(items.first_nm === enteredValue || items.last_nm ==enteredValue || items.zip_cd == enteredValue){
$scope.results.push({
firstName: item.first_nm,
lastName: item.last_nm,
zipCode: item.zip_cd
});
}
})
})
};
JSP
<input id="firstName" name="firstName" type="text" data-ng-model="enteredValue" />
<input id="lastName" name="lastName" type="text" data-ng-model="enteredValue" />
<input id="zipCode" name="zipCode" type="text" data-ng-model="enteredValue" />
<button class="btn btn-primary" data-ng-click='clickButton(enteredValue)'>Search</button>
<tr data-ng-repeat="result in results" class="text-center">
<td data-title="'ID'" >{{result.firstName}}</td>
<td data-title="'Name'" >{{result.lastName}}</td>
<td data-title="'Status'" >{{result.zipCode}}
</td>
Currently you are binding all search parameters to the same property - enteredValue. Instead you can assign them to separate properties and then use them accordingly in your search method:
HTML:
<input id="firstName" name="firstName" type="text" data-ng-model="enteredValue.firstName" />
<input id="lastName" name="lastName" type="text" data-ng-model="enteredValue.lastName" />
<input id="zipCode" name="zipCode" type="text" data-ng-model="enteredValue.zipCode" />
<button class="btn btn-primary" data-ng-click='clickButton(enteredValue)'>Search</button>
Controller:
$scope.clickButton = function(enteredValue) {
$scope.items=this.result;
// instead of enteredValue use enteredValue.lastName and other properties
// If your API allows it, you can even add the value only if it exists,
// not always as it is done currently
var url = 'http://localhost:8080/application/names/find?firstName='+
enteredValue.firstName+'&lastName='+
enteredValue.lastName+'&zipCode='+enteredValue.zipCode
$http.get(url).success(function(data){
angular.forEach($scope.items, function (item) {
if(items.first_nm === enteredValue || items.last_nm ==enteredValue || items.zip_cd == enteredValue){
$scope.results.push({
firstName: item.first_nm,
lastName: item.last_nm,
zipCode: item.zip_cd
});
}
});
});
};

Categories