Yii2: Adding two fields and populating third field on the fly - javascript

I am trying to populate a total field with id- #appointment-total_amount using javascript/jquery. Referring this Jsfiddle add two fields together - Which is working fine.
I am using this code in my _form.php
<?php
$script = <<<EOD
$(function() {
$('#appointment-doctor_fee').keyup(function() {
updateTotal();
});
$('#appointment-discount').keyup(function() {
updateTotal();
});
var updateTotal = function () {
var input1 = parseInt($('#appointment-doctor_fee').val());
var input2 = parseInt($('#appointment-discount').val());
$('#appointment-total_amount').text(input1 + input2);
};
});
EOD;
$this->registerJs($script);
?>
But nothing is happening on the page.
I am not able to see what I am missing here.
Thanks.
Related HTML
<div class="form-group field-appointment-doctor_fee">
<label class="control-label" for="appointment-doctor_fee">Doctor Fee</label>
<input type="text" id="appointment-doctor_fee" class="form-control" name="Appointment[doctor_fee]" maxlength="10">
</div>
<div class="form-group field-appointment-discount">
<label class="control-label" for="appointment-discount">Discount</label>
<input type="text" id="appointment-discount" class="form-control" name="Appointment[discount]" maxlength="10">
<div class="form-group field-appointment-total_amount">
<label class="control-label" for="appointment-total_amount">Total Amount</label>
<input type="text" id="appointment-total_amount" class="form-control" name="Appointment[total_amount]" maxlength="10">

The error is in this line:
$('#appointment-total_amount').text(input1 + input2);
Should be:
$('#appointment-total_amount').val(input1 + input2);
Besides that add at least simple check for illegal numbers, because you will get NaN if one of the fields is empty or input value is not valid number. Some range limit will be good too.
var updateTotal = function () {
var doctorFee = parseInt($('#appointment-doctor_fee').val());
var discount = parseInt($('#appointment-discount').val());
var totalAmount = doctorFee + discount;
if (isNaN(totalAmount) || totalAmount < 0 || totalAmount > 100000) {
totalAmount = '';
}
$('#appointment-total_amount').val(totalAmount);
};
One more error is in script registration. Change to this:
use yii\web\View;
$this->registerJs($script, View::POS_END);
Otherwise it will be inserted before jQuery (and your script depends on it) and will not be working.
Registering scripts that way is not good practice, it's even mentioned in official documentation. Separate file and using assets is definitely better than struggling with dependencies and inserting js as string (the errors are hard to detect, no autocomplete, etc.).

Related

Selecting specific element within a div?

