How can we make a JavaScript property global? - javascript

Please refer the below example code
var report = {
chartTypes : null,
init: function () {
this.getChartTypes(function(data){
this.chartTypes = data;
});
},
getChartTypes: function(callback) {
$.ajax({
data:'',
url:'',
success:function(response){
chartTypes = JSON.parse(response);
callback(chartTypes);
}
});
},
getToolbar:function() {
this.chartTypes --------------- NULL
}
}
getChartTypes function load different chart types via AJAX. Therefore i put it as a callback function. Data is received successfully. But when i use this.chartTypes in a different function like getToolbar it says this.chartTypes is null. Even i have initialized the same in the starting. May be scope issue. Please advise.

You are assigning to a variable (probably global) called chartTypes, but that isn't the same as reoprt.chartTypes. You need to assign to this.chartTypes, but this in your anonymous function won't be the same as this outside it, so you need to remember that value using another variable:
getChartTypes: function(callback) {
var self = this;
$.ajax({
data:'',
url:'',
success:function(response){
callback( self.chartTypes = JSON.parse(response) );
}
});
}

With an OOP approach, most developers would use a method and use .bind() to maintain the proper scope when the asynchronous success method is triggered. This way you do not have to worry about closures and using variables to hold the scope of this.
var report = {
chartTypes : null,
init: function () {
this.getChartTypes();
},
getChartTypes : function(callback) {
$.ajax({
data:'',
url:''
}).done(this._setSuccessResponse.bind(this));
},
_setSuccessResponse : function(data){
this.chartTypes = data;
},
getToolbar : function() {
console.log(this.chartTypes);
}
}
You also need to make sure that when you call getToolbar that the Ajax call has also been completed.

Related

Expose function and return object

I am trying to create an ajaxHelper module that should be able to expose some number of functions, and when they are called, should return a helper object that has either the data retrieved from an AJAX call, or errors associated with that AJAX call.
Here is something along the lines of what I am thinking:
define("helpers-ajaxDataRetriever", ["jquery"], function ($) {
var helper = {};
helper.getIndexData = function() {
fnIndexData();
return helper;
}
var fnIndexData = function () {
$.ajax({
url: nwatchBaseUrl + '/api/HomeApi/NodeSummary'
}).success(function (returnedData) {
helper.success = true;
helper.data = returnedData;
}).fail(function (jqXHR, textStatus) {
helper.success = false;
helper.error.jqXHR = jqXHR;
helper.error.textStatus = textStatus;
});
}
});
I then want other modules that import this ajaxHelper to be able to call the function (such as getIndexData), which would ultimately populate the helper object, and then be able to reference the various properties, such as the boolean success, data, or error objects.
How do I go about doing this?
For it to work the way you are expecting, the module has to return the properties that you like to expose to the outside world ( for it to be used by other modules).
And since ajax is asynchronous, it is better you tackle such scenarios with callbacks instead accessing the variables directly. As you do not know when the ajax call will successfully complete and return you the data.
define("helpers-ajaxDataRetriever", ["jquery"], function($) {
var helper = {};
// you will pass in the options
// which will contains the success and error
// callbacks, along with additional props
// that you wanna pass in and use
helper.getIndexData = function(options) {
fnIndexData(options);
}
var fnIndexData = function(options) {
$.ajax({
url: options.nwatchBaseUrl + '/api/HomeApi/NodeSummary'
}).success(function(returnedData) {
options.success && options.success.apply(null, arguments);
}).fail(function(jqXHR, textStatus) {
options.error && options.error.apply(null, arguments);
});
}
// You return the object, which are the public methods
// or properties which you wanna expose when this module is used
return {
getIndexData: getIndexData
}
});
// This is when you wanna use the above exposed function
// in any module
define("use-ajax", ["helpers-ajaxDataRetriever"], function(customAjax) {
var options = {
success: function(data) {
console.log('success');
// use the data
}, error: function(jqXHR, textStatus) {
console.log('failure');
// you will have access to the
// arguments of the error function here
},
nwatchBaseUrl: 'https://google.com/'
}
customAjax.getIndexData(options);
});
And since we only want to expose getIndexData in the above example, we can completely get rid of the helper namespace and just return the function definition.
You could also achieve the save by using the concept of promise

