JavaScript event, not updating knockout observable/model value - javascript

I have text field which is wired up with Keyup & change event, to trim the field length.
#Html.TextBoxFor(m => m.zipCode, new {data_bind = "textInput: zipcode, event: { keyup: trimField, change: trimField }", maxlength = "5"})
Trim function,
function trimField(data, event) {
var obj = event.target;
var maxlength = parseInt(obj.getAttribute('maxlength'), 10);
obj.value = obj.value.substring(0, maxlength);
obj.focus();
return true;
}
If I type "123456", on the UI it shows "12345", but the model has "123456".
How to get model updated after the keyup event?

You are not updating the observable variable which is bound to your element. It is better to make it generic as an observable extend so it can be used everywhere based on your max-length and to make sure it follows your rule for an initial value.
Example : https://jsfiddle.net/kyr6w2x3/55/
HTML:
<input data-bind='textInput: zipCode' />
<div>
zip code in Model:<span data-bind="text:zipCode"></span>
</div>
JS:
function AppViewModel(input) {
this.zipCode = ko.observable(input).extend({ maxLength:5});
this.phone = ko.observable(input).extend({ maxLength:11});
}
ko.extenders.maxLenght = function(target, characters) {
//you can use this to show an error message on view
// target.validationMessage = ko.observable();
//define a function to do validation for maxLength
function validate(newValue) {
var maxlength = parseInt(characters, 10);
if(newValue){
target(newValue.substring(0, maxlength) );
}
}
//initial validation
validate(target());
//validate whenever the value changes
target.subscribe(validate);
//return the original observable
return target;
};
ko.applyBindings(new AppViewModel("12345678910"));

change maxlength from 5 to 6:
#Html.TextBoxFor(m => m.zipCode, new {data_bind = "textInput: zipcode, event: { keyup: trimField, change: trimField }", maxlength = "6"})

Stop modifying the DOM. That's Knockout's job. You just modify the data item and Knockout will ensure that the UI is right.
function trimField(data, event) {
var obj = event.target;
var maxlength = parseInt(obj.getAttribute('maxlength'), 10);
data.zipcode(data.zipcode().substr(0, maxlength));
return true;
}

Related

Conditional jQuery validation based on custom attribute

I'd like to enable/disable buttons on key up change conditionally based on a custom data attribute that matches between an input and a button. I've solved it with just one input, but it seems that when I add another one in the mix, the buttons don't seem to enable.
Furthermore, I have a hunch that it's because of .each() but I can't put my finger on it.
Here's the CodePen I've tried and failed on
var validation = $('[data-validation]');
var validate;
validation.on("change keyup", function (e) {
let validated = true;
validation.each(function () {
let value = this.value;
validate = $(this).data('validation');
if (value && value.trim() != "") {
validated = false;
} else {
validated = true;
return false;
}
});
if (validated) {
$('[data-validator=' + validate + ']').prop("disabled", true);
} else {
$('[data-validator=' + validate + ']').prop("disabled", false);
}
});
The key here is to only run your validation code for the input that was changed. As opposed to what you have, which is to run for all inputs.
To get the input that actually changed, you can utilize the .target property of the event object passed to the event handler.
Alternatively, if you remove the validation.each() entirely, it also works. That is because jQuery sets the value of this to be the DOM element (not a jQuery-wrapped element) that actually triggered the event.
var validation = $("[data-validation]");
var validate;
validation.on("change keyup", function (e) {
let validated = true;
let value = this.value;
validate = $(this).data("validation");
if (value && value.trim() != "") {
validated = false;
} else {
validated = true;
return false;
}
if (validated) {
$("[data-validator=" + validate + "]").prop("disabled", true);
} else {
$("[data-validator=" + validate + "]").prop("disabled", false);
}
});

Prevent value to be inserted?

