passing array values into a function - javascript

In the script below, the console.log is returning the correct values, and both alerts fire. However, the value is coming up undefined.
Any ideas what I'm doing wrong?
jQuery(document).ready(function(){
jQuery("#customfield_21070").attr('style','width:60px');
jQuery("#customfield_21070").attr('disabled','disabled');
var customfields = [
'#customfield_11070',
'#customfield_11071',
'#customfield_20071',
'#customfield_20072',
'#customfield_20073',
'#customfield_20074',
];
for(var i=0, len=customfields.length; i<len; i++) {
console.log(customfields[i]);
jQuery(customfields[i]).keyup(function(){
alert('calculate');//FIRES
calculateSum();
});
}
function calculateSum() {
var sum = 0;
alert('value: ' + this.value);//UNDEFINED
if(!isNaN(this.value) && this.value.length!=0 && this.id !== "customfield_21070") {
sum += parseFloat(this.value);
}
jQuery("#customfield_21070").val(sum.toFixed(2));
}
});

Use Function.prototype.call():
Calls a function with a given this value and arguments provided
individually.
Its first parameter is thisArg:
The value of this provided for the call to fun. Note that this may not
be the actual value seen by the method: if the method is a function in
non-strict mode code, null and undefined will be replaced with the
global object, and primitive values will be boxed.
So, pass this from your handler function to calculateSum like so:
jQuery(customfields[i]).keyup(function(){
calculateSum.call(this);
});

#canon is absolutely correct, following as he said will resolve your Issue.
You can possibly write :-
jQuery(document).ready(function () {
jQuery("#customfield_21070").attr('style', 'width:60px');
jQuery("#customfield_21070").attr('disabled', 'disabled');
var customfields = [
'#customfield_11070',
'#customfield_11071',
'#customfield_20071',
'#customfield_20072',
'#customfield_20073',
'#customfield_20074',
];
for (var i = 0, len = customfields.length; i < len; i++) {
jQuery(customfields[i]).keyup(function () {
calculateSum(this);//Edited
});
}
function calculateSum(param) {//Edited
var sum = 0;
alert('value: ' + param.value);//UNDEFINED
if (!isNaN(param.value) && param.value.length != 0 && param.id !== "customfield_21070") {//Edited
sum += parseFloat(param.value);
}
jQuery("#customfield_21070").val(sum.toFixed(2));
}
});
This reflects proper value in your alert. **Tested**

Related

How to pass an object's method as a parameter to another function in Javascript

First take a look at my simple codes below:
function mySecondFunction(objArray,setFunc)
{
for (let i = 0; i < objArray.length; i++)
{
objArray[i].info.setTop(72);
}
}
function myFunction()
{
let myObjArray = [];
for (let i = 0; i < 10; i++)
{
myObjArray.push({
info:{topVar:0,
bottomVar:0,
get top() {return this.topVar;},
get bottom() {return this.bottomVar;},
setTop: function(input) {this.topVar = input;},
setBottom: function(input) {this.bottomVar = input; }
}
});
}
mySecondFunction(myObjArray); // This works Fine
mySecondFunction(myObjArray,setTop); // I want something like this!!!
}
As you can see, I want to pass a method of an object to another function. I know a lot of possible solutions to avoid this, but I want to know whether it is possible or not.
Detach it and pass as an argument. Remember to use call to set the intended this value.
function mySecondFunction(objArray, setFunc)
{
for (let i = 0; i < objArray.length; i++)
{
setFunc.call(objArray[i].info, 72);
/* explicitly telling that:
please set 'this' value in this function to be 'objArray[i].info' when running,
allowing, e.g. `this.topVar` in
`setTop: function(input) {this.topVar = input;}`
to be operating on `objArray[i].info.topVar` */
}
}
function myFunction()
{
let myObjArray = [];
for (let i = 0; i < 10; i++)
{
myObjArray.push({
info:{topVar:0,
bottomVar:0,
get top() {return this.topVar;},
get bottom() {return this.bottomVar;},
setTop: function(input) {this.topVar = input;},
setBottom: function(input) {this.bottomVar = input; }
}
});
}
mySecondFunction(myObjArray, myObjArray[0].info.setTop);
/* once detaching the method from the object,
(if we are not using arrow functions),
we lose 'this' value, meaning we are losing
the target of object that we want to operate on */
console.log(myObjArray)
}
myFunction();
You can target item number in the array list. You can do statically (i.e. 1-???) or dynamically with an iteration and a variable. You can then the object property within that. For example:
myObjArray[0].info.setTop
That will target the 1st item in the array. Be sure to omit parentheses (()) when passing the method as you want to pass the function reference not the result

Why is window[func] undefined in JS?