Using parent object's properties inside jQuery function and other methods

First of all: I don't know exactly how to call everyting since I am quite new to the more OOP way of writing javascript, so I'll try to explain everything as good as possible.
My problem is that I want to access properties inside an object ( so I can use the this-keyword. This works fine as long as I am in the scope of the object. When I go outside the scope, I would like to access those properties while I can't use the this-keyword anymore.
My code:
var Octa = Octa || function () {
this._initialize();
};
Octa.prototype = {
string: 'Foo',
_initialize: function () {
console.log(this.string); //Output: "Foo"
this.othermethod();
}
}
var Octa = new Octa();
But when I have a method within an Octa method, so outside the scope where I can't use this anymore to get Octa's properties, I can't reach the properties within Octa.
For example:
othermethod: function () {
$.ajax({
url: this.globalUrl + 'content/language/lang.' + l + '.php',
data: {
ajax: true
},
dataType: 'json',
success: function (response) {
Octa.lang = response;
}
});
console.log(JSON.stringify(this.lang)); //Output: null, which means Octa.lang wasn't reachable in the ajax success event (the ajax request was successful).
}
Is there a way to reach the scope of Octa within other objects? Or within jQuery callbacks since the same problem occurs there.
I hope my problem is understandable and if not, I'll try to give more clarification.
Simply refer back to this inside the function scope:
...,
someMethod: function () {
var self = this,
ajaxOptions = this.settings.ajaxOptions;
// note we can still refer to 'this' at this level
$.ajax(ajaxOptions).done(this.ajaxDone).fail(this.ajaxFail);
// the function scope changes for the deffered handlers so you can access by reference of 'this' => self
$.ajax(ajaxOptions).done(function(data, status, xhr){
self.ajaxDone(data, status, xhr)
}).fail(function(xhr, status, error){
self.ajaxFail(xhr, status, error);
});
},
ajaxDone: function(data, status, xhr) {},
ajaxFail: function(xhr, status, error) {},
...
Hope this makes sense.
Now there's also a .bind() function that can be used to bind function scope to a parameter:
$.ajax(ajaxOptions).done(function(){
this.ajaxDone();
}.bind(this));
You'll have to use a polyfill to support older browsers. It's much more easier to use var self imho.

JS Revealing Pattern event undefined issue

I am using the modular design pattern for JS and I keep running into issues when using arguments bound functions. I have a particular function that I would like to bind to different events to keep from having to write the function for each bound event. The only difference in the function, or the argument, is the table that will be updated. The problem is that when I build a function with the arguments I need and pass those arguments to bound events, I get an undefined error, in the console, on load. Keep in mind, I want to stick with this design pattern for the security it offers.
Here is my JS:
var Users = (function(){
var $addRoleForm = $('#addUserRole');
var $rolesTableBody = $('#table-roles tbody');
$addRoleForm.submit(ajaxUpdate(event, $rolesTableBody));
function ajaxUpdate(event, tableName) {
event.preventDefault();
event.stopPropagation();
var url = this.action;
var data = $(this).serialize();
var $this = $(this);
$.ajax({
type: 'POST',
url: url,
dataType: 'json',
data: data,
success: function(data) {
if(data.st === 0){
$messageContainer.html('<p class="alert alert-danger">' + data.msg + '</p>');
setTimeout(function(){
$messageContainer.hide();
}, 7000);
} else {
$messageContainer.html('<p class="alert alert-success">' + data.msg + '</p>');
tableName.fadeOut().html('').html(data.build).fadeIn();
$this.find('input').val('');
setTimeout(function(){
$messageContainer.hide();
}, 7000);
}
},
error: function(xhr, status, error){
console.log(xhr.responseText);
}
});
}
})();
Here is the error I get in the console, on load:
Uncaught TypeError: Cannot read property 'preventDefault' of undefined
I have tried to bind the event like this: $addRoleForm.on('submit', ajaxUpdate(event, $rolesTableBody)); and receive the same results.
Any ideas how to fix this?
You're seeing that issue, because the way you have it written now, ajaxUpdateexecutes, returns undefined and THEN passes undefined to the event listener, so you're basically doing this: $addRoleForm.submit(undefined).
2 Choices here:
1) You can wrap it in an anonymous function:
$addRoleForm.submit(function(event) {
//pass the value of "this" along using call
ajaxUpdate.call(this, event, someValue);
});
$someOtherForm.submit(function(event) {
//pass the value of "this" along using call
ajaxUpdate.call(this, event, someOtherValue);
});
2) You can set the first argument in-advance using bind:
$addRoleForm.submit(ajaxUpdate.bind($addRoleForm, someValue));
$someOtherForm.submit(ajaxUpdate.bind($someOtherForm, someOtherValue));
Using this way, you're binding the value of this to be $addRoleForm, setting the first argument to always be someValue, so it's the same as:
ajaxUpdate(someValue, event) {
//value of "this" will be $addRoleForm;
}
To pass the event, and the custom argument, you should be using an anonymous function call
$addRoleForm.submit(function(event) {
ajaxUpdate(event, $rolesTableBody));
});
This is by far the easiest and most readable way to do this.
What you're doing right now equates to this
var $addRoleForm = $('#addUserRole');
var $rolesTableBody = $('#table-roles tbody');
var resultFromCallingFunction = ajaxUpdate(event, $rolesTableBody); // undefined
$addRoleForm.submit(resultFromCallingFunction);
Where you're calling the ajaxUpdate function, as that's what the parentheses do, and pass the returned result back to the submit callback, which in your case is undefined, the default value a function returns when nothing else is specified.
You could reference the function, like this
$addRoleForm.submit(ajaxUpdate);
but then you can't pass the second argument
The question refers to the Revealing Module pattern. Benefit of using this design is readability. Going with the anon function may work, but defeats the overall purpose of the module pattern itself.
A good way to structure your module to help maintain your scope is to setup helper functions first, then call a return at the end.
Example use case with events:
var User = function() {
// local VARS available to User
var addRoleForm = document.querySelector('#addUserRole');
var rolesTableBody = document.querySelector('#table-roles tbody');
// Helper function 1
function ajaxUpdate(tableName) {
...
}
// Helper function 2
function someFunc() {
...
}
function bindEvents() {
addRoleForm.addEventListener('submit', ajaxUpdate, false);
addRoleForm.addEventListener('click', someFunc, false);
}
function init() {
bindEvents();
}
return {
runMe:init
}
}().runMe();
Helps to "modularize" your workflow. You are also writing your revealing pattern as an IIFE. This can cause debugging headaches in the future. Editing the IIFE to instead invoke via the return is easier to maintain and for other devs to work with and learn initially. Also, it allows you to extend outside of your IFFE into another Module, example:
var Clothes = function() {
function anotherFunc() {
...
}
init() {
User.runMe();
anotherFunc();
}
return {
addClothes: init
}
}().addClothes();
I hope this helps to give you a better understanding of how/when/why to use the JS revealing pattern. Quick note: You can make your modules into IIFE, that's not a problem. You just limit the context of the scope you can work with. Another way of doing things would be to wrap the var User and var Clothes into a main module, and then make that an IIFE. This helps in preventing polluting your global namespace.
Example with what I wrote above:
// MAIN APPLICATION
var GettinDressed = (function() {
// MODULE ONE
///////////////////////////
Var User = function() {
// local VARS available to User
var addRoleForm = document.querySelector('#addUserRole');
var rolesTableBody = document.querySelector('#table-roles tbody');
// Helper function 1
function ajaxUpdate(tableName) {
...
}
// Helper function 2
function someFunc() {
...
}
function bindEvents() {
addRoleForm.addEventListener('submit', ajaxUpdate, false);
addRoleForm.addEventListener('click', someFunc, false);
}
function init() {
bindEvents();
}
return {
runMe:init,
style: someFunc
}
}();
// MODULE TWO
//////////////////////////
var Clothes = function() {
function anotherFunc() {
...
}
init() {
User.style();
anotherFunc();
}
return {
dressUp: init
}
}();
// Define order of instantiation
User.runMe();
Clothes.dressUp();
}());