I am building a contact form, and I am having problems with jQuery. I want to select specific input fields that have an error and apply the class err. Unfortunately, my code selects all inputs when there is an error. I am having trouble identifying which part of my logic is wrong.
$('#send_mail').click(function(){
$("#contact_body").find('label').each(function(){
var contact_label = $('input[required=true], textarea[required=true]');
var label_check = $(this).find(contact_label);
$(contact_label).removeClass('err');
if (!$.trim($(label_check).val())){
$(contact_label).addClass('err');
}
});
});
The order of my HTML goes something like so:
#contact_body
<label>
<input>
</label>
This selects all input and textarea elements:
var contact_label = $('input[required=true], textarea[required=true]');
Instead, you should restrict it to the elements within the label:
var contact_label = $(this).find('input[required=true], textarea[required=true]');
Note that $(contact_label) and contact_label are equivalent in your code, as well as $(label_check) and label_check.
Also, you can use the state parameter of toggleClass() to simplify this:
contact_label.removeClass('err');
if (!$.trim(label_check.val())){
contact_label.addClass('err');
}
… to this:
contact_label.toggleClass('err', !$.trim(label_check.val()));
Here's the updated event:
$('#send_mail').click(function(){
$('#contact_body').find('label').each(function(){
var contact_label = $(this).find('input[required=true], textarea[required=true]');
var label_check = $(this).find(contact_label);
contact_label.toggleClass('err', !$.trim(label_check.val()));
});
});
I think your original code would work if you just changed this line:
$(contact_label).addClass('err');
To this:
$(label_check).addClass('err');
Because $(contact_label) references all the required inputs, whereas $(label_check) references only the input being checked.
But your code could be simplified, and you make unnecessary calls to $(), giving it an argument that is already a JQuery object.
I also do not see that you need to loop through the labels. You could loop through the required inputs instead.
$('#send_mail').click(function(){
$("#contact_body").find(':input[required]').each(function() {
var $input = $(this);
$input.removeClass('err');
if (!$.trim($input.val())){
$input.addClass('err');
}
});
});
Which could be shortened by using the .toggleClass() function:
$('#send_mail').click(function(){
$("#contact_body").find(':input[required]').each(function() {
$(this).toggleClass('err', !$.trim($input.val()));
});
});
Notes:
The selector ':input' matches <input>, <select> and <textarea> elements.
This is a slightly different approach. Gives a bit more flexibility.
arr = ['first', 'last', 'email', 'msg']; //IDs of fields to check
$('#send_mail').click(function(){
$('input, textarea').removeClass('err');
for (var i=0; i<arr.length-1; i++) { //Loop through all field IDs
if ( $('#'+arr[i]).val() == '' ) {
$('#'+arr[i]).addClass('err').focus();
return false;
}
}
//AJAX to send email goes here
alert('Email sent');
});
.err{background:yellow;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<label for="first">First Name:</label>
<input id="first" type="text" required /><br>
<label for="last">Last Name:</label>
<input id="last" type="text" required/><br>
<label for="email">Email:</label>
<input id="email" type="email" required /><br>
<label for="msg">Message:</label>
<textarea id="msg" required></textarea>
<button id="send_mail">Send</button>
you can simplify the code, there will be less mistakes:
$('#send_mail').click(function(){
$("#contact_body").find('label').each(function(){
var field = $(this).find('[required=true]');
if ($.trim($(field).val())){
$(this).removeClass('err');
}
});
});

Javascript keyup function throwing wrong alert message

With the following script, i am trying to validate whether the refund amount wlt_ln_refund_amt is greater than the balance amount wlt_ln_bal using keyup function.
In my html read only field wlt_ln_bal (field type = number) i have a an amount 222.00
the other field wlt_ln_refund_amt (field type = number)
The testcase
for the value 3 the system is throwing an error message like "Refund amount Rs.3 is greater than Balance Rs.222.
for the values 1, 2 or 2000 the system is not throwing any errors
Here is my html code:
<form id="lnrefund" name="lnrefund"
method="post" role="form"
class="form-horizontal"
action="<?php echo $_SERVER['PHP_SELF']; ?>"
onsubmit="return (checkform() && confirm_update())">
<div class="form-group">
<label class="col-md-2 col-xs-12 control-label">Loan Balance</label>
<div class="col-md-3 col-xs-12">
<input id="wlt_ln_bal" Name="wlt_ln_bal"
type="number"value ="<?php echo $bal ?>"
class="form-control required" readonly/>
<span class="help-block">Required</span>
</div>
</div>
<label class="col-md-2 col-xs-12 control-label">Refund Amount</label>
<div class="col-md-3 col-xs-12">
<input id="wlt_ln_refund_amt"
Name="wlt_ln_refund_amt"type="number" step="0.01"
class="form-control" required/>
<span class="help-block">Required</span>
</div>
</form>
And this is the javascript
<script type="text/javascript">
$(function(){
$("#wlt_ln_refund_amt").keyup(function () {
var ref = document.lnrefund.wlt_ln_refund_amt.value;
var bal = document.lnrefund.wlt_ln_bal.value;
if (ref>bal)
{
alert('Refund amount Rs.'+ref+ '\nis greater than Available Balance Rs.'+bal)
return true;
}
});
});
</script>
It looks like the variables are being compared as strings (i.e. alphabetically) you should try something like
var ref = parseInt(document.lnrefund.wlt_ln_refund_amt.value);
var bal = parseInt(document.lnrefund.wlt_ln_bal.value);
or maybe
var ref = parseFloat(document.lnrefund.wlt_ln_refund_amt.value);
var bal = parseFloat(document.lnrefund.wlt_ln_bal.value);
if you're expectiong decimals
Since you asked for suggestions... :P
I'd use jQuery to get the values of the two inputs. You're already using jQuery for the document ready function, so why not use:
var $refund = $('#wlt_ln_refund_amt'),
$balance = $('#wlt_ln_bal.value');
What you're doing works fine - as long as the structure of your HTML never changes. Using jQuery like this means you don't have to worry about ever wrapping your inputs in a containing DIV or changing the form to a popup dialog later on.
Next, I wouldn't use the keyup event, I'd use the blur event. Perhaps your use case requires the check after every keystroke, but that usually annoys users. If you bind to the blur instead of the keyup, your user will have an opportunity to correct a mistake during typing before getting yelled at by your function.
$refund.on('blur', function(){
var refAmount = parseInt($refund.val()),
balAmount = $balance.val() * 1;
if (refAmount > balAmount)
{
alert('Refund amount Rs.' +
refAmount +
'\nis greater than Available Balance Rs.' +
balAmount);
$refund.focus();
}
});
As someone else suggested, make sure the values you're comparing are numeric. You can use the parseInt as suggested (the preferred way) or force type conversion by multiplying the value by 1. Either way will result in a NaN (not a number) if the user enters something other than numbers.
After the alert, I'd return focus back to the refund amount to give the user another shot at the entry.
As a final suggestion, I'd recommend using readable variable names. Perhaps you shortened them just for this question, but descriptive variable names are much easier to deal with than obscure abbreviations.
Good luck!

Kendo UI Validator - Handling inputs with identical name attributes

I currently have a form similar to the below:
<form action="/" method="post" id="myForm">
<div class="row">
<input type="text" name="rowValue" class="rowValue">
</div>
<div class="row">
<input type="text" name="rowValue" class="rowValue">
</div>
<div class="row">
<input type="text" name="rowValue" class="rowValue">
</div>
<div class="row">
<input type="text" name="rowValue" class="rowValue">
</div>
<input type="submit" value="Submit">
</form>
A little background: JS is used to inject X amount of new "rows" into the form.
I tried using:
var myForm = $('#myForm').kendoValidator({
/* rules/messages go here*/
}).data('kendoValidator');
myForm.validate();
I only get one error message showing up on the first input[name='rowValue'].
JS Fiddle
My suspicion is Kendo Validator needs unique name attributes to validate correctly. This is a shame, since lots of backend languages have the ability to accept identical name attributes, as they concatenate the values or convert them into an array or collection (ASP.NET).
Is there a way to have Kendo UI Validator validate form fields with identical name attributes?
Your suspicion is correct. You could adjust the validator for your use case like this:
kendo.ui.Validator.prototype.validateInput = function (input) {
input = $(input);
var that = this,
template = that._errorTemplate,
result = that._checkValidity(input),
valid = result.valid,
className = ".k-invalid-msg",
fieldName = (input.attr("name") || ""),
lbl = input.parent().find("span" + className).hide(),
messageText;
input.removeAttr("aria-invalid");
if (!valid) {
messageText = that._extractMessage(input, result.key);
that._errors[fieldName] = messageText;
var messageLabel = $(template({
message: messageText
}));
that._decorateMessageContainer(messageLabel, fieldName);
if (!lbl.replaceWith(messageLabel).length) {
messageLabel.insertAfter(input);
}
messageLabel.show();
input.attr("aria-invalid", true);
}
input.toggleClass("k-invalid", !valid);
return valid;
};
Note that there are a few simplifications in this method, so it may break on certain corner cases.
(demo)

Creating a form entry calculator with jQuery

I am trying to build a calculator using form entries as the figures to be calculated and returned to corresponding form fields in another field set. I am fairly new to jQuery and I am following the advice of a book but I must be honest, I do need to rush this a bit and I'm having some problems.
Here is the jfiddle if you need to visualise it: http://jsfiddle.net/u3xx4ubv/14/
EDIT: Not sure on the appropriate form action for this, which may be causing some problems.
HTML:
<form action="" method="get" id="ATSCalc">
<fieldset>
<legend>Return on Investment Calculator</legend>
<label for="aptsBooked" class="label">Avg. Appointments Booked</label>
<input name="aptsBooked" type="text" id="aptsBooked">
<br />
<label for="aptsAttended" class="label">Avg. Appointments Attended</label>
<input name="aptsAttended" type="text" id="aptsAttended">
<br />
<label for="reqMeetings" class="label">Meetings per deal</label>
<input name="reqMeetings" type="text" id="reqMeetings">
<br />
<label for="orderVal" class="label">Avg. order value</label>
<input name="reqMeetings" type="text" id="reqMeetings">
<br />
<input type="submit" value="Submit" />
</fieldset>
<br />
<fieldset>
<legend>Results</legend>
<label for="convRate" class="label">Appointment conversion rate</label>
<input name="convRate" type="text" id="convRate">
<br />
<label for="meetingsPerDeal" class="label">No. of meetings per deal</label>
<input name="meetingsPerDeal" type="text" id="meetingsPerDeal">
<br />
<label for="meetingVal" class="label">Value of each meeting</label>
<input name="meetingVal" type="text" id="meetingVal">
<br />
</fieldset>
</form>
JavaScript:
$(document).ready(function(){
$('#ATSCalc').submit(function(){
var aptsBooked = $('#aptsBooked').val();
var aptsAttended = $('#aptsAttended').val();
var reqMeetings = $('#reqMeetings').val();
var orderVal = $('#orderVal').val();
//Collects values of form input
var aptsConvRate = aptsAttended / aptsBooked * 100;
$('#convRate').val(aptsCovRate); //Outputs meeting conversion rate to form field.
var meetingsPerDeal = aptsBooked / reqMeetings;
$('#meetingsPerDeal').val(meetingsPerDeal); //Outputs meeting per deal value to form field.
var meetingVal = orderVal / meetingsPerDeal;
$('#meetingVal').val(meetingVal); //Outputs value of each meeting to form field.
//Variables for calculation of Return on Investment figures
}}; // end submit()
}}; // end ready ()
I am trying to run the second group of variables and return the outputs in the fields seen in the second fieldset. I imagine it is just syntax errors or misplaced code but I am really struggling to fix it. Any help would be much appreciated.
Fiddle example
You missed
}}; instead of }); as your closing brackets
variable name aptsCovRate instead of aptsConvRate ( missing n )
$(document).ready(function () {
$('#ATSCalc').submit(function (e) {
e.preventDefault();
var aptsBooked = parseInt($('#aptsBooked').val(), 10);
var aptsAttended = parseInt($('#aptsAttended').val(), 10);
var reqMeetings = parseInt($('#reqMeetings').val(), 10);
var orderVal = parseFloat($('#orderVal').val());
//Collects values of form input
var aptsConvRate = aptsAttended / aptsBooked * 100;
$('#convRate').val(aptsConvRate); //Outputs meeting conversion rate to form field.
/* ^^^^ Conv !! not Cov */
var meetingsPerDeal = aptsBooked / reqMeetings;
$('#meetingsPerDeal').val(meetingsPerDeal); //Outputs meeting per deal value to form field.
var meetingVal = orderVal / meetingsPerDeal;
$('#meetingVal').val(meetingVal); //Outputs value of each meeting to form field.
//Variables for calculation of Return on Investment figures
}); // end submit()
}); // end ready ()
Also, it would be good to use parseInt() (value to integer Docs) with radix parameter and parseFloat() (value to floating-point number Docs) methods while getting your Number values from your inputs.
To round your results take a look at the toFixed() method.
There are a few things wrong with your javascript/HTML, and a few things you missed.
$('#convRate').val(aptsCovRate); should be $('#convRate').val(aptsCo**n**vRate);
The end submit and end ready should be }); not }};
You do not have a HTML input with orderVal as an ID. It needs to be changed.
Finally, what you missed - your form will submit and refresh and you'll lose the calculated values. You can fix this by using e.stopPropagation(); and e.preventDefault();
Updated fiddle should be working for you: http://jsfiddle.net/u3xx4ubv/17/

