1

I have a page which can have multiple rows with 4 input fields on each, like this

1.Number of units 2.Unit price 3.Discount rate 4.Total

I'd like to perform calculations based on the field values. As the user enters values I'd like to update the fourth field (total) depending on the values entered in the boxes, the fourth field shall not be editable by the user. If the discount rate is empty or that field shall be ignored.

To clarify, the total field shall only be updated once there are values in both the amount and price fields. Also the total field shall display the total with the discount deducted if a number is entered into the discount field.

Unfortunately I have no clue and am hoping for some help here.

The rows on the page can vary, the user can add/delete rows to the page (this is already in place)

the html looks like below, and here is a jsfiddle

<div class="row">
  <div>
    <input class="form-control" id="num-of-prods-1" type="text" placeholder="Number of units">
  </div>
  <div>
    <input class="form-control" id="prod-unit-price-1" type="text" placeholder="Unit price">
  </div>
  <div>
    <input class="form-control" id="discount-rate-1" type="text" placeholder="Discount rate %">
  </div>
  <div>
    <input class="form-control" id="order-amount-1" type="text" placeholder="Total">
  </div>
</div>

<div class="row">
  <div>
    <input class="form-control" id="num-of-prods-2" type="text" placeholder="Number of units">
  </div>
  <div>
    <input class="form-control" id="prod-unit-price-2" type="text" placeholder="Unit price">
  </div>
  <div>
    <input class="form-control" id="discount-rate-2" type="text" placeholder="Discount rate %">
  </div>
  <div>
    <input class="form-control" id="order-amount-2" type="text" placeholder="Total">
  </div>
</div>
user1425385
  • 127
  • 2
  • 10

3 Answers3

2

The html has been simplified, the ids have been removed, classes have been added, and the disabled attribute has been set on .total. These things were done so that more rows could be handled without having to assign new ids, so that the calculation could be triggered per row, and so that total wouldn't be editable by the user, but would stay an input field.

<div class="row">
    <input class="units" type="text" placeholder="Number of units"/>
    <input class="price" type="text" placeholder="Unit price"/>
    <input class="rate" type="text" placeholder="Discount rate %"/>
    <input class="total" type="text" placeholder="Total" disabled/>
</div>
<div class="row">
    <input class="units" type="text" placeholder="Number of units"/>
    <input class="price" type="text" placeholder="Unit price"/>
    <input class="rate" type="text" placeholder="Discount rate %"/>
    <input class="total" type="text" placeholder="Total" disabled/>
</div>

This allows the following JavaScript to work:

function calculate(units, price, discount) {
    return units * price * ((discount) ? discount : 1);
}

$('input').on('change', function () {
    var scope = $(this).parent('.row'),
        units = $('.units', scope).val(),
        price = $('.price', scope).val(),
        discount = $('.rate', scope).val(),
        total = $('.total', scope);
    if ($.isNumeric(units) && $.isNumeric(price) && ($.isNumeric(discount) || discount === '')) {
        total.val(calculate(units, price, discount));
    }
});

The JavaScript used sets up a function for the calculation which allows the discount rate to either be set or to be optional.

After that is set up we can set up a jQuery call for any time an input is changed. Once it is we get row that was changed into scope and use that to filter when we find each field.

The if statement checks to see if there are numbers present for units and price and if the discount rate is either a number or empty. If these conditions are met then it replaces the content of .total with the result of calculation.

see http://jsfiddle.net/RtN4e/6/

edited per comments

The HTML is now:

<div class="row">
    <div>
        <input class="units" type="text" placeholder="Number of units"/>
    </div>
    <div>
        <input class="price" type="text" placeholder="Unit price"/>
    </div>
    <div>
        <input class="rate" type="text" placeholder="Discount rate %"/>
    </div>
    <div>
        <input class="total" type="text" placeholder="Total" disabled/>
    </div>
</div>

and the JavaScript is now:

function calculate(units, price, discount) {
    if (discount) {
        if (discount < 0 || discount > 100) {
            discount = 0;
        }
    } else {
        discount = 0;
    }
    return (units * price) * (1 - (discount / 100));
}

