Why can I not see my object's properties - javascript

I'm stumped here and would love to know what I'm mising.
I was just re-writing some code and cleaning some stuff up. I have a method that does an ajax call and returns a json string. Since I use the method a couple of places I abstracted to this method here within a name space.
This is json object returned
{"charge_ID":"4","price":"37","description":"corporate rate"}
This is the abstracted method.
wwdb.getChargeRate = function( chargeID ){
var rate = {}, prop;
$.post(baseUrl + 'dashboard_job/jx_get_charge_rate',
{
'charge_ID': chargeID
},
function( data ){
for( prop in data) {
rate[prop] = data[prop];
}
},
'json'
);
return rate;
};
I thought I would just be able to assign data to rate but had to iterate through to get that to work. In fact I thought I would be able return data direct from within the function() part of $.post() method, but that didn't work. So question 1. is why can I not just return data?
But much more basic and frustrating is the fact that in the code below I cannot access the object properties. Question 2. is why?
$('#charge_rate_slt').change(function(){
var t = $(this), v = $(t).val(), rate;
console.log(wwdb.getChargeRate( v )); //returns object ok
rate = wwdb.getChargeRate( v ); //assigns object ok
console.log(rate); //displays in firebug as object with properties
console.log(rate.price) //undefined
for(p in rate)
console.log( p ); //undefined?
});

Remember this is asynchronous -- the function has not been called when you are looking at it.
When the function returns rate the call has not finished. If you look at the rate variable a later it will have the data. I didn't look at the 2nd part of the code, but I expect is a similar issue.
To understand this issue make the following change:
wwdb.getChargeRate = function( chargeID ){
var rate = {}, prop;
$.post(baseUrl + 'dashboard_job/jx_get_charge_rate',
{
'charge_ID': chargeID
},
function( data ){
for( prop in data) {
rate[prop] = data[prop];
}
},
'json'
);
return { "price" : "I am not undefined" };
};
Now your calling function will have something to print.

Related

How to define fluent API for curried or partial applicated functions

I want to write one utility that can be used in any of this ways:
metadata("Copyright Notice", "John Smith");
metadata.copyright("John Smith");
metadata("Manufacturer", "Canon");
metadata.manufacturer("Canon");
How can I approach the problem?
You can try this
function metadata(a, b) {
console.log('Metadat function',a,b);
}
metadata.copyright = function (a) {
console.log('copyright',a);
}
metadata.manufacturer = function (a) {
console.log('manufacturer',a);
}
metadata("Copyright Notice", "John Smith");
metadata.copyright("John Smith");
metadata("Manufacturer", "Canon");
metadata.manufacturer("Canon");
I don't know why you would this (it sounds like a confusing way to offer an API - if people use it in the wrong way, or unintended way, its best to fail) but it can be done using Proxy:
var metadata = function(){
var data = {};
return new Proxy(function( k, v ){
return v ? data[ k ] = v : data[ k ];
}, {
get( target, key ){
return v => target( key, v );
}
});
}();
metadata( 'Copyright Notice', 'John Smith' );
metadata.manufacturer( 'Canon' );
console.log( metadata.manufacturer(), metadata( 'Copyright Notice' ), )
In essence we just set up traps for the get property, se when users request proxy.manufacturer it will return a function that will already have the key filled in as manufacturer - when called it just calls the function it is proxying.
Update I made it a private function that returns the proxy. To get the value out, just call the desired function without a value. It will return undefined if never set.
Proxies usually are used for debugging. For more info, have a look at this MDN page: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy

block functions from JS objects in ajax call