I am writing a script that validates form fields. The form input has a data-validate attribute that contains a CSV of different functions. I loop through all inputs containing a data-validate value, separate those values into an array, and loop through that array to call the corresponding function. My problem is that window[func] is undefined - I get "Cannot read property 'apply' of undefined" message. What am I doing wrong? Here's a jsFiddle
<input type="text" name="first_name" data-validate="min_length[4]">
(function($){
var fields = [];
var i = 0;
$('[data-validate]').each(function(){
var rules = $(this).data('validate').split("|");
var label = $(this).parent().find('label').first();
fields[i] = {
name: $(this).attr("name"),
label: label.text(),
rules: rules,
element: $(this)
}
i++;
});
$.each(fields, function(key, field){
field.element.on('focusout', function(){
$.each(field.rules, function(key1, rule){
if(rule.includes("[")){
var rule = rule.match(/(.*?)\[(.*?)\]/);
func = rule[1];
var len = rule[2];
params = [field.element.val(), len];
} else {
func = rule;
params = [field.element.val()];
}
if(callFunc(func, params)){
field.element.addClass('is-valid').removeClass('is-invalid');
} else {
field.element.addClass('is-invalid').removeClass('is-valid');
}
});
});
});
})(jQuery);
function min_length(str, len){
return str.length > len;
}
function callFunc (func, arguments){
window[func].apply(null, Array.prototype.slice.call(arguments, 1));
}
I think I made it work with some improvements, check it out:
(function($){
window.min_length = function(str, len) {
return str.length > len;
}
function callFunc(func, arguments){
return window[func](...arguments);
}
var fields = [];
$('[data-validate]').each(function(){
var rules = $(this).data('validate').split("|");
var label = $(this).parent().find('label').first();
fields.push({
name: $(this).attr("name"),
label: label.text(),
rules: rules,
element: $(this)
});
});
$.each(fields, function(key, field){
field.element.on('input', function(){
$.each(field.rules, function(key1, rule){
if(rule.includes("[")){
var rule = rule.match(/(.*?)\[(.*?)\]/);
func = rule[1];
var len = rule[2];
params = [field.element.val(), len];
} else {
func = rule;
params = [field.element.val()];
}
if(callFunc(func, params)){
field.element.removeClass('is-invalid').addClass('is-valid');
} else {
field.element.removeClass('is-valid').addClass('is-invalid');
}
});
});
});
})(jQuery);
input {
outline: 0;
}
.is-valid {
border: 1px solid green;
}
.is-invalid {
border: 1px solid red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="text" name="first_name" data-validate="min_length[4]">
Some issues:
You check the result of function callFunc(func, params) call but it
returns nothing, i.e. undefined, so your condition is always
false. You should return the result of min_length() from
callFunc().
I found 2 classes is-valid in the css section of the snippet on
jsFiddle. One of them should be is-invalid.
Also due to some closure issues it seems that your
function min_height didn't become a property of window. So, I
placed it inside the IIFE and explicitly added it to window which
works because of closures, again.
There is no need for i variable - you can just push() objects
into array.
Also it is possible to use ... operator calling min_length
inside callFunc instead of that apply thing.
Consider using input event instead of focusout.
With regard to the specific error you're getting, I'm not getting that at all from your code. However, here's what I figured out with some testing (and fixed):
1) You need to return the value of the function you call in callFunc otherwise you'll just get undefined. In addition you're not passing in the right number of arguments.
return window[func].apply(null, Array.prototype.slice.call(arguments));
2) You need to convert your len parameter to a number from a string:
return str.length > Number(len);
3) Both of your classes are called is-valid :)
Here's the full working code.

Get object out of observable array

