Knockout JS textInput after event - javascript

I am having trouble using the textInput binding with a custom autocomplete/dropdown function. I am using the textInput binding so I do see the view model get updated, but the update takes place after the keypress event which is what my autocomplete is bound too. Here is my autocomplete function:
$("#table-body").on("keypress", ".combo", function (e) {
var item = ko.dataFor(e.target),
drop = $(".dropdown-menu", this);
if (item.Name() !== undefined && item.Name().length === 0) {
$(".input-group-btn", this).removeClass("open");
} else if (item.Name() !== undefined && item.Name() !== null && item.Name() !== "") {
drop.children().not(":containsNoCase(" + item.Name() + ")").hide();
drop.children().filter(":containsNoCase(" + item.Name() + ")").show();
$(".input-group-btn", this).addClass("open");
}
});
In the event above, item.Name() is null or whitespace when the first character is entered, and then is always 1 character behind what is typed. Any ideas on how I can change the event capture to be after the textInput has updated the view model?

It's interesting that the update of the model value takes so long and gets set so late in the game. I looked at the textInput binding source and found that knockout defers the setting of the value by 4ms when the input is changed. Might explain why the value is not updated at the time the handler is run?
var deferUpdateModel = function (event) {
if (!timeoutHandle) {
// The elementValueBeforeEvent variable is set *only* during the brief gap between an
// event firing and the updateModel function running. This allows us to ignore model
// updates that are from the previous state of the element, usually due to techniques
// such as rateLimit. Such updates, if not ignored, can cause keystrokes to be lost.
elementValueBeforeEvent = element.value;
var handler = DEBUG ? updateModel.bind(element, {type: event.type}) : updateModel;
timeoutHandle = setTimeout(handler, 4);
}
};

Instead of manually binding to the keypress event, I would consider using a knockout subscription on your Name observable. By doing do, you can be assured that you have the latest value and won't have to worry about the 1-behind problem. Whipped up a quick fiddle to demonstrate.
vm.name.subscribe(function(val){
var name = val,
drop = $(".dropdown-menu");
if (name !== undefined && name.length === 0) {
$(".input-group-btn").removeClass("open");
} else if (name !== undefined && name !== null && name !== "") {
drop.children().not(":contains(" + name + ")").hide();
drop.children().filter(":contains(" + name + ")").show();
$(".input-group-btn").addClass("open");
}
});
http://jsfiddle.net/ttz0p33n/

Related

How can I add keypress events when I already have an onclick event?

I'm building a web calculator.
It works fine when the user clicks the buttons, but I also want the user to press the keys. I'm not sure how to smoothly do this.
I've only included the event listener portion of my program since the rest of it is unnecessary to my question.
const a = document.getElementsByTagName('input');
// button press conditions
for (let i = 0; i < a.length; i++) {
a[i].addEventListener('click', function(e) {
// operators
if (a[i].value === '+' ||
a[i].value === '-' ||
a[i].value === '×' ||
a[i].value === '÷') {
prsOpr(i);
}
// decimal button
else if (a[i].value === '.') prsDeci(i);
// equal button
else if (a[i].value === '=') prsEql(i);
// backspace button
else if (a[i].value === '←') prsBksp();
// clear button
else if (a[i].value === 'Clear') prsClr();
// any number button
else logNum(i);
});
};
Your current code uses an anonymous function as the callback to the click event and because it's anonymous, you can't reuse it for other events as well without duplicating it. So, separate your callback function out and give it a name. Then, just use a second .addEventListener() and point it (and the first one) to the same function:
Here's an example:
let input = document.querySelector("input");
input.addEventListener("click", foo); // Set up a click event handler
input.addEventListener("keydown", foo); // Set up a key down event handler
// Both event registrations point to this one function as their callback
// so, no matter whether you click or type in the field, this function
// will run. But, all event handlers are passed a reference to the event
// that triggered them and you can use that event to discern which action
// actually took place.
function foo(evt){
console.log("The " + evt.type + " event has been triggered.");
}
<input>

How to get element id as function parameter