I am having a bit of an issue that I have never seen before. Usually when I build JS "classes" I will just do the simple object style ...
var page = {
add: function(k, v) {
this[k] = v;
}
}
I recently tried doing it with the var page = function() { blah blah } page.prototype = { style but now when I am trying to send off stuff through ajax everything breaks. I put in console logs to try to see what is happening and it seems that in the ajax call, the objects are being turned into window scope, and then the functions are trying to be parameterized. Horrible explanation, I apologize, here is the code, maybe it will make more sense...
var bioLocation = function(obj) {
this.ID = 0,
this.name = '',
this.address = '';
if(typeof obj != 'undefined') {
return this.init(obj);
}
}
bioLocation.prototype = {
init: function(obj) {
for(var index in obj) {
this[index] = obj[index];
}
}
}
Obviously a stripped down version of my real object, but a perfect representation. So then later on in the code I have a button even that will send data off over ajax to save some stuff like so.
$('.save').click(function() {
var postData = {};
var saveTest = new bioLocation({ID: $('.whatever').val()});
postData.locations = saveTest;
var reply;
$.ajax({
async: false,
type: "POST",
data: postData,
dataType: 'json',
success: function(msg) {
reply = msg;
}
});
});
Again, obviously a very simplified version of my code, but pretty much right on target.
The problem is I am getting very strange errors...
"cannot read property 'undefined' of undefined" the line will point directly to the second line of the init function in the bioLocations prototype.
I have done tons of console logs throughout the code, when I put them in the init method it will log twice, once when I create the object in the click function, and once in the ajax call. Here is the code with the console logs in it...
var bioLocation = function(obj) {
this.ID = 0,
this.name = '',
this.address = '';
if(typeof obj != 'undefined') {
return this.init(obj);
}
}
bioLocation.prototype = {
init: function(obj) {
console.log(this);
for(var index in obj) {
this[index] = obj[index];
}
}
}
So I will get 2 console logs when I click the submit button, the first shows...
bioLocation {ID: 5, name: '', address: ''}
then the second time it logs (still within the same click event) it will log...
Window {top: Window, window: Window, location: Location, external: Object...}
and then I will get that error "Cannot read property 'undefined' of undefined"
I can't figure out why it is logging it twice, and more so, why is the second log a Window object. It seems like the ajax call is trying to serialize the functions and getting errors, but I have never seen a prototype being serialized when the object is passed into ajax as a param. I can confirm that it is in fact trying to param the functions because with more logging and hair pulling I was actually able to see the ajax call in the network tab and it did in fact have the init: undefined as one of the params being passed to the ajax page.
So I guess my questions are, how do you stop jquery's ajax function from parameterizing the prototype piece of an object when using it as a param? And, why on earth is it actually adding the prototype into the the call?
Please, you gotta help me, I am loosing my mind here. I have never seen anything like this. Thank you in advance.
The constructor needs to return the new object, not the return value of the init function. Omit the return and it will return this by default. You also had commas where you should have had semi-colons. By convention, classes are capitalized.
var BioLocation = function (o) {
this.ID = 0;
this.name = '';
this.address = '';
if (typeof o !== 'undefined') {
this.init(o);
}
}
BioLocation.prototype = {
init: function (o) {
for (var i in o) {
this[i] = o[i];
}
}
};
var saveTest = new BioLocation({ ID: 5 });
console.log(saveTest);

How am I getting the scope wrong? Unable to access this function from jQuery ".on"

Morning all,
I'm using the following code to somewhat imitate setInterval with AJAX:
// Poll for ALERTs
(function pollForAlerts() {
var params = { "send": 1, "poll": 1 };
// Set up the correct patch for sending AJAX data
ALERTS = {};
ALERTS.Auth = { site: data_site, uuid: data_uuid };
ALERTS.API = function(app,data) {
var url = "//myurl.com/alerts/"+ app +"/?";
var data = $.extend({}, ALERTS.Auth, data);
return url + jQuery.param(data || "") + '&timestamp='+$.now();
}
// Run the AJAX request
$.getJSON( ALERTS.API( 'touchscreen', params ) , function (response) {
if( typeof response === "object" ) {
for( var i = 0; i < response.length; i++ )
renderAlert(response[i]);
} else { setTimeout( pollForAlerts, 3000 ); }
});
}());
The function runs repeatedly until it finds a response.
I'd like to then set a jQuery ".on" to restart this loop if a certain element is clicked on:
// Respond to ALERT
$('#alerts').on('click', 'td.initials span', function(event) {
$(this).closest('tr').removeClass("active abs cri");
pollForAlerts();
});
However, when I do that, I get the following error in Firebug:
ReferenceError: pollForAlerts is not defined
http://myurl.com/alerts/static/js/touchscreen.js
Line 14
I can't work out why pollForAlerts() can't be accessed. Is it because of the self-executing function, or is it just because it's being used within jQuery's on function?
I'm not JavaScript expert, especially when it comes to self-executing functions and closures, so please be gentle with me!
Duncan
You wrote self invoking function, These functions are executing only once in lifetime. If you want to call a function multiple times then you can write it as normal function.

Wierd AngularJS error

I am rather new to AngularJS programming and was working on a sample application. I came across a problem while returning an object from a service. Consider following code in my custom service:
this.getCompanyInfo = function(companyID)
{
console.log( companyID );
angular.forEach( companyInfo, function( coInf ) {
if( coInf.companyID == companyID )
{
console.log(coInf);
return coInf;
}
})
}
in this code, companyInfo is an array containing information about companies, each company is represented by an object. the second console.log is showing this:
Object {companyID: "CHCL", companyName: "Chilime Hydropower", stockPriceTrend: Array[4]}
in my controller, I have this:
$scope.companyInfo = dataServices.getCompanyInfo( $routeParams.companyID);
console.log($scope.companyInfo);
but here, console.log says 'undefined'.
I don't know what wrong I have been doing, any help will be highly appreciated!
Regards.
You are just returning from the iterator function argument of the forEach (not from getCompanyInfo) which will still go on. But you need to return the actual value from your getCompanyInfo function. You can just use a simple for loop and return the value once you find a match.
this.getCompanyInfo = function(companyID)
{
var cInfo, i, l;
for(i=0,l=companyInfo.length; i<l;i++){
if((cInfo = companyInfo[i]).companyID === companyID ){
return cInfo ;
}
}
}
Returning from the iterator function does not break the looping using angular.forEach. Loop will still go on.
change function to
this.getCompanyInfo = function(companyID)
{
var coInfo;
console.log( companyID );
angular.forEach( companyInfo, function( coInf ) {
if( coInf.companyID == companyID )
{
console.log(coInf);
coInfo = coInf;
}
})
return coInfo;
}
you are not returning value from function but from forEach and that will not return the value from the function.
it has nothing to do with angular, btw.
EDIT
also you can use libraries like lodash/underscore for search/filter functionalities

JQuery plugins: Function using $(this) inside the plugin's options

Hello everyone.
I am trying to develop a Jquery plugin following the steps I found in http://docs.jquery.com/Plugins/Authoring and I seem to have problems reaching the caller object (the “this” variable) inside the options passed to the plugin. It is a plugin that I just want to use to make a button have a “blink” effect.
I would like to be able to pass the functions to execute in “show/hide” (or link blink-on, blink-off, if you prefer) as an option for the plugin. Let's say the user wants to achieve the “blinking” effect by hiding/showing the whole button every 1000 milliseconds. Then I would like the options to be something like:
$("#bttnOk").myBlinker ({
blinkHide: function(){$(this).hide();},
blinkShow: function(){ $(this).show();},
interval:1000
});
// … //
// And to make it actually blink:
$("#bttnOk").myBlinker ("blink");
Or let's say that the user wants to move the button up and down applying an inline css sytle every 200ms. Then the options would something like:
$("#bttnOk").myBlinker ({
blinkHide: function(){$(this).css(“margin-top: 10px”);},
blinkShow: function(){ $(this).css(“margin-top: 0px”);},
interval:200
});
The problem is that I seem to lose the reference to “$(this)” when I am inside the options. When the plugin reaches the blinkHide/blinkShow functions, “this” is the whole DOM window, not the button $(“#bttnOk”) my “myBlinker” plugin is attached to.
This is the first Jquery plugin I'm trying to write so I'm not even sure if there's a way to achieve what I'm trying to do.
My plugin code follows the following structure:
(function($){
var defaultOptions = {
interval: 500
}
var methods = {
init : function( options ) {
return this.each(function(){
this.options = {}
$.extend(this.options, defaultOptions, options);
var $this = $(this);
var data = $this.data('myBlinker');
// If the plugin hasn't been initialized yet
if ( ! data ) {
$this.data('myBlinker', {
on : true
});
}
});
},
destroy : function( ) { // Some code here},
blink: function ( ){
console.log("Blinking!. This: " + this);
var current = 0;
var button=this.get(0);
setInterval(function() {
if (current == 0){
button.options["blinkShow"].call(this);
current=1;
} else {
button.options["blinkHide"].call(this);
current=0;
}
}, button.options["interval"]);
}
};
$.fn. myBlinker = function( method ) {
// Method calling logic
if ( methods[method] ) {
return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
} else if ( typeof method === 'object' || ! method ) {
return methods.init.apply( this, arguments );
} else {
$.error( 'Method ' + method + ' does not exist on jQuery.myBlinker ' );
return null;
}
};
})(jQuery);
Any idea, correction, link or tip will be appreciated.
Thank you.
Within the setInterval function, this is the global object, not the current element DOMElement like in the blink function.
A solution to that is to save a reference of this and use this saved reference in the setInterval:
blink: function ( ){
// save a reference of 'this'
var that = this;
setInterval(function() {
// use the saved reference instead of 'this'
button.options["blinkShow"].call(that);
}, button.options["interval"]);
}
DEMO

Categories

Resources