$('input').on('change', function () {
    var scope = $(this).closest('.row'),
        units = $('.units', scope).val(),
        price = $('.price', scope).val(),
        discount = $('.rate', scope).val(),
        total = $('.total', scope);
    if ($.isNumeric(units) && $.isNumeric(price) && ($.isNumeric(discount) || discount === '')) {
        total.val(calculate(units, price, discount));
    } else {
        total.val('');
    }
});

This incorporates the discount calculation that Luxelin suggested along with a simplified set of limits that remove any discount if it is out of range and changes .parent() to .closest() to allow for wrapping <div> elements on each input.

Community
  • 1
  • 1
Jason Aller
  • 3,541
  • 28
  • 38
  • 38
  • 1
    One problem with the discount part. You want them to be able to type in, say, `25%` for the item to cost only 75% of the original price. Yours doesn't do that. You should change the second line to `return units * price * ((discount) ? (1 - discount/100) : 1);` – royhowie Apr 28 '14 at 00:03
  • If that functionality is added then limit checks, to prevent a 150% discount for instance, should also be put in place at the same time. I didn't feel like the OP was clear enough about how they wanted the discount implemented. By separating out the calculation function from the interface I was setting it up so that it would be easier to unit test. – Jason Aller Apr 28 '14 at 00:08
  • 1
    I suppose this would work: `return units*price*((discount && discount <= 100)? (1-discount/100):1);` – royhowie Apr 28 '14 at 00:10
  • At that point I'd probably set up the logic prior to the return and check for both greater than or equal to 0 and less than or equal to 100. Your suggestions are improvements, but we are both speculating, though not unreasonably, what the OP had in mind at this point. – Jason Aller Apr 28 '14 at 00:15
  • I want the last field to display the total for the customer to pay. say he buys 10 units at $10 at a 10% discount he will pay 90 which is what I'd like in the total field – user1425385 Apr 28 '14 at 01:32
  • I can not simplify the html like you have done, all the inputs have to be surrounded with divs and then the above code no longer works, how can I fix that? – user1425385 Apr 28 '14 at 01:54
  • It can be modified to both handle the discount like @Luxelin was showing and to work with HTML closer to your original format. What portions of the HTML can't be changed? – Jason Aller Apr 28 '14 at 02:46
  • I am having a new problem, the calculations work fine on the elements which are available on page load, but does not work on the rows added by clicking a button in the gui which uses the jquery clone and bumps the id of the new row with +1 so that prow-row_1 becomes prod-row_2 on the newly generated row. Is there a way to make it work on the added elements as well? – user1425385 Apr 28 '14 at 22:35
  • 1
    If you look at http://stackoverflow.com/questions/8021436/turning-live-into-on-in-jquery it shows how to modify the `.on()` call so that it will handle items added to the dom after it has been called. – Jason Aller Apr 28 '14 at 22:40
0

You're going to want to use the keyup, parseFloat(To get numbers with a decimal) and isNaN to check if it's a number or not.

    $(".form-control").keyup(function(e) {
   var num = parseFloat(String.fromCharCode(e.which));
    if(!isNaN(num)) {
        if(num == 0) {
             $(this).val("You entered 0!");   
        }
    }
});

If you want to ignore a textbox add something like data-editable="false" and check it with the .data method.

Try it out: http://jsfiddle.net/Zyneak/AGAj8/

Jordan Schnur
  • 1,225
  • 3
  • 15
  • 30
0

you can check out the fiddle: http://jsfiddle.net/RtN4e/3/

$('#inputFormID').blur(function(e) {
    var txtVal = parseFloat($(this).val());
    $('#outputF').val(txtVal);
});

since you want to be able to add more rows programmatically, you'll just have to include class in you input tag and concatinate the class to the id referenceing.

ibonly
  • 133
  • 1
  • 3
  • 10
  • this one seems not to handle the calculations of the discount rates correctly. Seems the value entered into the discount rate is transferred to the total field. Instead of the value of the total field beeing the sum of (amount*price -discount) Also, if possible it would be great if the total field does not get updated unless there are values in both the amount and price fields – user1425385 Apr 27 '14 at 23:28
  • 1
    this is just to show you how to go by it, it depends on how you do your calculation – ibonly Apr 27 '14 at 23:31