ko.observable() trim ending whitespace - javascript

I have an HTML text input called hours that is verified as a number through a knockout extender. If the user entering the hours has any whitespace after the number they enter, I would like to simply have this trimmed without getting notified that what they've entered for hours is not a number. For example, if they enter "2.5 ", this should be correct as I want the extra spaces trimmed automatically. How am I able to do this with what I have below? Thank you.
hours: ko.observable().extend({ number: true, required: true })

You can add a trimming functionality to observables, e.g. adding a custom function to ko.subscribable.fn as explained in another SO post:
ko.subscribable.fn.trimmed = function() {
return ko.computed({
read: function() {
return this();
},
write: function(value) {
this(value.trim());
this.valueHasMutated();
},
owner: this
}).extend({ notify: 'always' });
};
var vm = function () {
this.num = ko.observable().trimmed().extend({ number: true });
this.num(' 2 ');
}
ko.applyBindings(new vm());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout-validation/2.0.3/knockout.validation.min.js"></script>
<input type="text" data-bind="value: num" />
P.S. Don't be tempted to add a trim() call into Knockout validator plugin number rule:
// this is the original 'number' rule implemetation, with a 'trim()' call added to it
ko.validation.rules['number'] = {
validator: function (value, validate) {
if (!validate) { return true; }
return ko.validation.utils.isEmptyVal(value) ||
(validate && /^-?(?:\d+|\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test(value.trim()));
},
message: 'Please enter a number.'
};
... because you don't want the trimming to happen during validation, but much earlier, that is: during the writing.

Related

how to display only the last four digits of an input field in angular material and angular js?

I want to make an md-input field where only the last 4 digits will be displayed, like this,"********1234". I'm new to angular material and angular js, my knowledge is limited only to md-input password yet where all characters are hidden or replaced with "". I made a javascript of this, and it worked! but i don't know what's the equivalent code of this in angular js. Here is my code: `$('#cardNum').bind('input', function () {
$(this).val(($(this).val().replace(/\d(?=\d{4})/g, '')));
});
You can create a custom filter for it:
appModule.filter('passwordMask', function() {
return function (input, num) {
if (isNaN(num) || num < 1) {
return String(input).replace(/./g, '*');
}
var mask = RegExp('(.{1,' + num + '}$)|.', 'g');
return String(input).replace(mask, function(hide, show) {
return show || '*';
});
};
});
And use it this way:
<div ng-bind="passwordModel | passwordMask:4" class="password-style"></div>
Hope it helps.
#Guedes can i use this in an input field?
<input name="password" type="password" ng-bind="user.password | passwordMask:4" />
myapp.controller('controller', function ($scope) {
$scope.user = {
name: '',
password: '',
};
});

jquery validate rules with multiple if/else condition

