Function not being set correctly - javascript

I have a function that creates a new record in the database and returns the ID of the record created. From this I need to assign a function to a select with the value of the returned ID.
.done(function(response) {
//console.log(siblings[1].dataset.contno);
var dbResponse = JSON.parse(response);
document.getElementById(runID).setAttribute('data-runid', dbResponse.id);
var newRunID = dbResponse.id;
var driverSelectID='driverSelectRun'+runCode;
//adding the onchange function with the correct ID to the dropdowns (the assignVehicle FUnction takes the runID which is unknown untill response)
(function(newRunID) {
document.getElementById(driverSelectID).onchange = function () {
assignDriver(newRunID);
}
})(newRunID);
(function(newRunID) {
document.getElementById(vehicleSelectID).onchange = function () {
assignVehicle(newRunID);
}
})(newRunID);
console.log(newRunID);
});
The console.log for the newRunID is 1566 but the onchange function of this select does not contain the new run id it simply shows as assignVehicle(newRunID) instead of actually using the value returned from the database (assignVehicle(1566)). I have used the exact same method on another part of the code which works fine. Can anyone see why this is not working correctly.
Thanks in advance for any replies!
<select id="driverSelectRun6">...</select>
That is the code for the select. It is definately being targeted correctly as the function is being set just without the arguement.
UPDATE
This was a scope issue. The newRunID was declared using var newRunID= but this was delcared in the .done function so was of local scope instead of global. removing the var to make it just newRunID= worked because assigning a value to an undeclared variable implicitly creates it as a global variable (it becomes a property of the global object)

