0

I am implementing simple functionality of arithimatic operators using Javascript in my HTML form. But it is not working. It seems right, but dont know why it is not showing value in desired field. Please have a little look into my following code and guide.

var input = $('[name="ServiceTotalCost"],[name="GrantRequest"],[name="MatchingShare"]'),
  ServiceTotalCost = $('[name="ServiceTotalCost"]'),
  GrantRequest = $('[name="GrantRequest"]'),
  MatchingShare = $('[name="MatchingShare"]');

input.change(function() {
  var ServiceTotalCost = (isNaN(parseInt(ServiceTotalCost.val()))) ? 0 : parseInt(ServiceTotalCost.val());
  var GrantRequest = (isNaN(parseInt(GrantRequest.val()))) ? 0 : parseInt(GrantRequest.val());

  if (ServiceTotalCost > GrantRequest) {
    Difference = ServiceTotalCost - GrantRequest;
    MatchingShare.val(Difference);
  } else if (GrantRequest >= ServiceTotalCost) {
    PercentAge = (GrantRequest * 20) / 100;
    MatchingShare.val(PercentAge);
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="col-md-12">
  <div class="form-group form-float">
    <h2>*Support Requried Through Grant</h2><br>
    <div class="form-line">
      <input type="number" class="form-control" name="ServiceTotalCost" id="ServiceTotalCost" required>
      <label class="form-label">Total cost of the service (Rs.)</label>
    </div>
  </div>
</div>
<div class="col-md-12">
  <div class="form-group form-float">
    <div class="form-line">
      <input type="number" class="form-control" name="GrantRequest" max="500000" id="GrantRequest" required>
      <label class="form-label">Amount of grant requested (Rs.)*</label>
    </div>
  </div>
</div>
<div class="col-md-12">
  <div class="form-group form-float">
    <div class="form-line">
      <input type="number" name="MatchingShare" id="MatchingShare" class="form-control" readonly>
      <label class="form-label">Matching share by the applicant (Rs.)</label>
    </div>
  </div>
</div>
Rory McCrossan
  • 331,213
  • 40
  • 305
  • 339

2 Answers2

3

You're getting this in the console:

Uncaught TypeError: Cannot read property 'val' of undefined

The reason is things like this in the change handler:

var ServiceTotalCost = (isNaN(parseInt(ServiceTotalCost.val()))) ? 0 : parseInt(ServiceTotalCost.val());

That creates a local ServiceTotalCost variable that initially has the value undefined, and then in its initializer tries to use it as an object. (I assume you meant to use the ServiceTotalCost from outside the function, but the local shadows it, making it inaccesible.)

Give your locals different names from the variables the handler closes over that hold the jQuery objects for the fields.

Your code is also falling prey to what I call The Horror of Implicit Globals. By not declaring Difference and PercentAge before assigning to them, you're creating global variables. Instead, declare them.

Finally, the overwhelming convention in JavaScript is that variables start with a lower case letter, so difference rather than Difference, etc. (Also, the "A" in percentage isn't usually capitalized).

For example:

var input = $('[name="ServiceTotalCost"],[name="GrantRequest"],[name="MatchingShare"]'),
  ServiceTotalCost = $('[name="ServiceTotalCost"]'),
  GrantRequest = $('[name="GrantRequest"]'),
  MatchingShare = $('[name="MatchingShare"]');

input.change(function() {
  var cost = (isNaN(parseInt(ServiceTotalCost.val()))) ? 0 : parseInt(ServiceTotalCost.val());
  var request = (isNaN(parseInt(GrantRequest.val()))) ? 0 : parseInt(GrantRequest.val());

  if (cost > request) {
    var difference = cost - request;
    MatchingShare.val(difference);
  } else if (request >= cost) {
    var percentage = (request * 20) / 100;
    MatchingShare.val(percentage);
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="col-md-12">
  <div class="form-group form-float">
    <h2>*Support Requried Through Grant</h2><br>
    <div class="form-line">
      <input type="number" class="form-control" name="ServiceTotalCost" id="ServiceTotalCost" required>
      <label class="form-label">Total cost of the service (Rs.)</label>
    </div>
  </div>
</div>
<div class="col-md-12">
  <div class="form-group form-float">
    <div class="form-line">
      <input type="number" class="form-control" name="GrantRequest" max="500000" id="GrantRequest" required>
      <label class="form-label">Amount of grant requested (Rs.)*</label>
    </div>
  </div>
</div>
<div class="col-md-12">
  <div class="form-group form-float">
    <div class="form-line">
      <input type="number" name="MatchingShare" id="MatchingShare" class="form-control" readonly>
      <label class="form-label">Matching share by the applicant (Rs.)</label>
    </div>
  </div>
</div>

Note that you don't need the conditional operator in lines like this:

var cost = (isNaN(parseInt(ServiceTotalCost.val()))) ? 0 : parseInt(ServiceTotalCost.val());

Since NaN and 0 are both falsy values, you can use || instead to get the same result:

var cost = parseInt(ServiceTotalCost.val()) || 0;

I should note that parseInt may or may not be your best choice for converting to number, though; see my answer here for a rundown of your choices and their pros and cons.

You may not need to parse at all. On modern browsers, you can get the numeric value from the valueAsNumber property. That said, if you need to support older browsers that don't have it, it doesn't help you much:

var cost = ServiceTotalCost.prop("valueAsNumber") || parseInt(ServiceTotalCost.val()) || 0;

But if you don't have to support browsers without support for type="number", then:

var cost = ServiceTotalCost.prop("valueAsNumber");
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • 1
    @RoryMcCrossan - I did mean to say something about that, and kinda forgot. :-) – T.J. Crowder Feb 07 '20 at 11:11
  • @mplungjan - I'm not sure what you mean there...? With an `input` (even `input type="number"`), `val` always returns a string. – T.J. Crowder Feb 07 '20 at 11:15
  • ah, I may have been assuming the implementation has a `valHook` to return a number from type=number. So a blank or a string with a number. – mplungjan Feb 07 '20 at 11:45
  • 1
    @mplungjan - Even so, `valueAsNumber` can be `NaN` (for instance, in a blank field). Good point about `valHook` though. – T.J. Crowder Feb 07 '20 at 11:59
1

Just for completeness sake. Apart from the already answered scope issue and the poor readability of having the same variable names for object and values, the script can be vastly simplified.

$(".form-control").on("change", function() {
  const serviceTotalCosts = +$("#ServiceTotalCost").val(); // converting to number using unary +
  const grantRequestAmount = +$("#GrantRequest").val();
  let matchingShareAmount = 0;
  if (serviceTotalCosts > grantRequestAmount) {
    matchingShareAmount  = serviceTotalCosts - grantRequestAmount;
  } else if (grantRequestAmount >= serviceTotalCosts) {
    matchingShareAmount = (grantRequestAmount  * 20) / 100; // percentage
  }
  $("#MatchingShare").val(matchingShareAmount);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="col-md-12">
  <div class="form-group form-float">
    <h2>*Support Requried Through Grant</h2><br>
    <div class="form-line">
      <input type="number" class="form-control" name="ServiceTotalCost" id="ServiceTotalCost" required>
      <label class="form-label">Total cost of the service (Rs.)</label>
    </div>
  </div>
</div>
<div class="col-md-12">
  <div class="form-group form-float">
    <div class="form-line">
      <input type="number" class="form-control" name="GrantRequest" max="500000" id="GrantRequest" required>
      <label class="form-label">Amount of grant requested (Rs.)*</label>
    </div>
  </div>
</div>
<div class="col-md-12">
  <div class="form-group form-float">
    <div class="form-line">
      <input type="number" name="MatchingShare" id="MatchingShare" class="form-control" readonly>
      <label class="form-label">Matching share by the applicant (Rs.)</label>
    </div>
  </div>
</div>
mplungjan
  • 169,008
  • 28
  • 173
  • 236
  • Your variable naming is atrocious btw - I'd hate to have to read your code on a daily basis – Rob Feb 07 '20 at 10:54
  • @T.J.Crowder see update. Using +str now. We do want 0 when empty – mplungjan Feb 07 '20 at 12:15
  • 1
    That should work on browsers that support `type="number"` (and thus won't have non-valid numbers). I'd still belt-and-braces if it were me. :-) But even IE10 had `type="number"`, so... – T.J. Crowder Feb 07 '20 at 12:23