How to create global variable in query and usable with javascript

$(document).ready(function ()
{
$.ajax(
{
url: "Bibliotheek.xml",
dataType: "xml",
success: function (data)
{
var song = $(data).find('key').filter(function ()
{
return $(this).text().indexOf('Name') != -1;
}).each(function()
{
window['globalVar']= $(this).next('string').text();
console.log(globalVar);
});
}
});
});
I want to use globalVar outside that each loop. But once i put de console.log outside the function. It tells my globalVar is undefined.Is it also possible to use that variable later on in javascript code?
This probably happens, because you loop over an empty list (i.e. it never enters the .each callback). This thing is wrong: .find('key'). It searches for a key tag (which is not HTML, unless you actually are not dealing with HTML?). Perhaps you were looking for .find('.key')?
EDIT: It seems that you want to put console.log outside of ajax call. If you do, then you're out of luck, since you are trying to log a variable that does not exist yet. That's because a in ajax stands for asynchronous, i.e. the piece of code will run later.
EDIT 2: Welcome to asynchronous programming! It seems that you are trying to force ajax to be synchronous, which is wrong and pure eveil. Don't do it. You're code should be similar to this:
var my_fn = function(clb) { // <-- this is callback to be called later
var els = [];
$.ajax({
url: "Bibliotheek.xml",
dataType: "xml",
success: function (data) {
var song = $(data).find('key').filter(function () {
return $(this).text().indexOf('Name') != -1;
}).each(function() {
var el = $(this).next('string').text();
els.push(el);
});
clb(els); // <-- call it now
}
});
};
$(document).ready(function() {
my_fn(function(els) {
console.log(els);
// do coding here
});
});
Define the globalVar outside of the functions...
var globalVar;
var song = {...
console.log(globalVar);//will work here
};
console.log(globalVar);//and, will work here