I am studying as front end developer. I am new to javascript. And i got this problem when i execute a js from backend passing some elements id. It displays some error Cannot read property 'addEventListener' of null.
My js:
function disableOtherCheckBoxGrpScrolls(elementsContainerId) {
console.error("Elements id from backend: " + elementsContainerId);
var container = document.getElementById(elementsContainerId);
// I am checking here length of elements
console.error("Elements length : " + container.length);
// It displays Elements length : undefined
container.addEventListener('mousewheel', function(e){
if (!$(this).hasScrollBar()) return;
// If container div has a scrollbar it will do nothing
var event = e.originalEvent,
d = event.wheelDelta || -event.detail;
this.scrollTop += (d < 0 ? 1 : -1) * 30;
e.preventDefault();
}, {passive: false});
}
Any solution of this ?
And my backend passing elements id
if (!isMobile)
JSUtil.execFn("disableOtherCheckBoxGrpScrolls", checkboxGrp.getElementsContainerId());
Guys i solved my problem :). But i don't understand well how this working. My solution is:
function disableOtherCheckBoxGrpScrolls(elementsContainerId) {
console.error('containerId: ' + elementsContainerId);
// First is element undefined or Not rendered to DOM my opinion
(function() {
if (typeof elementsContainerId === "undefined") {
throw new Error("elementsContainerId parameter is undefined");
}
var container = document.getElementById(elementsContainerId);
console.error("Elements ID : " + container.length);
container.addEventListener('mousewheel', function(e) {
if (!$(this).hasScrollBar()) return;
// logger.debug('has scroll');
var event = e.originalEvent,
d = event.wheelDelta || -event.detail;
this.scrollTop += (d < 0 ? 1 : -1) * 30;
e.preventDefault();
}, { passive: false });
})();
}
And i thought maybe js worked before html elements not loaded thats i got null and undefined error. By the way thanks for all comments and answers :).
Be sure to pass an function parameter while calling.
disableOtherCheckBoxGrpScrolls('yourIDElement');
You should check that your elementsContainerId parameter isn't undefined or null. In some place, you are calling the disableOtherCheckBoxGrpScrolls without a parameter, with an undefined variable, with a variable which value is null.
You could check that elementsContainerId is not undefined or null just before your logic and throw an error if condition is true. In this way, you will instantly notice if your are passing a wrong parameter to your function.
function disableOtherCheckBoxGrpScrolls(elementsContainerId) {
if(typeof elementsContainerId === "undefined" || elementsContainerId === null) {
throw new Error("elementsContainerId parameter is undefined or null");
}
// ...
}
Also, after the first validation, you could check if the element with the specified id exists (just to be shure that the mousewheel event is bound)

Validate input on blur [duplicate]

I am using the following code jsFiddle:
function Field(args) {
this.id = args.id;
this.name = args.name ? args.name : null;
this.reqType = args.reqType ? args.reqType : null;
this.reqUrl = args.reqUrl ? args.reqUrl : null;
this.required = args.required ? true : false;
this.error = args.error ? args.error : null;
this.elem = document.getElementById(this.id);
this.value = this.elem.value;
this.elem.addEventListener('blur', this, false);
this.elem.addEventListener('focus', this, false);
}
// FormTitle is the specific field like a text field. There could be many of them.
function FormTitle(args) {
Field.call(this, args);
}
Field.prototype.getValue = function() { return Helpers.trim( this.value ) };
Field.prototype.blur = function (value) {
alert("blur");
};
Field.prototype.focus = function (value) {
alert("focus");
};
Field.prototype.handleEvent = function(event) {
var prop = event.type;
if ((prop in this) && typeof this[prop] == "function")
this[prop](this.value);
};
inheritPrototype(FormTitle, Field);
var title = new FormTitle({name: "sa", id: "title"});
function inheritPrototype(e, t) {
var n = Object.create(t.prototype);
n.constructor = e;
e.prototype = n
}
if (!Object.create) {
Object.create = function (e) {
function t() {}
if (arguments.length > 1) {
throw new Error("Object.create implementation only accepts the first parameter.")
}
t.prototype = e;
return new t
}
}
The problem is that the 'blur' event is fired every time the field is brought to focus, which is opposite of what you'd expect. This is despite the fact that the focus event isn't even mentioned in the code. The problem is that I cannot replicate this problem in jsFiddle but the problem is happening in IE.
Also, on jsFiddle, there is another problem. The focus event is triggered multiple times...
Is there a possible explanation for this and/or a solution?
Updated:
Bonus question (and last on this, promise).
I added a function addEvent to dynamically add events to form fields instead of adding them all directly in the parent constructor. This is the jsFiddle for it. I am trying to call the function but it doesn't seem to work. What might I be doing wrong?
The alert in your focus handler immediately removes focus away from the field as soon as it gains focus. The loss of focus triggers the blur. It is odd that the blur comes first.
If you change the alerts to console.log (or something that does not steal focus), you will see that the events fire correctly.
http://jsfiddle.net/rsKQq/4/

How to apply an ng-class when data is added dynamically (?)