I want to use jquery validate() rule to 2 cross fields. If either of the field is typed in the other one is also required. Also, once they are required there format for number field should be 1st 15 digits should be integer and date field should be mm/dd/yyyy format and date should be less than todays date.
//..
$("#adjustmentsFormID").validate({
rules: {
refTranNbr: "required",
refTranDate: "required"
},
messages: {
refTranNbr: {
required: function (element) {
if($("#refTranDate").val().length > 0){
return "Please enter the reference transaction number ";
} else if(!refNumChk($("#refTranNbr").val())){
return "Please enter a valid Reference Transaction Number";
} else {
return false;
}
}
},
refTranDate: {
required : function (element) {
var tdate = $("#refTranDate").val();
if($("#refTranNbr").val().length > 0){
return "Please enter a date for the Refering Transaction to complete this transaction.";
}else if((new Date() > new Date(tdate))) {
return "Please enter a reference transaction date less than today's date.";
}else{
return false;
}
}
},
});
..//
In both the cases the 1st condition for required field works. However for refNum field the 2nd condition which has refNumChk isnot working. Actually its not getting called. Similarly for refTranDate required field validation works however date > tDate is not getting checked. Not sure if this method would work or should i do something different for multiple conditions.
Your approach to jQuery validation is wrong, the messages is used to return only the error message in case of an validation error.
So the only validation you are doing is the required validation, you can add custom validation rules to solve this
jQuery(function($) {
jQuery.validator.addMethod("refNumChk", function(value, element, params) {
return this.optional(element) || /^\d{15}[A-Z]$/.test(value);
}, jQuery.validator.format("Enter a value in forat aa-999"));
jQuery.validator.addMethod("lessThanToday", function(value, element, params) {
return this.optional(element) || new Date() > new Date(value);
}, jQuery.validator.format("Value should be less than today"));
$("#adjustmentsFormID").validate({
rules: {
refTranNbr: {
required: true,
//refNumChk: true
pattern: /^\d{15}[A-Z]$/
},
refTranDate: {
required: true,
lessThanToday: true
}
},
messages: {
refTranNbr: {
required: "Please enter the reference transaction number",
pattern: "Please enter a valid Reference Transaction Number"
},
refTranDate: {
required: "Please enter a date for the Refering Transaction to complete this transaction.",
lessThanToday: "Please enter a reference transaction date less than today's date."
},
}
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.13.1/jquery.validate.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.13.1/additional-methods.js"></script>
<form id="adjustmentsFormID" method="post" action="">
<div>
<input name="refTranNbr" />
</div>
<div>
<input name="refTranDate" />
</div>
<input type="submit" value="Save" />
</form>

Validation of multiple table fields using Dynamic Rule

I have a dynamic table, which each row contains country and numberOfState fields. Currently I am able to add new record and validate the country and numberOfState field separately (e.g. required) after click the "AddNewRecord" button, which is below code that generate dynamic table unique field name, i.e. name="country_0", "numberOfState_0" for 1st row, and ="country_1", "numberOfState_1" for 2nd row and etc.
Would like to check whether can validate the dynamic country and numberOfState fields together (i.e. Country is US and NumberOfState must be 50), using dynamic rule code as per below addRowRule function. Thanks in advance.
$(document).ready(function(e){
var rowindex = 0;
$("#AddNewRecord").click(function(){
var row =
"<tr><td>input name=country_"+rowindex+" type=text class='countryRule'/></td>
<tr><td>input name=numberOfState_"+rowindex+" type=text class='stateRule'/></td></tr>";
$("#table").append(row);
rowindex++;
addRowRule(rowindex);
});
jQuery.validate.addClassRules({
countryRule:{required:true},
stateRule:{required:true}
});
$('form').validate();
function addRowRule(i) {
var country = '#country_' + i,
var numberOfState = '#numberOfState_' + i;
$(country).rules('add', {
required: true,
numberOfState:{
required: {
depend: function(element){
if ($(country).val() == 'US' &&
$(numberOfState).val() !=50){
return false;
}
return true;
}
messages: {
numberOfState: "Number of state not match with country",
}
},
messages: {
required: "Required input",
}
});
});
Updated code to share with all:
$( document ).ready(function() {
$("#myForm").validate(); //sets up the validator
var rowindex = 0;
$("#AddNewRecord").click(function(){
var row = "<tr><td>input name=country_"+rowindex+" type=text /></td>" +
"<tr><td>input name=numberOfState_"+rowindex+" type=text /></td></tr>";
$("#table").append(row);
addRowRule(rowindex);
rowindex++;
});
function addRowRule(row_index) {
var country = '#country_' + row_index;
var numberOfState = '#numberOfState_' + row_index;
$(country).rules('add', {
required: true,
messages: {
required: "Pls input country."
}
});
$(numberOfState).rules('add', {
required: true,
checkCountryAndState: [country, numberOfState],
messages: {
required: "Pls input number of state."
}
});
}
jQuery.validator.addMethod("checkCountryAndState", function(value, element, params){
var varCountry = params[0];
var varNumberOfState = params[1];
if ($(varCountry).val() === 'America' && $(varNumberOfState).val() !== 50){
return false;
}
return true;
}, jQuery.format("Country is not match with Number of State."));
});
You can specify validation rules with the rules property. This should do what you specified in the question as an example:
$(".selector").validate({
rules: {
field2: {
required: true,
field1: {
depends: function(element) {
if ($('#field1').val() === 'A' && $('#field2').val() === 'Z') {
return false;
}
return true;
}
}
}
}
});
After this, you need to assign a message if the validation fails with the messages property.
Part of your problem is putting invalid objects inside of the .rules() method. Since the .rules() method is already attached to a selector (representing a SINGLE field), you cannot declare rules for additional fields inside of it...
function addRowRule(i) {
var country = '#country_' + i,
var numberOfState = '#numberOfState_' + i;
$(country).rules('add', {
required: true,
numberOfState: { // <- you can NOT put this here
required: { ...
The only objects allowed inside of .rules() is a key: value list of various rules/methods and/or the messages object.
You would have to attach other fields to different instances of .rules()....
function addRowRule(i) {
var country = '#country_' + i,
var numberOfState = '#numberOfState_' + i;
$(country).rules('add', {
required: true,
....
});
$(numberOfState).rules('add', {
required: true,
....
});
....

jquery validate with a character counter plugin

I have a page that has fields that are validated using jquery-validate plugin, and wanted to include a twitter like character counter on the fields to see how many chars are left
Here is my demo
http://jsfiddle.net/4k1vokgv/1/
$(document).ready(function() {
$(".counter").characterCounter({
counterCssClass: 'text-counter',
limit: 1000,
counterFormat: 'Characters Remaining: %1',
});
var validatorStrat = $("#strategyForm").validate({
rules:{
exampleInputEmail1: {
required: true,
},
ZB_note: {
required: true,
maxlength: 140,
},
ZC_note: {
required: true,
maxlength: 140,
},
},
submitHandler: function(form) {}
});
});
Both character counters work fine until
the issue, when jquery-validate fires a validation error (required, maxlength, etc), the character counter then stops working on any element that has an error.
I do not believe this is an issue with the character counter plugin itself. I think the error generation that jquery validate does somehow causes this.
Anyways, included the full snippet below, any help is greatly appreciated
/**
* Character Counter v1.0
* ======================
*
* Character Counter is a simple, Twitter style character counter.
*
* https://github.com/dtisgodsson/jquery-character-counter
*
* #author Darren Taylor
* #author Email: shout#darrenonthe.net
* #author Twitter: darrentaytay
* #author Website: http://darrenonthe.net
*
*/
(function($) {
$.fn.characterCounter = function(options){
var defaults = {
exceeded: false,
limit: 150,
counterWrapper: 'span',
counterCssClass: 'help-block',
counterFormat: '%1',
counterExceededCssClass: 'exceeded',
onExceed: function(count) {},
onDeceed: function(count) {},
customFields: {},
};
var options = $.extend(defaults, options);
return this.each(function() {
$(this).after(generateCounter());
bindEvents(this);
checkCount(this);
});
function customFields(params)
{
var html='';
for (var i in params)
{
html += ' ' + i + '="' + params[i] + '"';
}
return html;
}
function generateCounter()
{
var classString = options.counterCssClass;
if(options.customFields.class)
{
classString += " " + options.customFields.class;
delete options.customFields['class'];
}
return '<'+ options.counterWrapper +customFields(options.customFields)+' class="' + classString + '"></'+ options.counterWrapper +'>';
}
function renderText(count)
{
return options.counterFormat.replace(/%1/, count);
}
function checkCount(element)
{
var characterCount = $(element).val().length;
var remaining = options.limit - characterCount;
if( remaining < 0 )
{
$(element).next("." + options.counterCssClass).addClass(options.counterExceededCssClass);
options.exceeded = true;
options.onExceed(characterCount);
}
else
{
if(options.exceeded) {
$(element).next("." + options.counterCssClass).removeClass(options.counterExceededCssClass);
options.onDeceed(characterCount);
options.exceeded = false;
}
}
$(element).next("." + options.counterCssClass).html(renderText(remaining));
};
function bindEvents(element)
{
$(element)
.bind("keyup", function () {
checkCount(element);
})
.bind("paste", function () {
var self = this;
setTimeout(function () { checkCount(self); }, 0);
});
}
};
})(jQuery);
$.validator.setDefaults({
errorElement: "span",
errorClass: "help-block",
// validClass: 'stay',
highlight: function (element, errorClass, validClass) {
$(element).addClass(errorClass); //.removeClass(errorClass);
$(element).closest('.form-group').removeClass('has-success').addClass('has-error');
},
unhighlight: function (element, errorClass, validClass) {
$(element).removeClass(errorClass); //.addClass(validClass);
$(element).closest('.form-group').removeClass('has-error').addClass('has-success');
},
errorPlacement: function(error, element) {
if(element.parent('.input-group').length) {
error.insertAfter(element.parent());
} else if (element.hasClass('select2')) {
error.insertAfter(element.next('span'));
} else {
error.insertAfter(element);
}
}
});
$(document).ready(function() {
$(".counter").characterCounter({
counterCssClass: 'text-counter',
limit: 140,
counterFormat: 'Characters Remaining: %1',
});
var validatorStrat = $("#strategyForm").validate({
rules:{
exampleInputEmail1: {
required: true,
},
ZB_note: {
required: true,
maxlength: 1000,
},
ZC_note: {
required: true,
maxlength: 1000,
},
},
submitHandler: function(form) {}
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet"/>
<script src="http://cdn.jsdelivr.net/jquery.validation/1.14.0/jquery.validate.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap-theme.min.css" rel="stylesheet"/>
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css" rel="stylesheet"/>
<script src="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
<form role="form" id="strategyForm">
<div class="form-group">
<label for="exampleInputEmail1" class="control-label">Email address</label>
<input type="email" class="form-control" name="exampleInputEmail1" placeholder="Enter email" />
</div>
<div class="form-group">
<label class="control-label">What amount is to be solicited and when?</label>
<textarea class="form-control counter" rows="1" id="ZB_note" name="ZB_note" ></textarea>
</div>
<div class="form-group">
<label class="control-label">Who will be involved in the soliciation?</label>
<textarea class="form-control counter" rows="1" id="ZC_note" name="ZC_note" ></textarea>
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
I have made a code pen with exactly this functionality here.
Codepen
I will also add and discuss the code here, it really is not that hard.
$( document ).ready(function() {
$('#text').on('keypress', function(e) {
var count = $(this).val().length;
if(count != 0) {
count += 1;
} else {
count = count;
}
$('#characterCount').text(count);
})
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="row">
<div class="col m-2">
<textarea id="text" rows="4" cols="50"></textarea>
</div>
<div class="col m-2">
<textarea id="characterCount" rows="4" cols="50"></textarea>
</div>
</div>
The document.ready function insures the function loads when the DOM is ready. You then have a function that fires whenever a key is pressed on the text area. You then have the count variable that is set to the length of the value of the current text ID. You then set the count to plus one, because it is zero index. You then represent the value on the other text area.
Problem is due to where the error element is being inserted compared to the traverse you use to set the counter text.
In your counter plugin you are looking for next() to set the count display using the following:
$(element).next("." + options.counterCssClass).html(renderText(remaining));
That is based on structure being:
<inputElement/>
<counterDisplay/>
But the validator errorPlacment is doing:
<inputElement/>
<validatorError/>
<counterDisplay/>
So now the plugin next(classSelector) returns no matches
You could simply use nextAll() instead of next() in plugin...or change the errorPlacement to something like :
error.parent().append(element);
Demo using nextAll() in plugin
Finding the reason (Problem)
I believe I've found the problem.
When the a field doesn't pass the validation rules it shows an error.
The error is being append to the DOM as:
This field is required.
When I removed it using the console the counter worked.
That made my wonder - maybe the checkCount function still works but the "output" (the span counter) doesn't.
So on line 72, I added:
console.log(characterCount);
Duplicated that scenario again - and it indeed printed the count.
So the problem is that from some reason - when the " error" appears it conflicts with the " text counter". Please notice, that after start writing again - it seems that the " error" is gone - but the truth is that it still in the DOM, it's just hidden using CSS.
<span id="ZB_note-error" class="help-block" style="display: none;"></span>
Then, I added the following code on Line 92:
console.debug( options.counterCssClass );
console.debug( $(element).next("." + options.counterCssClass).html());
Guess what was the output.
For the first debug line: text-counter (that's good)
For the second debug line: undefined (not good)
How to solve it?
Solution 1: You're using the next() function wrongly.
Description: Get the immediately following sibling of each element in
the set of matched elements. If a selector is provided, it retrieves
the next sibling only if it matches that selector.
When the " error" element is being added, the text-counter field is no longer apply to the next() rule. Consider change it with something like: .parent().find('.text-counter') in case each field+text-counter have a common parent.
Solution 2: When a user starts typing, remove the " error" element from the DOM.

Alerts conditional on model validation

I have an Picture model with various validations:
validates :title, presence: true
validates :caption, presence: true
validates :image, presence: true
validates :price, numericality: { greater_than_or_equal_to: 1, less_than_or_equal_to: 1000 }
validates_size_of :tag_list, :minimum => 3, :message => "please add at least three tags"
The tag list has to be submitted in a specific format: at least three tags, separated by a comma and a space: eg foo, bar, cats
I want to have an alert that tells the user to "please wait, we're uploading your image" - but only AFTER the model has passed ALL of the validations ( before the .save in the controller)
Is there a way of doing this in the controller, which I'd prefer, or do I have to use some javascript like:
$("form#new_picture").on("submit", function () {
if LOTS OF HORRIBLE REGEX ON FORM FIELDS {
MESSAGE HERE
return true;
} else {
return false;
}
});
OR Is there a way of doing this in the model, as part of an after_validation callback?
Any suggestions much appreciated. Thanks in advance.
I would build a JS function to extract the fields that I want to be validated.
Then create a custom AJAX controller action, which:
instantiates a new object with given params
call valid? on it without saving it
Then:
On failure, update the form with error messages
On success, I would return a custom ajax response to display the alert and start POSTing the real object.
I've realised that this isn't really possible through through the model or controller, and resorted to a combination of three validation processes:
Validations in the model
The simpleform client side validations gem - this is v good, it tests validity the moment a form field loses focus - "real time" validation.
And some additional javascript to alert with popups and errors, pasted below.
Hopefully this makes the form virtually un-submittable without the user knowing what's missing.
THE JS SOLUTION
FORM
<form id="new_pic" novalidate>
<p><input type="file" name="file" required></p>
<p><input type="string" name="name" placeholder="Name" required></p>
<p><input type="string" name="tags" placeholder="Tags" data-validation="validateTags"></textarea></p>
<p><textarea name="description" data-validation="validateDescription"></textarea></p>
<p><button type="submit">Submit</button>
</form>
JS
var Validator = function(form) {
this.form = $(form);
}
$.extend(Validator.prototype, {
valid: function() {
var self = this;
this.errors = {};
this.form.find('[required]').each(function() {
self.validateRequired($(this));
});
this.form.find('[data-validation]').each(function() {
var el = $(this),
method = el.data('validation');
self[method].call(self, el);
});
return $.isEmptyObject(this.errors);
},
validateRequired: function(input) {
if (input.val() === '') {
this.addError(input, 'is required');
}
},
validateDescription: function(input) {
if (input.val().length < 64) {
this.addError(input, 'must be at least 64 characters');
}
},
validateTags: function(input) {
var tags = input.val().split(/, ?/);
if (tags.length < 3) {
this.addError(input, 'must have at least 3 tags');
}
},
addError: function(input, error) {
var name = input.attr('name');
this.errors[name] = this.errors[name] || [];
this.errors[name].push(error);
input.after('<span class="error">' + error + '</span>');
}
});
$('form#new_pic').on('submit', function(event) {
event.preventDefault();
var form = $(this),
validator = new Validator(form);
form.find('.error').remove();
if (validator.valid()) {
// continue with upload
alert('Go!');
return true;
} else {
// complain
alert('Stop!');
return false;
}
});

Categories