Passing a reference to an object to a callback function in jQuery [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
$.ajax context option
I've got some code that looks like this:
$.post(self.baseUrl + 'api/method',
{param1: data},
function (response) {
// Do something
});
I want to pass a reference to the self object through to the callback, which I imagined would be something like this:
$.post(self.baseUrl + 'api/method',
{param1: data},
function (response, self) {
// Do something
});
However, it doesn't work like this, the jQuery documentation doesn't show a way that would make this possible and a cursory Google search hasn't turned up anything. Is this possible, and how can I do so?
If you use the $.ajax method you can specify a context:
$.ajax({
type: "post",
context: self,
data: {param1: data},
success: function (response) {
console.log(this); // now 'this' refers to self
}
});
#karim79 shows the best solution. I just want to show some other possible ways
var App = {
baseUrl: "http://.../",
fetchData: function() {
var self = this;
$.post(self.baseUrl + 'api/method', {
param1: data
}, function(data) {
self.onDatafetch(data);
//or
globalDataFetch(data, self);
});
},
onDatafetch: function(data) {
this.showMsg();
},
showMsg: function() {
alert("Success");
}
}
App.fetchData();
function globalDataFetch(data, object){
// received data and object
}
why do you want to do sth like that?
you could easily use a closure:
var param1 = data;
$.post(self.baseUrl + 'api/method', function (data) {
// access param1 here
});

Categories

Resources