The issue here is you are redefining the variable newRunID as an argument and that argument is actually the event object that is returned from the change event listener.
.onchange = function (newRunID) {
should be
.onchange = function () {

(function(newRunID) {
document.getElementById(driverSelectID).onchange = function () {
assignDriver(newRunID);
}
})(newRunID);

Related

Setting "this" to the instance, in a callback set during the creation of a prototype function

I have this code:
var createAllAreSelectedClickedHandler = function(selectablesArrayGetter) {
return function() {
var array = selectablesArrayGetter();
var desiredState = array.every(function(selectable) { return selectable.selected; }) ? false : true;
array.forEach(function(selectable) {
selectable.selected = desiredState;
});
};
};
Followed by this one:
function PromoViewModel() { this.registrations = [...] }
PromoViewModel.prototype.allEventsSelectedClickedHandler = createAllAreSelectedClickedHandler(function() { return this.registrations; }));
I can't manage to set the correct value of this. The "this" value when the function is created points to Window so I can't do .bind(this). I've tried doing .bind(PromoViewModel.prototype) but it lacks all the precious instance fields set inside the constructor.
I know I could simply set this.allEventsSelectedClickedHandler in the constructor function, but I'm trying to separate the methods creation from the variables.
The problem is the call selectablesArrayGetter(); which determines the this value for the callback.
You will need to "pass" the this value that the method (i.e. the closure you are returning) is invoked on, using call:
var array = selectablesArrayGetter.call(this);
I'd recommend defining your PromoViewModel.prototype.allEventsSelectedClickedHandler method as follows:
PromoViewModel.prototype.allEventsSelectedClickedHandler = function() {
var _array = this.registrations;
var desiredState = _array.every(function(selectable) { return selectable.selected; }) ? false : true;
_array.forEach(function(selectable) {
selectable.selected = desiredState;
});
};
the function that you're passing as callback uses this, but doesn't have the PromoViewModel context. You can ensure the method has the proper context by binding this to a variable.
function PromoViewModel()
{
var me = this;
this.registrations = [...];
this.allEventsSelectedClickedHandler = createAllAreSelectedClickedHandler(function() {
return me.registrations;
});
}
Working fiddle: http://jsfiddle.net/michaschwab/coegnL5j/9/ also has Bergi's answer in there (commented out) to show that that works just as well.
Ok here is what I did.
In the prototype definition instead of directly associating it to createAllAreSelectedClickedHandler function, I actually define a function that returns the createAllAreSelectedClickedHandler function. By doing this, I can define a variable (in this case protoScope) that maps this context when defined.
When doing that, if you put a break-point in the createAllAreSelectedClickedHandler function you will see that the selectablesArrayGetter value is correct (the acutal registrations array).
PromoViewModel.prototype.allEventsSelectedClickedHandler = function (){
var protoScope = this;
return createAllAreSelectedClickedHandler(function() {
return protoScope.registrations;
});
}

td:nth-of-type('+element+') not working in casperjs

I want to get text of all td's inside the table with the class .fmBigTbl .
When I hard code the no's instead of giving piElements, for eg td:nth-of-type(2). The above works but when I use a variable instead of a number it outputs null.
How can I loop through all the td's in the table?
piElements = 1;
var data;
var count = 5;
this.repeat(count, function() {
this.then(function() {
data = this.evaluate(function() {
return $('.fmBigTbl').find('td:nth-of-type('+piElements+').fmLblCell2').text();
});
this.echo(data);
piElements++;
});
});
evaluate is sandboxed. The inside (page context) cannot simply access variables in outer scope (casper context). You need to pass piElements explicitly into evaluate:
data = this.evaluate(function(piElements) {
return $('.fmBigTbl').find('td:nth-of-type('+piElements+').fmLblCell2').text();
}, piElements);
From the docs:
Note: The arguments and the return value to the evaluate function must be a simple primitive object. The rule of thumb: if it can be serialized via JSON, then it is fine.
or you could simply use casper.fetchText:
data = this.fetchText('.fmBigTbl td:nth-of-type('+piElements+').fmLblCell2');

Calling a Dynamically set Function Object Property from within the Object

From within my webpage I am creating an object and trying to call a dynamically set function from within it. The dynamic function however, isn't being executed.
Here is a subset of the Object:
var LightBoxLogin = {
DialogBox: null,
SuccessFunction: null,
..........
Login: function(){
console.log(LightBoxLogin.SuccessFunction) // Displays "TestSubmit()"
LightBoxLogin.SuccessFunction(); // does nothing, should alert the page
}
}
LightBoxLogin.SuccessFunction is set with:
function SuperLightbox(functOnSuccess)
{
LightBoxLogin.SuccessFunction = functOnSuccess;
if(IsLightboxNeeded())
{
LightBoxLogin.Login();
}
else{
alert("Not needed");
}
}
And called like:
function TestSubmitHandler ()
{
SuperLightbox(TestSubmit);
}
function TestSubmit ()
{
alert('TEST SUBMIT ALL CAPS');
}
Let me know if im missing anything.
I just need to execute the function passed as a parameter initially.
Instead of the line
SuperLightBox(TestSubmit);
Use the function name as a String instead:
SuperLightBox("TestSubmit");
This means in the Login:function() this line:
LightBoxLogin.SuccessFunction();
Will be replaced with:
window[LightBoxLogin.SuccessFunction]();
This yields the results I was looking for, but beware; it only works if the desired function is accessible globally in the page.

Retrieving the value of a variable through a closure

I have the following module pattern in the javascript for a webpage:
var swf_debugger = swf_debugger || {};
(function($, swf_debugger) {
swf_debugger.pageSetup = (function() {
var
swfType = null,
formData = {},
// ... unimportant code continues ...
initChangeEvents = function() {
$.each(formElements, function(index, $el) {
if ($el.hasClass("swfToLoad")) {
// THIS EVENT IS WHERE I MAKE THE ASSIGNMENT TO 'swfType'
$el.change(function() {
swfType = $("option:selected", this).val();
console.log("swfToLoad has triggered");
console.log(swfType);
});
return;
}
// NO ISSUES HERE WITH THESE EVENTS...
switch($el.prop("tagName")) {
case "SELECT":
$el.change(function() {
formData[$el.attr('id')] = $("option:selected", this).val();
});
break;
case "INPUT":
switch ($el.attr('type')) {
case "text" :
$el.change(function() {
formData[$el.attr('id')] = $(this).val();
});
break;
case "checkbox" :
$el.change(function() {
formData[$el.attr('id')] = $(this).prop("checked");
});
break;
default:
}
break;
default:
}
});
},
init = function() {
$(function() {
addFormComponents();
populateDropdowns();
initCachedData();
initChangeEvents();
});
};
init();
return {
swfType: swfType,
formData: formData
};
}());
}($, swf_debugger));
Essentially I am attaching an event to a series of jquery selected elements, with the callback simply storing the contents of a particular form element (specifically select, text, and checkbox elements) in a variable or an object.
I know my events are attaching properly because when I add console.log statements to them I can see them firing. Also, whenever I call swf_debugger.pageSetup.formData in the console I see valid contents of the object that each of those events are populating, so those events are doing what they're supposed to.
My troubles are happening whenever I try to access swf_debugger.pageSetup.swfType it always returns null and I am not understanding why. I know that the particular event feeding this value, is firing and I know that at least within the function scope of the callback, swfType is valid because of what is returned in my console.log statements. However, whenever I try to access the contents of swfType through the closure, (i.e. typing swf_debugger.pageSetup.swfType in the console), It always returns null.
I am guessing that I am running into the difference between an objects reference being passed and a variables value being passed, but I am not sure. Can someone please help me along here and explain why swfType is always returning null through the closure.
why is swfType always returning null
Because that's the value which you assigned to the property (gotten from the swfType variable which had that value at the time of the assignment). A property is not a reference to the variable assigned to it - you can only assign a value.
What you can do:
make the object property a getter method which returns the value of the local swfType variable whenever it is called
don't use a variable but assign to the property of the swf_debugger.pageSetup object each time

Global Variable Not Assigning between Functions

I am writing an extension for Google Chrome in HTML/Javascript. I am trying to use a global variable to pass information between two functions, however even if I assign my variable in one function it hasn't changed when I read it from the other function.
var type = 0; //define global variable
window.onload=function(){onCreated()}; //set onCreated function to run after loading HTML
function onCreated()
{
chrome.history.search({'text': ''},function(historyItems){gotHistory(historyItems)});//search for historyItems and then pass them to the gotHistory function
}
function gotHistory(historyItems)
{
var idcount=0;//used to increment the ids of each new element added
for(var count=0; count < historyItems.length; count++)//go through each history item
{
chrome.history.getVisits({'url':historyItems[count].url}, function(visitItems){gotVisits(visitItems)}); //search for visitItems for the url and pass the results to gotVisists function (atm all this function does is assign the global variable to =3)
var body = document.getElementById("outputid");//find the body of the HTML
var newt = document.createElement("p");//create a new element
newt.setAttribute("id","url"+idcount);//give it a unique id
newt.innerHTML = historyItems[count].title;//set the text to say the title of the url
if(type != 0)//if the other function was successful, type=3 and the text should be green
{
newt.style.color="green";
}
body.appendChild(newt);//add the new element to the body
idcount++;
}
}
function gotVisits(visitItems)
{
//assign the global variable to be 3
type = 3;
}
But, the elements are NEVER green. They should always be green. This means that in the function gotVisits, type is not being successfully assigned to 3.
Can anyone explain what is going on here?
Cheers,
Matt
p.s I realise the gotVisits function is useless here really, but I'm using it to demonstrate a point. In reality I will use it to pass back useful information to
Rather than:
var type = 0;
Try:
window.type = 0;
Optionally you can also use a closure such as:
(function() {
var type = 0;
var type = 0; //define global variable
window.onload=function(){onCreated()}; //set onCreated function to run after loading HTML
function onCreated()
{
chrome.history.search({'text': ''},function(historyItems){gotHistory(historyItems)});//search for historyItems and then pass them to the gotHistory function
}
function gotHistory(historyItems)
{
var idcount=0;//used to increment the ids of each new element added
for(var count=0; count < historyItems.length; count++)//go through each history item
{
chrome.history.getVisits({'url':historyItems[count].url}, function(visitItems){gotVisits(visitItems)}); //search for visitItems for the url and pass the results to gotVisists function (atm all this function does is assign the global variable to =3)
var body = document.getElementById("outputid");//find the body of the HTML
var newt = document.createElement("p");//create a new element
newt.setAttribute("id","url"+idcount);//give it a unique id
newt.innerHTML = historyItems[count].title;//set the text to say the title of the url
if(type != 0)//if the other function was successful, type=3 and the text should be green
{
newt.style.color="green";
}
body.appendChild(newt);//add the new element to the body
idcount++;
}
}
function gotVisits(visitItems)
{
//assign the global variable to be 3
type = 3;
}
})();
This saves you from polluting the window object, which you should avoid doing anyhow and allows the inner functions access to the type variable.
It should do what you want.
Also the outer function wrapper for your window.onload is redundant, just do:
window.onload = onCreated;
It looks like chrome.history.getVisits executes the callback asynchronously, so you first try to check that variable, and it gets updated later. You can verify this with a pair of console.log messages.
Move the rest of the code inside the callback, so it gets executed at the right time.

Categories