0

Well I am working on the sales and billing part of my small project. Where the left side table shows you the stocks levels and on double clicking any row the form on the left populates as shown in picture.

But the user must be able to specify the quantity. So on inserting required quantity the amt column should be auto calculated(qty*rate).

At first I implemented jQuery's 'keyup' function, but it didn't work. Then I tried implementing angularjs, I failed with it. Then I discovered this jAutoCalc plugin , its also not working.

I tested all these three with simple form, they have been working well. But when I apply the same logic with the form to be generated through jQuery, based on click event, it doesn't work at all.

I have been struggling with this since 6 hours.Please help me out. If there is any proper way of implementing angularjs or jAutoCalc please let me know.

@model IEnumerable<FYPPharmAssistant.Models.InventoryModel.Stock>
....
<script src="~/Scripts/jAutoCalc.js"></script>

@using (Html.BeginForm())
{
    <table class="table table-hover" id="maintable">
        <thead>
            ....
        </thead>
        @foreach (var item in Model)
        {
            <tr class="rows">
                <td>@Html.DisplayFor(modelItem => item.ID)</td>
                <td>@Html.DisplayFor(modelItem => item.Item.Name)</td>
                <td>@Html.DisplayFor(modelItem => item.Item.DrugGenericName.GenericName)</td>
                <td>@Html.DisplayFor(modelItem => item.Qty)</td>
                <td>@Html.DisplayFor(modelItem => item.SellingPrice)</td>
            </tr>
        }
    </table>
}

<!-- Rows to be appended using jQuery-->
<table id="tblAppendHere" name="table1" class="table table-condensed">
    <thead>
        ....
    </thead>
    <tbody>

    </tbody>
</table>

Scripts

<script type="text/javascript" >            
<!--
    function autoCalcSetup() {
        $('form[name=table1]').jAutoCalc('destroy');
        $('form[name=table1] tr[name=row1]').jAutoCalc({ keyEventsFire: true, decimalPlaces: 2, emptyAsZero: true });
        $('form[name=table1]').jAutoCalc({ decimalPlaces: 2 });
    }
    autoCalcSetup();
//-->     

//gets data from table row and populates in the form.
document.getElementById('maintable').ondblclick = function (event) {
    event = event || window.event;
    var target = event.target || event.srcElement;
    while (target && target.nodeName != 'TR') {
        target = target.parentElement;
    }
    var cells = target.cells;
    if (!cells.length || target.parentNode.nodeName == 'THEAD') {
        return;
    }                

    //appends a table row with for each record dblclick 
    var $table = $('#tblAppendHere');
    $table.append(
        '<tr name="row1">' +
        '<td><input type="hidden" name="ID1"  value= "' + cells[0].innerHTML + '"/>#' + '</td>' +
        '<td><input type="hidden" name="Name1"  value= "' + cells[1].innerHTML + '"/>' + cells[1].innerHTML + '</td>' +
        '<td><input type="text" id="qty1" name="Qty1"   style="width:60px;"/>' + '</td>' +
        '<td><input type="hidden" id="rate1" name="Rate1"  value= "' + cells[4].innerHTML + '"/>' + cells[4].innerHTML + '</td>' +
        '<td><input type="text" style="width:90px;" id="amt1"  name="Amount1" value="" jAutoCalc="{Qty} * {Rate}" />' + '</td>' +
        '<td><a href="#" class="glyphicon glyphicon-remove" onclick="removeItem(this)"></a></td>'
        +'</tr>'
    );                
}
//removes row
function removeItem(obj) {
    $obj = $(obj).parent().parent().remove();
};    

        </script>

Interface

At the end, on client side I want something similar to like this: link for demo :http://c17r.github.io/jAutoCalc/sample.html enter image description here

Avishekh Bharati
  • 1,858
  • 4
  • 27
  • 44
  • Are you wanting to handle the `.change()` event in the RHS `Qty` textboxes and update the `Amt` textbox? –  Sep 19 '15 at 04:41
  • And are you intending to submit the form with the quantities? You not adding any inputs to the form so nothing will be posted back. The inputs you are creating are invalid html (duplicate `id` attributes) and have duplicate `name` attributes so wont bind to anything anyway –  Sep 19 '15 at 04:44
  • Ok I realized that there is duplication. but even after removing duplication. It doesn't work. Well, the html helpers used doesn't generate id or names for the display field on table. I checked it on "view page source". So I dont think there is duplication. Anyway I changed the id's to avoid confusion. – Avishekh Bharati Sep 19 '15 at 05:31
  • 1
    `` is generating duplicate `id` attributes each time you add a row! Doing the calculation is easy and you certainly don't need a plugin for it but unless you answer my queries I can't answer –  Sep 19 '15 at 05:33
  • Thanks, I certainly didn't realize that. I will try and let you know. – Avishekh Bharati Sep 19 '15 at 05:47
  • @ Stephen Muecke : I think name is required and needs to be dupliated. As later I would send all these form values to the controller as form collection and save/update in database. – Avishekh Bharati Sep 19 '15 at 06:00
  • Yes the `name` attribute is required, but it needs `indexers` assuming you actually want to submit something to a controller (having duplicates wont work). But you need to explain what is is you actually want to do (other wise you might as well just delete this question) –  Sep 19 '15 at 06:09
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/90090/discussion-between-avi-b-and-stephen-muecke). – Avishekh Bharati Sep 19 '15 at 06:19

2 Answers2

1