I'm doing a js function that will prevent some input data to be inserted within an input textbox (only DDDDDD,DD for example):
// decimal
$('.is-decimal').keydown(function (e) {
// prevent to insert non-decimal number (number with comma, max 2 digits after comma)
var elem = $(this);
var value = elem.val();
var regex = /^\d+,?\d{0,2}$/;
if (!regex.test(value)) {
e.preventDefault();
}
});
but I can't use keydown: first time is empty, so it won't check the following char.
Which method should I use? keyup is later (so it's just inserted); the same with .on("input propertychange", function ()
EDIT: my best attempt:
// decimal
$('.is-decimal').on("input propertychange", function (e) {
var elem = $(this);
var value = elem.val();
// prevent to insert non-decimal number (number with comma, max 2 digits after comma)
var regex = /^\d+,?\d{0,2}$/;
if (!regex.test(value)) {
elem.val(elem.attr('data-actual-value'));
e.preventDefault();
} else {
elem.attr("data-actual-value", value);
}
});
You define a lastValidValue can use keyup and change events to check the newly entered value and if the new value is invalid change it back to the lastValidValue.
Note: You modify the value without triggering keyup and keydown e.g. right-click and paste
Here's an example
let decField = document.querySelector('#decimal')
let lastValidValue = ''
const regex = /^\d+,?\d{0,2}$/;
const eventHandler = e => {
if( event.target.value.match(regex) ) {
lastValidValue = event.target.value
}
else {
event.target.value = lastValidValue
}
}
decField.addEventListener('keyup', eventHandler)
decField.addEventListener('change', eventHandler)
Edit: here's an example with multiple fields using data-regex attribute to have individual regexes
const eventHandler = e => {
let regex = new RegExp(event.target.dataset.regex);
if( regex.test(event.target.value) ) {
event.target.dataset.lastValidValue = event.target.value
}
else {
event.target.value = event.target.dataset.lastValidValue || ''
}
}
document.querySelectorAll('input[data-regex]').forEach(field => {
field.addEventListener('keyup', eventHandler);
field.addEventListener('change', eventHandler);
})
<input type="text" data-regex="^\d+,?\d{0,2}$" placeholder="digits" />
<input type="text" data-regex="^[\w\s]+$" placeholder="text" />
<input type="text" data-regex="^\W+$" placeholder="special chars" />

How I can watching to change input value for validation form? [duplicate]

This question already has answers here:
JQuery: detect change in input field [duplicate]
(5 answers)
Closed 6 years ago.
How I can watching to change input value for validation form?
I have validation script for my form.
This function work normal, but it start always only after click.
How I can watching when change input.val()
form# send - form
input(type = "text"
data - validate = "name")
button(class = "form_btn")
function validator(el, validName) {
var mask_name = /^[A-Za-z0-9 ]{3,20}$/;
var el = el;
if (validName == "name") {
var el_val = $(el).val();
if (mask_name.test(el_val)) {
$(el).removeClass('error');
} else {
$(el).addClass('error');
}
}
});
function mainValidation() {
if ($('#send-form')) {
$('input').each(function() {
var this_el = $(this);
var validName = $(this).data("validate");
validator(this_el, validName);
});
}
};
$('.form_btn').on('click', function(e) {
e.preventDefault();
mainValidation();
});
Simply set up an event handler for the input event of the input field in question.
// You need to provide a selector that references the input field properly
$('inputSelectorHere').on('input', mainValidation);
You could also wait until the user leaves the input field to do the validation by using the blur event:
// You need to provide a selector that references the input field properly
$('inputSelectorHere').on('blur', mainValidation);
You can bind to the input change event:
$('input').on('change', function() {
var this_el = $(this);
var validName = $(this).data( "validate" );
validator( this_el, validName );
});
Try:
('input').on('change', function() {
mainValidation();
});
Now you are listening the changes on all the input fields. When a field change, it will call your function mainValidation().
Let me know.
Cheers

Detect input value change with MutationObserver

