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

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.

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

How can we make a JavaScript property global?

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.

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();
}());

Can I bind something else than 'this' for a function? [duplicate]

This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 7 years ago.
I need to pass outside variable to jQuery Ajax's success-function. My ajax is in a loop like so:
var props = ['a', 'b', 'c'];
var results =
{
a: null,
b: null,
c: null
};
for(var key in props)
{
var prop = props[key];
$.ajax(
{
url: 'someurl',
data:
{
somedata: 'somevalue'
},
success: function(data, status, xhr)
{
// This here does not work properly, because the prop's value changes
// Due to promise stuff
results[prop] = data;
}
});
}
I need to stuff the data returned in success in to the correct place, but the implementation above does not work because the value of prop changes before the success is called.
I found out that I can "bypass" this problem by binding the success-function's this with the prop like so:
success: function(data, status, xhr)
{
results[this] = data;
}.bind(prop)
But this does not seem like a very good idea and besides, I can only bind one variable, not multiple if need be.
This does not seem to work either:
success: function(data, status, xhr)
{
var privprop = prop;
results[privprop] = data;
}
So basically what is the best way to give each success callback a "private" variable, which value is what the value of prop was when the whole ajax was made? Can I somehow bind multiple variables and overwrite something else than this?
Try to limit the scope of every prop variable for a specific ajax by doing something like this:
for(var key in props)
{
var prop = props[key];
(function(prop){
$.ajax(
{
url: 'someurl',
data:
{
somedata: 'somevalue'
},
success: function(data, status, xhr)
{
// This here does not work properly, because the prop's value changes
// Due to promise stuff
results[prop] = data;
}
});
})(prop);
}

Firing Events in JavaScript

I have a JavaScript class named 'Item'. 'Item' is defined as shown here:
function Item() { this.create(); }
Item.prototype = {
create: function () {
this.data = {
id: getNewID(),
}
},
save: function() {
$.ajax({
url: getBackendUrl(),
type: "POST",
data: JSON.stringify(this.data),
contentType: "application/json",
success: save_Succeeded,
error: save_Failed
});
},
function save_Succeeded(result) {
// Signal an event here that other JavaScript code can subscribe to.
}
function save_Failed(e1, e2, e3) {
// Signal an event here that other JavaScript code can subscript to.
}
}
Please note, I'm coming from a C# background. So I'm not even sure if what I want to accomplish is possible. But essentially, I want to create an object, subscribe to some event handlers, and attempt to save my object. For instance, I envision doing something like the following throughout my code.
var i = new Item();
i.item_save_succeeded = function() {
// Do stuff when the item has successfully saved
};
i.item_save_failed = function() {
// Do stuff when the item has failed to save
};
i.save(); // start the save process
Is this event-based approach even possible in JavaScript? If so, how? What am I missing? I keep getting a variety of errors that are vague. Because of that, I'm not sure if I'm getting closer or farther away.
If you are using jQuery, you can add an event handler to a custom event type.
The following snippet is taken from the jQuery docs
$('#foo').bind('custom', function(event, param1, param2) {
alert(param1 + "\n" + param2);
});
$('#foo').trigger('custom', ['Custom', 'Event']);
But since jQuery 1.7 deprecates bind, you should use on now. See the jQuery docs for on.
Not 100% sure and I look forward to seeing the answer from a JS pro, but here is what I would do.
Expose some properties within you Item object - namely the functions you wish to be subscribed to.
Upon instancing an item you could then provide callback functions for the events that you wish to be notified of. In your code you could then do something like this:
save: function() {
var self = this;
$.ajax({
url: getBackendUrl(),
type: "POST",
data: JSON.stringify(this.data),
contentType: "application/json",
success: function() { if(typeof(self.success) == "function") self.success(); }
error: function() { if(typeof(self.fail) == "function") self.fail(); }
});
},
In effect, pass the callback functions to the object and let it call them directly when needed. I'm sure someone will now suggest a better way of doing it. :-)

Categories

Resources