Dynamic fields params inside the controller

I have a form containing some dynamically added date fields using jQuery, In order to handle their params submitted I need to count the number of those fields, then inside the controller I can make a loop based on that number:
application.js
$(function() {
var scntDiv = $('#foo');
var i = $('#foo div.bar').size();
$(document).on('click', '#addField', function() {
$('<div class="bar"><input type="text" id="start" name="start_'+ i +'"><input type="text" id="end" name="end_'+ i +'"></div>').appendTo(scntDiv);
i++;
return false;
});
Saying I added n input, then the html output will be:
<input type="text" id="start" name="start_1">
<input type="text" id="end" name="end_1">
.
.
.
<input type="text" id="start" name="start_n">
<input type="text" id="end" name="end_n">
My question is, how to access that "n" inside my controller?
I have to store all the dates range in one array then create multiple records based on each date.
Another option, close to how nested_attributes are implemented (not the most beautiful javascript you'll see out there, but you get the spirit) :
$(function() {
var $scntDiv = $('#foo');
$('#addField').click( function(){
var index = $scntDiv.find( '.bar' ).size();
$(
'<div class="bar">' +
'<input type="text" class="start" name="ranges[' + index + '][start]">' +
'<input type="text" class="end" name="ranges[' + index + '][end]">' +
'</div>'
).appendTo( $scntDiv );
return false;
});
});
This will build a hash like this :
>> params[:ranges]
=> {"0" => {"start" => "a_date_string", "end" => "another_date_string"},
"1" => {"start" => "a_date_string", "end" => "another_date_string"} }
it's then easy to process this hash, even using mass assignment :
class MyClass
def ranges= ranges
ranges.each do |*,attributes|
# whatever logic pleases you, self.ranges.build( attributes ) for instance
end
end
end
however, if you go this way, you better use directly nested_attributes, except if you have a very convoluted logic to perform. Nested attributes come with a lot of goodies like auto instantiation of child records, record deletion management, auto-rejection of some attributes if needed, they work well with the fields_for form helper, etc. It's native to rails, so why bother and reinvent the wheel ?
This will help.
$(function() {
var scntDiv = $('#foo');
$('#addField').click(function(){
$('<div class="bar"><input type="text" id="start" name="start[]"><input type="text" id="end" name="end[]"></div>').appendTo(scntDiv);
return false;
});
});
This will send the params like below. for example
"start"=>["1", "2", "3"], "end"=>["11", "22", "33"]

Categories