I want to detect when text/value change in input field. Even if I change the value with js, I want to detect that changes.
Here's what I've tried so far in demo in fiddle.
HTML:
<input type="text" id="exNumber"/>
JavaScript:
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
// console.log('Mutation type: ' + mutation.type);
if ( mutation.type == 'childList' ) {
if (mutation.addedNodes.length >= 1) {
if (mutation.addedNodes[0].nodeName != '#text') {
// console.log('Added ' + mutation.addedNodes[0].tagName + ' tag.');
}
}
else if (mutation.removedNodes.length >= 1) {
// console.log('Removed ' + mutation.removedNodes[0].tagName + ' tag.')
}
}
if (mutation.type == 'attributes') {
console.log('Modified ' + mutation.attributeName + ' attribute.')
}
});
});
var observerConfig = {
attributes: true,
childList: false,
characterData: false
};
// Listen to all changes to body and child nodes
var targetNode = document.getElementById("exNumber");
observer.observe(targetNode, observerConfig);
To understand what is going on is necessary to clear up the difference between attribute (content attribute) and property (IDL attribute). I won't expand on this as in SO there are already excellent answers covering the topic:
Properties and Attributes in HTML
.prop() vs .attr()
What is happening behind .setAttribute vs .attribute=?
When you change the content of a input element, by typing in or by JS:
targetNode.value="foo";
the browser updates the value property but not the value attribute (which reflects the defaultValue property instead).
Then, if we look at the spec of MutationObserver, we will see that attributes is one of the object members that can be used. So if you explicitly set the value attribute:
targetNode.setAttribute("value", "foo");
MutationObserver will notify an attribute modification. But there is nothing like properties in the list of the spec: the value property can not be observed.
If you want to detect when an user alters the content of your input element, the input event is the most straightforward way. If you need to catch JS modifications, go for setInterval and compare the new value with the old one.
Check this SO question to know about different alternatives and its limitations.
I've modified Shawn's method a little and wanted to share it. Can't believe there's actually a solution to this.
Type into the input box to see the default behavior. Now, open the DevTools and select the input element, then change its value, e.g. $0.value = "hello". Examine the UI vs. API difference. It seems UI interactions do not modify value property directly. If it were, it would also log "...changed via API...".
let inputBox = document.querySelector("#inputBox");
inputBox.addEventListener("input", function () {
console.log("Input value changed via UI. New value: '%s'", this.value);
});
observeElement(inputBox, "value", function (oldValue, newValue) {
console.log("Input value changed via API. Value changed from '%s' to '%s'", oldValue, newValue);
});
function observeElement(element, property, callback, delay = 0) {
let elementPrototype = Object.getPrototypeOf(element);
if (elementPrototype.hasOwnProperty(property)) {
let descriptor = Object.getOwnPropertyDescriptor(elementPrototype, property);
Object.defineProperty(element, property, {
get: function() {
return descriptor.get.apply(this, arguments);
},
set: function () {
let oldValue = this[property];
descriptor.set.apply(this, arguments);
let newValue = this[property];
if (typeof callback == "function") {
setTimeout(callback.bind(this, oldValue, newValue), delay);
}
return newValue;
}
});
}
}
<input type="text" id="inputBox" placeholder="Enter something" />
the value property can be observed, Don't waste your time.
function changeValue (event, target) {
document.querySelector("#" + target).value = new Date().getTime();
}
function changeContentValue () {
document.querySelector("#content").value = new Date().getTime();
}
Object.defineProperty(document.querySelector("#content"), "value", {
set: function (t) {
alert('#changed content value');
var caller = arguments.callee
? (arguments.callee.caller ? arguments.callee.caller : arguments.callee)
: ''
console.log('this =>', this);
console.log('event => ', event || window.event);
console.log('caller => ', caller);
return this.textContent = t;
}
});
<form id="form" name="form" action="test.php" method="post">
<input id="writer" type="text" name="writer" value="" placeholder="writer" /> <br />
<textarea id="content" name="content" placeholder="content" ></textarea> <br />
<button type="button" >Submit (no action)</button>
</form>
<button type="button" onClick="changeValue(this, 'content')">Change Content</button>
This works and preserves and chains the original setter and getter so everything else about your field still works.
var registered = [];
var setDetectChangeHandler = function(field) {
if (!registered.includes(field)) {
var superProps = Object.getPrototypeOf(field);
var superSet = Object.getOwnPropertyDescriptor(superProps, "value").set;
var superGet = Object.getOwnPropertyDescriptor(superProps, "value").get;
var newProps = {
get: function() {
return superGet.apply(this, arguments);
},
set: function (t) {
var _this = this;
setTimeout( function() { _this.dispatchEvent(new Event("change")); }, 50);
return superSet.apply(this, arguments);
}
};
Object.defineProperty(field, "value", newProps);
registered.push(field);
}
}

Simple dynamic form

I have aI need to validate the input by .onblur such that whenever a text input loses focus it gets validated by the same JS function.
My problem is with the JS function. I want to grab the value of the item that loses focus.
function valid(){
if (isNaN("this should be the value of the text item??")){
}
Thankss..
To grab the value of an item as you blur, you should add the onBlur trigger to the DOM element as follows:
<input type="text" name="validate_me" onBlur="valid(this);" />
That way you have access to the DOM element that triggered the onBlur event and can access its properties (such as value or innerHTML in the case of textarea elements.
Your valid function can then be something like:
function valid(element) {
if (element.value != '' && isNaN(element.value))
alert('This field is not valid!');
};
This javascript should do what you are asking for:
(function(){
var after = function(existing,after) {
if ( existing == null || typeof existing !== 'function' ) {
return after;
}
else {
return function() { existing(arguments); after(arguments); }
}
}
var validate = function(input) {
alert('validating ' + input.name);
}
window.onload = after(window.onload, function() {
var inputs = document.getElementsByTagName('input');
for (var i = 0; i < inputs.length; i++) {
if ( inputs[i].type === 'text' ) {
inputs[i].onblur = after(inputs[i].onblur, function() {
validate(this);
});
}
}
});
}());
Clearly you will have to replace the alert in the validate function with your validation logic, but this should do what you ask.
A couple notes, the immediately invoked function is to ensure you don't clobber any globals, and the after function is to ensure that if you there is already an attached listener that your new validate listener will be called after the existing one.

Categories