Your current implementation will not work for a number of reasons including

  1. Your second table where you dynamically adding inputs is outside the form element so will not post back to the controller when you submit
  2. Even if you fix this, the controls your creating have duplicate name attributes without indexers so will not be able to bind to your model when you submit (and using FormCollection will not help because of the difficulty in match up values)
  3. Your generating invalid html because of duplicate id attributes

If you do want to do this as a separate table, then the 2nd part of this answer will give you some guidance as to creating the inputs so that it will bind correctly. However, an easier solution would be to just add additional columns in your table for the Quantity and Amount. Create a view model for editing

public class PurchaseVM
{
  [Display(Name = "Stock ID")]
  public int ID { get; set; }
  public string Item { get; set; }
  [Display(Name = "Generic Name")]
  public string Name { get; set; }
  public int Stock { get; set; }
  [DisplayFormat(DataFormatString = "{0:0.00")]
  public decimal Rate { get; set; }
  public int Quantity { get; set; }
  [DisplayFormat(DataFormatString = "{0:0.00")]
  public decimal Amount { get { return Quantity * Amount; } }
}

In your controller GET method, map your data model to the view model

public ActionResult Edit()
{
  IEnumerable<Stock> stock = .....
  IEnumerable<PurchaseVM> model = stock.Select(s => new PurchaseVM()
  {
    ID = s.ID,
    Item = s.Item.Name,
    Name = s.Item.DrugGenericName.GenericName,
    Stock = s.Qty,
    Rate = s.SellingPrice
  });
  return View(model);
}

then create an EditorTemplate for displaying each row in the table. In /Views/Shared/EditorTemplates/PurchaseVM.cshtml

@model yourAssembly.PurchaseVM
<tr>
  <td>@Html.DisplayFor(m => m.ID)</td>
  <td>@Html.DisplayFor(m => m.Item)</td>
  <td>@Html.DisplayFor(m => m.Name)</td>
  <td>@Html.DisplayFor(m => m.Stock)</td>
  <td>@Html.DisplayFor(m => m.Rate)</td>
  <td>
    @Html.HiddenFor(m => m.ID)
    @Html.TextBoxFor(m => m.Quantity, new { @class = "quantity" })
  </td>
  <td>@Html.DisplayFor(m => m.Amount)</td>
</tr>

and in the view

@model IEnumerable<yourAssembly.PurchaseVM>
@using (Html.BeginForm())
{
  <table>
    <thead>
      <tr>
        <td>@Html.DisplayNameFor(m => m.ID)</td>
        ..... // other headings
      </tr>
    </thead>
    <tbody id="items">
      @Html.EditorFor(m => m)
    </tbody>
    <tfoot>
      <tr>
       <td>Total<td>
       ....
       <td id="total"><td>
      </tr>
    </tfoot>
  </table>
  <input type="submit" />
}

and add a script to update the amount and the total amount

var rows = $('.items tr');
var totalCell = $('#total');
$('.quantity').change(function() {
  var cells = $(this).closest('tr').children('td');
  var quantity = Number($(this).val());
  var stock = Number(cells.eq(3).text());
  var rate = Number(cells.eq(4).text());
  // prevent entering a quantity greater than the available stock
  if(quantity > available) {
    $(this).val(available);
    quantity = available;
  }
  // calculate the total
  var amount = (quantity * rate).toFixed(2);
  cells.eq(6).text(amount);
  var total = Number();
  $.each(rows, function(index, item) {
    total += Number($(this).children('td').eq(6).text());
  });
  totalCell.text(total.toFixed(2));
});

and finally, your POST method

public ActionResult Edit(IEnumerable<PurchaseVM> model)
{
  var purchasedItems = model.Where(m => m.Quantity > 0);
  // save the data and redirect
}

Refer this fiddle for an example of how the script works

Community
  • 1
  • 1
0

I'm not familiar with the jAutoCalc plugin, however, AngularJS has a two-way binding feature that updates your views (the html output) based on changes made into your model (and vice versa). Making use of this feature you can easily solve your problem.

I will show a quick example so you can improve it to suit your needs:

var app = angular.module('app', []);

app.controller('myController', ['$scope', function ($scope) {

    $scope.models = [];

    $scope.add = function() {

        var randomPrice = parseInt(Math.random() * 10 + 1, 10),
            randomQuantity = parseInt(Math.random() * 10 + 1, 10);

        $scope.models.push({price: randomPrice, quantity: randomQuantity});
    };

    $scope.total = function() {

        var total = 0;

        angular.forEach($scope.models, function(i){
            total += (i.price * i.quantity);
        });

        return total;

    };

}]);
<!DOCTYPE html>
<html ng-app="app">

<head>

  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.6/angular.min.js"></script>
  <script src="app.js"></script>

  <style type="text/css">
    table {
      border-collapse: collapse;
    }
    td,
    th {
      border: 1px solid #000;
      padding: 5px 10px;
    }
  </style>

</head>

<body>

  <div ng-controller="myController">

    <table>
      <thead>
        <tr>
          <th>id</th>
          <th>price</th>
          <th>quantity</th>
          <th>total</th>
        </tr>
      </thead>
      <tbody>
        <tr ng-repeat="model in models">
          <td>{{$index+1}}</td>
          <td>
            <input type="text" ng-model="model.price">
          </td>
          <td>
            <input type="text" ng-model="model.quantity">
          </td>
          <td>
            {{model.price * model.quantity}}
          </td>
        </tr>
      </tbody>
      <tfoot>
        <tr>
          <th colspan="3">grandtotal</th>
          <th>{{total()}}</th>
        </tr>
      </tfoot>
    </table>

    <br />

    <button ng-click="add()">add</button>

  </div>

</body>

Hope it helps!

Romulo
  • 4,896
  • 2
  • 19
  • 28