0

I have the following table with editable fields (columns).

Table image

Any change I make to the sellprice or casecost column fields, I would like the GP column field to update as the user types or changes values. My code is as follows.

function calculate() {

            $('tr #item_q_sellprice').change(function calculate() {
            //$('.productTable tr').on('change','#item_q_sellprice,#item_q_casecost',function calculate() {
            // $("input[type=text]").change(function () {                

                //fields used for calculations
                var newPrice = parseFloat($('tr #item_q_sellprice').val());                    
                var casecost = parseFloat($('tr #item_q_casecost').val());
                var casesize = parseFloat($('tr #item_q_casesize').val());
                var vatrate = parseFloat($('tr #item_productVat').val());

                //formulae
                var netprice = newPrice / (1 + vatrate / 100);
                var unitcost = casecost / casesize;
                var profit = (newPrice / (vatrate / 100 + 1)) - unitcost;

                var grossprofit = (profit / netprice) * 100

                //alert("change price: " + price);
                //Update GP field
                $('#item_grossProfit').val(grossprofit.toFixed(1));

            });

    }

My Html for the rows is as follows

@foreach (var item in Model)
{
    <tr>
        <td onclick="location.href = '@(Url.Action("Index", "Product", new { id = item.q_guid }))'">
            @Html.DisplayFor(modelItem => item.q_description)
        </td>
        <td onclick="location.href = '@(Url.Action("Index", "Product", new { id = item.q_guid }))'">
            @Html.DisplayFor(modelItem => item.q_barcode)
        </td>
        <td>
            € @Html.TextBoxFor(modelItem => item.q_sellprice, "{0:0.00}", new { @class = "calc-list" })
        </td>
        <td>
            € @Html.TextBoxFor(modelItem => item.q_casecost, "{0:0.00}", new { @class = "calc-list" })
        </td>
        <td>
            @Html.TextBoxFor(modelItem => item.grossProfit, new { @class = "calc-list" })
        </td>
        <td onclick="location.href = '@(Url.Action("Index", "Product", new { id = item.q_guid }))'">
            @Html.DisplayFor(modelItem => item.productDepartment)
        </td>
        <td onclick="location.href = '@(Url.Action("Index", "Product", new { id = item.q_guid }))'">
            @Html.TextBoxFor(modelItem => item.productVat, "{0:0.0}",new { @class = "calc-list", @readonly = "readonly" })
        </td>
        <td onclick="location.href = '@(Url.Action("Index", "Product", new { id = item.q_guid }))'">
            @Html.DisplayFor(modelItem => item.q_stocklevel)
        </td>
        <td onclick="location.href = '@(Url.Action("Index", "Product", new { id = item.q_guid }))'">
            @Html.TextBoxFor(modelItem => item.q_casesize, new { @class = "calc-list", @readonly = "readonly" })
        </td>            

    </tr>
}

Now this currently only works for the top row, and not the rest of the table rows. I guess the error will be on my variable lines

var newPrice = parseFloat($('tr #item_q_sellprice').val()); 

How do I make it so any row where these fields (sellprice/casecost) are changed/updated also updates the corresponding GP column?

LavsTo
  • 129
  • 4
  • 19

1 Answers1

1

You use of a foreach loop is generating duplicate id attributes which is invalid html, and the reason your script fails. Using $('tr #item_q_sellprice').val() for example will only ever return the value of the first element with that id.

More importantly, your use of a foreach loop means that your view will never bind correctly. Instead, you need to use a for loop or EditorTemplate - refer Post an HTML Table to ADO.NET DataTable for more detail).

Give the elements your need in the calculations a class name, and then use relative selectors to find the associated elements in the same container (your <tr>).

The view should be (using a for loop)

@for(int i = 0; i < Model.Count; i++)
{
    <tr>
        ....
        @Html.TextBoxFor(m => m[i].q_sellprice, "{0:0.00}", new { @class = "sell-price calc-list" })
        @Html.TextBoxFor(m => m[i].q_casecost, "{0:0.00}", new { @class = "case-cost calc-list" })
        ....
    </tr>
}

Then your script becomes

$('.sell-price, .case-cost').change(function calculate() {
    // Get the containing table row
    var row = $(this).closest('tr');
    var newPrice = Number(row.find('.sell-price').val());
    var casecost = Number(row.find('.case-cost').val());
    ....
    // calculate results
    row.find('.gross-profit').val(grossprofit.toFixed(1))
});

As a side note, you should be checking the the values are valid (both parseFloat and Number() will return NAN if the value entered in the textbox is not valid), for example

if (isNaN(newPrice) || isNaN(casecost) {
    // its invalid and the calculation would fail

In addition, since grossProfit is a calculated value, it should not be generated as a textbox. Instead use a (say) <span> inside the <td> to display the calculated value (and the value should be recalculated on the server in the POST method to protect against malicious users altering the request)