I am newbie to Angular js. I am trying to calculate the sum of values of third column and save result into downtime box.
Here, I had already done.
1.Dynamically Add/remove the row.
2.Calculate the difference between the 1st column and 2nd column and save the result into third column.
3.Now,sum the values of third column and save into textbox.
Now, third point is not working.
HTML:
<!doctype html>
<html ng-app="Myapp">
<head>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
</head>
<body ng-controller="myctrl">
<table id="t1" style="border:none;">
<tr><th>Start</th><th>Stop</th><th>Downtime(In Min)</th><th>Reason</th></tr>
<tr ng-repeat="item in invoice">
<td><input type="text" required ng-model="$invoice.start" name="r1[]"></td>
<td><input type="text" required ng-model="$invoice.stop" ng-blur="diff($invoice)" name="r2[]"></td>
<td><input type="text" name="r3[]" ng-model="$invoice.diff"/></td>
<td><input type="text" ng-model="$invoice.reason" name="r4[]" ></td>
<td style="border:none;"><a href ng-click="remove(item)">X</a></td>
</tr>
<tr style="border:none;">
<td style="border:none;"><a href ng-click="add()">+</a></td>
</tr>
</table>
<br/>
<div>
<span class="labelCode">Total Downtime</span><input required type="text" ng-value="run()" name="Tot_D" /></span></br>
</div>
Angular js
var myapp=angular.module("Myapp",[]);
myapp.controller("myctrl",function($scope){
$scope.invoice = [{
start :"7:00",
stop:"7:30" ,
reason: "M/C Ready to Start",
}]
$scope.add= function(){
$scope.invoice.push({
start:"7:30",
stop:"8:00"
});
};
//Remove the rows
$scope.remove=function(index){
$scope.invoice.splice(index,1);
};
$scope.diff = function(item) {
item.diff = computeDiff(item.start,item.stop);
}
function computeDiff(start, stop) {
if (start && stop) {
var s_hr = start.split(":")[0];
var s_min = start.split(":")[1];
var e_hr = stop.split(":")[0];
var e_min = stop.split(":")[1];
return Math.abs((parseInt(e_hr) - parseInt(s_hr)) * 60) + Math.abs(parseInt(e_min) - parseInt(s_min))
}
}
$scope.run = function(){
var total = 0;
for(var i = 0; i < $scope.invoice.length; i++){
var product = $scope.invoice[i];
total += parseInt($scope.diff());
}
return total;
}
});
Sum of repeated values of third column is not updating into downtime text box.
I dont where I am going wrong.Please help me out.Thanks in advance.
First of all I suggest you to us ng-init directive for every iteration to calculate, but it's Very Important to use functions inside ng-init. Something like this:
<tr ng-repeat="item in invoice">
<td><input type="text" ... ng-init="sum(item)"></td>
</tr>
Then inside your controller you can define that function and do there whatever you want, because ng-repeat will call that function for every iteration
Related
I have one form that has a whole table inside it. A for loop iterates through a list of items and adds records to the table. Each record has a submit button. When I click that button, I use AJAX to serialize
and POST the form (because I need to reload a partial), and expect to POST data about that single item in the record to the controller.
In other words, if a table is displaying records from a looped list, I want to be able to POST a single list item to the controller.
What is holding me back is the for and foreach loops when I try to POST a record:
If I use a foreach loop the form always POSTs the very first record in the table, even if I click the button on other records.
If I use a for loop I am required to POST the whole list to my controller because the list is a parameter inside another object. This means I am forced to bind to the model that contains the list.
Possible reason for the issue: I suspect this has something to do with the values of the name attributes in the HTML that asp-for attribute generates. With a for loop the names are not unique, so the binding process assumes the first record with the correct names. With a foreach loop, the names are unique, but the binding process needs to bind to the model that contains the list.
HTML View (simplified). A foreach loop in place of the for loop would look like #foreach (var shipment in lineItem.Shipments) { <tr>... </tr> }
#model OrderTrackingContract.SalesOrder
#foreach (var lineItem in Model.LineItems)
{
<table class="lineItemTables">
//line items table
</table>
#if (lineItem.Shipments.Count > 0)
{
<form method="post">
<table class="table shipmentTable">
<thead>
<tr>
<th>
ShipmentID
</th>
<th>
Qty Shipped
</th>
<th>
Actions
</th>
</tr>
</thead>
<tbody>
#for (int i = 0; i < lineItem.Shipments.Count; i++)
{
<tr>
<td>
<input id="shipmentID" asp-for="#lineItem.Shipments[i].ShipmentID" />
</td>
<td>
<input id="qtyShipped" asp-for="#lineItem.Shipments[i].QtyShipped" value="#lineItem.Shipments[i].QtyShipped" min="1" max="999" />
</td>
<td>
<input class="updateButton" type="submit" value="Update" />
</td>
</tr>
}
</tbody>
</table>
</form>
}
}
AJAX
$('.updateButton').on('click', function () {
var form = $(this).closest('form');
form.submit(function (event) {
event.preventDefault(); /* stop form from submitting normally */
$.ajax({
url: "/OrderTracking/UpdateShipment",
type: "POST",
data: form.serialize(),
success: function () {
//some code here
}
});
});
});
Controller Action
[HttpPost]
public IActionResult UpdateShipment(Shipment shipment)
{
_orderTrackingService.UpdateShipmentByID(shipment.ShipmentID, shipment.QtyShipped);
return NoContent();
}
I have tried to extract the values from the tags using the tag ids shipmentID and qtyShipped closest to the button and pass them to the controller, which worked. However, I want to avoid doing that and instead actually POST the form.
If you just want to pass data for a single row then just get the data for that row and pass to your action... don't serialize every row and post it.
You should do 2 things...
First update the inputs in your loop by giving them a class and not an id. id's should be unique for the page, but you are creating the same id="shipmentID" and id="qtyShipped" for your inputs. Don't do this, the asp-for will create your id and name attributes for you.
<tbody>
#for (int i = 0; i < lineItem.Shipments.Count; i++)
{
<tr>
<td>
<input class="shipmentID" asp-for="#lineItem.Shipments[i].ShipmentID" />
</td>
<td>
<input class="qtyShipped" asp-for="#lineItem.Shipments[i].QtyShipped" value="#lineItem.Shipments[i].QtyShipped" min="1" max="999" />
</td>
<td>
<input class="updateButton" type="submit" value="Update" />
</td>
</tr>
}
</tbody>
Next, update your ajax request to find the row the button is in then get the values of the inputs of that row and make a data object to be passed to your action. Like this:
$('.updateButton').on('click', function (e) {
e.preventDefault(); /* stop form from submitting normally */
var row = $(this).closest('tr'),
shipmentID = $('.shipmentID', row),
qtyShipped = $('.qtyShipped', row);
var data = { ShipmentID: shipmentID.val(), QtyShipped: qtyShipped.val() };
$.ajax({
url: "/OrderTracking/UpdateShipment",
type: "POST",
data: data,
success: function () {
//some code here
}
});
});
You were also attaching a submit event to your form everytime a submit button was clicked. This is bad and would result in 3 submit events being attached if 3 buttons were clicked, so I removed this.
I am writing a movie app that allows you to rent movies (similar to Redbox). I have a CheckOut cart view containing a table. Each table row has a remove button which uses AJAX to delete element in the view and also update the SQL database that the app works with. After removing any items from the cart, the user should be able to click 'purchase' and process the items that were left in the cart, all without needing to refresh the page.
I have an Order model containing a list of OrderDetails. Each OrderDetails item has information about a movie. It is data from OrderDetails list that the table is populated with.
The issue comes in when I remove items from cart and try to post the form with the values in the table. My CheckOut HttpPost controller method receives the model, but the OrderDetail list still has the item count it originally had before I removed items from cart. Logically, there is no data bound to the properties since I deleted the hidden tags I had in each record.
Because the list contains elements I don't need, processing the list results in garbage data going into the database.
I tried to simply remove the garbage elements within my CheckOut HttpPost method before it begins processing the list. This worked great but I don't want to have to remove anything in the CheckOut method after posting the form. I'm expecting the list to not contain the elements.
CheckOut POST method:
[HttpPost]
public IActionResult CheckOut(Order order)
{
if (ModelState.IsValid == false)
{
return View("CheckOut", order);
}
foreach (var orderDetailObj in order.OrderDetailsList)
{
_checkOutService.StoreMoviesInOrder(GetConnectionString(), order.OrderId, orderDetailObj);
}
return RedirectToAction("PurchaseSummary", new { Id = order.OrderId });
}
CheckOut.cshtml view:
#model MovieContract.Order
...
#for (int i = 0; i < Model.OrderDetailsList.Count; i++)
{
<tr>
<td>
<input type="button" name="btnRemove" class="removeButton" value="Remove" onclick="Remove(this, '#Model.CartId', #Model.OrderDetailsList[i].Movie.FilmId)" />
</td>
<td hidden>
<input asp-for="#Model.OrderDetailsList[i].Movie.AddedToCart" value="#Model.OrderDetailsList[i].Movie.AddedToCart" hidden />
</td>
<td hidden>
<input asp-for="#Model.OrderDetailsList[i].Movie.FilmId" value="#Model.OrderDetailsList[i].Movie.FilmId" hidden />
</td>
<td>
<input asp-for="#Model.OrderDetailsList[i].Movie.FilmName" value="#Model.OrderDetailsList[i].Movie.FilmName" hidden />
#Model.OrderDetailsList[i].Movie.FilmName
</td>
<td>
<input asp-for="#Model.OrderDetailsList[i].Movie.GenreName" value="#Model.OrderDetailsList[i].Movie.GenreName" hidden />
#Model.OrderDetailsList[i].Movie.GenreName
</td>
<td>
<input asp-for="#Model.OrderDetailsList[i].Movie.PricePerDay" value="#Model.OrderDetailsList[i].Movie.PricePerDay" class="pricePerDay" hidden />
#Html.DisplayFor(modelItem => #Model.OrderDetailsList[i].Movie.PricePerDay)
</td>
<td hidden>
<input asp-for="#Model.OrderDetailsList[i].Movie.AmountOnHand" value="#Model.OrderDetailsList[i].Movie.AmountOnHand" hidden />
</td>
</tr>
}
As for AJAX, I simply have an AJAX function that calls a post controller method. The method deletes the appropriate item from the database and returns NoContent();. Upon success, AJAX deletes the desired row from the view.
I expect that by the time I reach the CheckOut HttpPost method, the parameter object's list property will contain less elements if I had decided to remove any from the cart. I don't want to have to refresh the whole page to rebuild my model each time I remove an item from the cart.
Here is a working demo :
View
#model AjaxDeleteItem.Models.Order
<div>
<form method="post" asp-action="CheckOut">
<table class="table" id="table">
<thead>
<tr>
<th></th>
</tr>
</thead>
<tbody>
#for (int i = 0; i < Model.OrderDetailsList.Count; i++)
{
<tr class="count">
<td>
<input type="button" name="btnRemove" class="removeButton" value="Remove" onclick="Remove(this, #Model.OrderDetailsList[i].Id)" />
</td>
<td >
<input class="FilmId" asp-for="#Model.OrderDetailsList[i].Movie.FilmId" value="#Model.OrderDetailsList[i].Movie.FilmId" />
</td>
<td >
<input class="FilmName" asp-for="#Model.OrderDetailsList[i].Movie.FilmName" value="#Model.OrderDetailsList[i].Movie.FilmName" />
</td >
<td>
<input class="GenreName" asp-for="#Model.OrderDetailsList[i].Movie.GenreName" value="#Model.OrderDetailsList[i].Movie.GenreName" />
</td>
<td>
<input class="PricePerDay" asp-for="#Model.OrderDetailsList[i].Movie.PricePerDay" value="#Model.OrderDetailsList[i].Movie.PricePerDay" />
</td>
</tr>
}
</tbody>
</table>
<input type="submit" value="Submit"/>
</form>
</div>
#section Scripts
{
<script>
function Remove(obj,id) {
$.ajax({
type: "post",
url: "/orders/deleteorderitem?id="+id,
success: function () {
$(obj).closest('tr').remove();
var count = $(" tbody tr").length;
var i = 0;
$("tbody tr").each(function () {
var row = $(this);
if (i < count)
{
row.find("input[class=FilmId]").attr("name", "OrderDetailsList[" + i + "].Movie.FilmId");
row.find("input[class=FilmName]").attr("name", "OrderDetailsList[" + i + "].Movie.FilmName");
row.find("input[class=GenreName]").attr("name", "OrderDetailsList[" + i + "].Movie.GenreName");
row.find("input[class=PricePerDay]").attr("name", "OrderDetailsList[" + i + "].Movie.PricePerDay");
i++;
}
});
},
error: function () {
alert("Fail to delete");
}
});
}
</script>
}
2.Controller:
[HttpPost]
public async Task<IActionResult> DeleteOrderItem(int id)
{
var orderdetail = await _context.OrderDetails.FindAsync(id);
_context.OrderDetails.Remove(orderdetail);
await _context.SaveChangesAsync();
return NoContent();
}
[HttpPost]
public IActionResult CheckOut(Order order)
{
if (ModelState.IsValid == false)
{
return View("Details", order.CartId);
}
//the stuff you want
}
3. Result :
I'm trying to build a dynamic table in Angular with add, edit, delete functionality. Both my edit/delete buttons exist as their own column on every single one of the table rows. The edit button (onclick) is designed to make each of the 3 text data fields (in the same row as the edit button itself) turn into input fields whose user-entered text can then be saved.
<table id="thetable" align="center">
<tr>
<th>Application ID</th>
<th>Description</th>
<th>API Key</th>
<th>EDIT/DELETE</th>
</tr>
<tr id="newtablerow" ng-app="tblRowApp" *ngFor="let prov of providers; let i = index">
<td id="tablevalues" *ngFor="let col of columns">
<span id="columnText" ng-init="getRowIndex(i)" *ngIf="!editing">{{prov[col]}}</span>
<span class="editfield" *ngIf="editing">
<input id="changeText" ng-init="getChangeTextCol(col)" type="text" style="margin-right: 10px" placeholder="{{prov[col]}}">
<button ngOnload="getChangeTextCol(col)" (click)="save(changeText.value); !editing">Save</button>
</span>
</td>
<td id="editdelete">
<button class="edit" name="editButton" (click)="editToggle(i)">/</button>
<button class="delete" (click)="deleteRow(i)">x</button>
</td>
</tr>
public editing: boolean = false;
editToggle(event) {
var table = (<HTMLTableElement>document.getElementById("thetable"));
var getTextFields = table.getElementsByClassName("columnText");
for (var i = 0; i < getTextFields.length; i++) {
if (getTextFields[i].getRowIndex(event) == event) {
getTextFields[i].editing = !getTextFields[i].editing;
}
}
}
getRowIndex(event) {
console.log("row index = " + event);
return event;
}
getChangeTextCol(event) {
return event;
}
deleteRow(event) {
this.providers.splice(event, 1);
}
editToggle() is activated on edit button click and finds the current row index of itself by variable i specified by *ngFor. However, I also need to tell angular the row index of the span element containing the html input field to be shown, but I get an error saying property 'getRowIndex' does not exist on type 'Element'. This works for other functions in HTML elements like deleteRow(i), for instance.
I have the following AngularJS/HTML code:
<tbody>
<tr ng-repeat="currElement in myCtrl.elementList track by $index" >
<td ng-class-odd="'element-tilt-left'" ng-class-even="'element-tilt-right'">
<a ui-sref="myState({elementId: currElement.elementId)" ng-bind="currElement.name">
</a>
</td>
</tr>
</tbody>
Instead of having a multi-row table, I would like a table with only a single row. The data in the <td> of that row should cycle among the elements of myCtrl.elementList with 1 second intervals. IE: the data displayed should change after every second. After the elements are exhausted, it should go back to the beginning of that list and repeat forever.
How can I do it?
Instead of using an ng-repeat, do something like this:
<tr>
<td ng-class="{ 'element-tilt-left': myCtrl.currentIndex % 2 == 1, 'element-tilt-right': myCtrl.currentIndex % 2 == 0}">
<a ui-sref="myState({elementId: myCtrl.currElement.elementId)"
ng-bind="myCtrl.currElement.name">
</a>
</td>
</tr>
And in your controller:
app.controller("myCtrl", function($scope, $interval) {
var _self = this;
_self.elementList = [
// some array with your data
];
_self.currentIndex = 0;
_self.currentElem = _self.elementList[_self.currentIndex];
var currentElementInterval = $interval(function() {
_self.currentIndex++;
if(_self.currentIndex >= _self.elementList.length) {
_self.currentIndex = 0;
}
_self.currentElem = _self.elementList[_self.currentIndex];
}, 1000);
// remember to clear the interval to prevent memory leaks
$scope.$on("destroy", function() {
$interval.cancel(currentElementInterval);
currentElementInterval = undefined;
});
});
i have a table that loads multiple checkboxes and selectboxes.when i click on one checkbox or select box it automatically selects every other box on the table .i want to have the option to choose either one checkbox or select box per row on it own.
<tr id="TableBody" ng-repeat="code in Register.RegisterDetails">
<td>{{$index+1}}</td>
<td ng-bind="code .CodeID"><input type="text" ng-model="Register.CodeID" /></td>
<td ng-bind="code .name"><input type="text" ng-model="Register.Firstname" /></td>
<td ng-bind="code .Lastname"><input type="text" ng-model="Register.Lastname" /></td>
<td><input type="checkbox" ng-model="Register.Presentstatus" id="PresentCheckbox" name="PresentCheckbox" /></td>
<td><select id="reasons" name="reasons" ng-model="Register.Category" ng-disabled="Register.Presentstatus" ng-clicked="Register.Presentstatus && O" ></td>
</tr>
my module that gets my data
function Register(){ self.RegisterDetails = function () {
var params = { pass params here };
return $http.get
{
url: GetRegisterDetails,
params: params,
success: function (data) {
self.RegisterDetails = data.data;
}
});
}
my controller
ngAppModule.controller('RegisterController',['$scope','$http',function($scope,$http)
{
var self = this;
$scope.Register = new Register($http);
}]);
all the above code works fine. i just dont know how to check a single box per row.sorry im new to this site
The main problem you're running into is you are binding to the wrong this inside your ng-repeat.
Your HTML currently repeats a checkbox for every code, but binds that to the same object property Register.Presentstatus.
<tr id="TableBody" ng-repeat="code in Register.RegisterDetails">
<td>{{$index+1}}</td>
....
<td><input type="checkbox" ng-model="Register.Presentstatus" .../></td>
</tr>
You'll need to bind this to a row-specific (code-specific) property if you want each row to have independent check boxes. Perhaps you are looking for something that binds the checkbox to an element in an array:
<tr id="TableBody" ng-repeat="code in Register.RegisterDetails">
<td>{{$index+1}}</td>
....
<td><input type="checkbox" ng-model="Register.Presentstatus[$index]" .../></td>
</tr>
or actually binds to a property of the code object
<tr id="TableBody" ng-repeat="code in Register.RegisterDetails">
<td>{{$index+1}}</td>
....
<td><input type="checkbox" ng-model="code.Presentstatus" .../></td>
</tr>
Since youur ng-model="Register.Presentstatus" is repeating and is same for all so you have same binding for all rows. you can alter them to have different binding