Why is m "undefined" in this code:
currentViewModel = ko.mapping.fromJS(viewModel);
currentViewModel.getReport = function(reportId) {
for(var i=0;i<currentViewModel.availableReports().length;i++) {
if(currentViewModel.availableReports()[i].id == reportId) {
var m = currentViewModel.availableReports()[i];
return currentViewModel.availableReports()[i];
}
}
}
I call getReport() as an onclick event and I want to send the report object to a view (modal) I can do a foreach on the availableReports and it's all there. When I run through the debugger, it loops through the array and finds the right one. But why can't I pull it out of the array? "m" remains undefined the the function returns undefined.
What am I missing here?
EDIT: there is a follow up question here:
Can knockout.js wait to bind until an onClick?
You just need to change if(currentViewModel.availableReports()[i].id ... to if(currentViewModel.availableReports()[i].id() ... because after mapping id will become an observable, i.e. function.
Updated code:
currentViewModel = ko.mapping.fromJS(viewModel);
currentViewModel.getReport = function(reportId) {
for (var i = 0; i < currentViewModel.availableReports().length; i++) {
if (currentViewModel.availableReports()[i].id() == reportId) {
var m = currentViewModel.availableReports()[i];
return currentViewModel.availableReports()[i];
}
}
}
Demo - Fiddle.
I'll repeat the solution from #NikolayErmakov's answer here, but want to add two things to get a more complete answer. You end with:
...m remains undefined and the function returns undefined.
What am I missing here?
You're missing two things:
The var m bit of the first statement inside the if is hoisted to the top of the current scope (the top of the function). This is why the debugger can tell you what m is, even if you never reach the line of code it's on.
If a function invocation reaches the end of a function (as is the case for you, since you never go inside the if) without seeing an explicit return statement, it will return undefined.
To better understand this, you should interpret your function like this:
currentViewModel.getReport = function(reportId) {
var m;
for (var i = 0; i < currentViewModel.availableReports().length; i++) {
if (currentViewModel.availableReports()[i].id == reportId) {
m = currentViewModel.availableReports()[i];
return currentViewModel.availableReports()[i];
}
}
return undefined;
}
Some people (e.g. Douglas Crockford) do recommend placing var statements at the top of a function, though it's a matter of style to some degree. I don't think many people explicitly return undefined at the end of a function, though in your case I might be explicit about that scenario and return null (or throw an Error even).
As promised, I'll repeat the actual solution, as I concur with the other answer:
you need to invoke id as a function to get its value (because the mapping plugin will map to observable()s.
In addition:
I'd retrieve the array only once
I'd suggest using === instead of ==
Here's my v0.5 version:
currentViewModel.getReport = function(reportId) {
var m = null, reports = currentViewModel.availableReports();
for (var i = 0; i < reports.length; i++) {
if (reports[i].id() === reportId) {
m = reports[i];
return m;
}
}
return m;
}
But I'd optimize it to this v1.0:
currentViewModel.getReport = function(reportId) {
var reports = currentViewModel.availableReports();
for (var i = 0; i < reports.length; i++) {
if (reports[i].id() === reportId) {
return reports[i];
}
}
return null;
}
For completeness, here's another version that utilizes filter on arrays:
currentViewModel.getReport = function(reportId) {
var reports = currentViewModel.availableReports().filter(function(r) { return r.id() === reportId; });
return reports.length >= 1 ? reports[0] : null;
}

need help using for loop and getText() in protractor

browser.findElements(protractor.By.repeater('cat in cats')).then(function(rows) {
for (i = 0; i < rows.length; i++) { //which is always 3
var tmp = element(by.repeater('cat in cats').row(i)).element(by.binding('cat.name')).getText();
tmp.then(function(text) {
console.log('text is : ' + text);
console.log('iteration number is: ' + i);
if (text == 'someText') {
element(by.repeater('cat in cats').row(i)).element(by.binding('cat.action')).click();
}
});
}
In this case the value of 'i' inside the function is always returning 3.
I have get text and then check if the text is what I want and click on an element.
The value of 'i' in the 'if' statement is always returned as 3. Something to do with promises, but I am not sure.
Any help with modified code is much appreciated.
Thanks!
Don't call by.repeater() multiple times; instead, use map() and "chain" the promises like this:
element.all(By.repeater('cat in cats')).map(function(elm) {
return {
text: elm.element(by.binding('cat.name')).getText(),
action: elm.element(by.binding('cat.action'))
};
}).then(function(arr) {
for (var i = 0; i < arr.length; i++) {
if (arr[i].text == 'someText') {
return arr[i].action;
}
}
throw new Error('Text not found');
}).then(function(elm) {
elm.click();
});
All of the credits go to #Andres for the solution provided here:
Passing Protractor ElementFinder to deferred.fulfill() results in a promise containing a null value
Also see:
Protractor find element inside a repeater

_.each function for looping in JS issue

We are asked to do the following:
Write a function called checkValue that searches an array for a value. It takes an array and a value and returns true if the value exists in the array, otherwise it returns false.
var helloArr = ['bonjour', 'hello', 'hola'];
var checkValue = function(arr, val) {
//checks if the val is in arr
}
Rewrite checkValue using _.each.
here is what I have to itterate over helloArr using _.each:
var helloArr = ['bonjour', 'hello', 'hola'];
var checkValue = function (num) {
return num;
}
checkValue('hola');
var output = us.each(helloArr, function(num){
if (checkValue())
{return true;}});
return output;
What am I doing wrong? When I run it with node, theres no errors but no output either. I know you can use _.find to do this but the spec is asking to itterate over the array and find the value using _.each.
In your second example, you're calling checkValue without a parameter, so it's going to return undefined, which is a falsey value, and the callback to each never returns anything.
Then again, it doesn't normally need to return anything anyway. _.each returns the list it operates on.
_.each is like a for-loop; consider treating it more like one.
function checkValue_original1(arr, val) {
for (var i = 0; i < arr.length; i++) {
if (val == arr[i]) return true;
}
return false;
}
function checkValue_original2(arr, val) {
return arr.indexOf(val) >= 0;
}
function checkValue_us_each(arr, val) {
var found = false;
_.each(arr, function(element, index, list) {
if (element == val) found = true;
});
return found;
}

Categories

Resources