I have an application running with Ionic/Angular, all I need is to apply a class which is from the animate.css library, I have here this function which is the one working once you call $scope.addLineToBetSlip() and I want that class that I mentioned working once you call that function:
$scope.addLineToBetSlip = function(line, row, type) {
$scope.picksCount = !$scope.picksCount;
var spreadSelected = (row.spreadSelected && type === 'spread'),
totalSelected = (row.totalSelected && type === 'total'),
moneyLineSelected = (row.moneyLineSelected && type === 'moneyline');
if (spreadSelected || totalSelected || moneyLineSelected) {
BetSlipFactory.remove(line, row, type);
}else {
BetSlipFactory.add(line, row, type);
}
return $scope.picksCount;
};
here is my wrong HTML:
UPDATE
just change my code, is working now but only the first time that {{betSlipCount}} chenges
<span class="badge badge-assertive animate infinite"
ng-class="{bounceIn: picksCount}">{{betSlipCount}}</span>
<i class="icon ion-code-download"></i>
the other way I see, is that {{betSlipCount}} is constantly changing, actually {{betSlipCount}} changes every time you call $scope.addLineToBetSlip(), so the other way is activating that class every single time that {{betSlipCount}} changes.
It's been a while since I've used ng-class but I'm pretty sure the syntax is:
ng-class="{'className': booleanVariable}"
(meaning you've got the class name and variable backwards)
(also you may want to try enclosing the class name in single quotes, though I'm not sure if that's necessary)
The boolean variable can be a function that returns a boolean variable ie:
ng-class="{'fadeIn': addLineToBetSlip()}"
But it doesn't appear that your function returns a boolean variable. You could have it toggle a boolean variable in $scope, and use that variable name instead of the function, or you could have the function return true.
But I'm also not sure why you wouldn't just always want the 'fadeIn' class active.
Maybe you could tell us more about what your code is supposed to do and what it is currently doing.
UPDATE
Controller Code:
//Intialize the boolean variable
$scope.picksCount = false;
$scope.addLineToBetSlip = function(line, row, type) {
var spreadSelected = (row.spreadSelected && type === 'spread');
var totalSelected = (row.totalSelected && type === 'total');
var moneyLineSelected = (row.moneyLineSelected && type === 'moneyline');
if (spreadSelected || totalSelected || moneyLineSelected)
{
BetSlipFactory.remove(line, row, type);
}
else
{
BetSlipFactory.add(line, row, type);
}
$scope.picksCount = !$scope.picksCount;
};
HTML Code:
<span class="badge badge-assertive animate infinite" ng-class="{'bounceIn': picksCount}">{{betSlipCount}}</span>
<i class="icon ion-code-download"></i>

searching inputs for duplicated values except this

I'm in a medium project and a issue occurs.
I have extracted the essence of the problem to this fiddle
What this code is
$(".6").focusout(function(){
if( $(".6").filter(function(){ return this.value;}).not(this).length>0)
{ $(this).val("duplicated");}
What this code should do is get this.value and search it in other inputs, if it productive, alert the user and prevents the blur. The setback is in searching by value (if()), it does'nt working.
UPDATE
I have made a change in fiddle above: now the event sets a new value to input instead alert() and focus()
I have noticed that filters by "have a value" and not "have the value".
The main issue is regarding the filter.
.filter(function(){ return this.value;})
The jQuery filter method will return all jQuery objects where the return value is true. In the above code, every input that has a value will result to true.
To fix this, first save the value of the current input being modified, and then compare it with each value of the elements:
$(".6").focusout(function (e) {
var val = this.value;
if( $(".6").filter(function(){ return this.value === val;}).not(this).length>0){
$(this).val("duplicated");
}
})
A few additional tips. If you use the not() method first, then you will reduce the number of filter queries by 1.
$(".6").focusout(function (e) {
var val = this.value;
if( $(".6").not(this).filter(function(){ return this.value === val;}).length>0){
$(this).val("duplicated");
}
})
Even better, you can use the siblings() selector instead to only select other elements.
$(".6").focusout(function (e) {
var val = this.value;
if ($(this).siblings(".6").filter(function () { return this.value == val; }).length > 0) {
$(this).val("duplicated");
}
})
In this particular case, you can omit >0 in the if statement
$(".6").focusout(function (e) {
var val = this.value;
if ($(this).siblings(".6").filter(function () { return this.value == val; }).length) {
$(this).val("duplicated");
}
})
see: http://jsfiddle.net/tleish/VM2fy/4/

Categories

Resources