Firing Events in JavaScript - 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. :-)

Related

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 always call a function from a JavaScript object literal, not only when the object is created

I hope my title isn't too confusing. An example first. I have the following code that configures the read operation for a Kendo UI data source. I am trying to filter all reads based on the selected company id, but my getSelectedCompanyId function is only ever called once, when the page loads. The code below is too long to include all here, so just an excerpt.
$(function () {
function getSelectedCompanyId() {
var id = $("#CompanyId").val();
return id;
}
$("#CompanyId").kendoDropDownList({
change: function () {
grid.dataSource.read();
}
});
var departmentIndexDataSource = new kendo.data.DataSource({
transport: {
read: {
url: '#Url.Action("ListForCompanyIdJson", "Department")' + '?companyId=' + getSelectedCompanyId(),
type: "GET"
},
The ListForCompanyIdJson action is always called with the value selected in the dropdown when $("#CompanyId").kendoDropDownList() is called. I want this function to be called whenever I call grid.dataSource.read().
I realize this might be highly specific to the Kendo stuff, but maybe it's something I can solve with plain JavaScript closures and some help.
You could install a replacement function for grid.dataSource.read() that always calls your function first and then calls grid.dataSource.read().
For example:
grid.dataSource.oldRead = grid.dataSource.read;
grid.dataSource.read = function() {
// call your function here
return grid.datasource.oldRead.apply(this, arguments);
}
Or, if you want to be able to call one sometimes and one other times, you can just make a new method that provides the new behavior and use it when you want to:
grid.dataSource.myread = function() {
// call your function here
return grid.datasource.read.apply(this, arguments);
}

jquery variable scope problem

I have a chat class with two methods: updateChat and sendChat.
//chat.js
var state;
var room;
function Chat (theRoom) {
this.update = updateChat;
this.send = sendChat;
this.room = theRoom;
}
function updateChat(){
alert('ROOM: '+this.room);
$.ajax({
type: "POST",
url: "/chat/process.php",
data: {
'function': 'update',
'state': state,
'room': this.room
},
dataType: "json",
success: function(data){
if(data.text){
for (var i = 0; i < data.text.length; i++) {
$('#chat-area').append($("<p>"+ data.text[i] +"</p>"));
}
}
if(data.state)
state = data.state;
}
});
}
}
//send the message
function sendChat(message, nickname)
{
alert('A'+state); //20
//XXX
updateChat();
alert('B'+state); //20
$.ajax({
type: "POST",
url: "/live-event/chat/process.php",
data: {
'function': 'send',
'message': message,
'nickname': nickname,
'room': this.room
},
dataType: "json",
success: function(data){
alert('C'+state); //wrong!: 2 //it should be 20!
//XXX
updateChat();
alert('D'+state); //21
},
});
}
The constructor of the chat object:
var chat = new Chat(4); //4 = the number of the chat room
chat.send('test', 'tester');
My problem are the method calls at the locations marked with XXX.
In the updateChat() method, this.room is undefined if I call the updateChat methods like that.
But I need to pass the room number to get the right state (state is simply the number of lines in the chat room's text file).
I think it's a problem with variable scope or with the methods not being called in the context of the object.
You need to maintain this when calling those methods, so instead of this:
updateChat();
You can use .call() to maintain context (so this doesn't revert to window inside the called function), like this:
updateChat.call(this);
Or call the method on the object as #casablanca points out below:
this.update();
There also one more issue, this won't be what you want in your $.ajax() callbacks, it'll be the ajax settings object by default, so you need to set the context option to maintain it, like this:
$.ajax({
context: this,
type: "POST",
//...rest of your current options/methods
You may find this easier to grasp if you forget about classes. What you've written there is not a class. There are no classes in JavaScript. You've written a constructor function, but it's of somewhat dubious value because you're assigning the members individually for every instance. The main purpose of constructor functions is to take advantage of prototype-inheritance, so you'd assign the "methods" to the constructor function's prototype.
function Chat (theRoom) {
this.room = theRoom;
}
Chat.prototype.send = sendChat;
Chat.prototype.update = updateChat;
That way, each object created with new Chat(r) will only need to store the room number, and will not need to store the two methods as properties.
Alternatively, just write a createChatRoom function:
var createChatRoom = function(room) {
return {
update: function() {
alert('updating: ' + room);
// ... other stuff
},
sending: function() {
alert('sending: ' + room);
// ... other stuff
}
};
};
The beauty of that is probably obvious: you don't need to prefix anything with this. The room parameter is in scope to the method definitions, and is also truly private (cannot be modified except through calls to the methods. And the caller doesn't have to remember to put new. They just call the function and get back a fresh object with two methods in it.
You can even safely do this:
setTimeout(chatRoom.update, 10);
The update function "knows" what object it is associated with. It never needs to be told which this to use.
This is so convenient and useful, unless I'm expecting to create very large quantities of objects, I don't bother with constructor functions, new, prototype, etc.

JQuery/JavaScript : refactoring nested functions

I have this interesting jQuery function. It basically adds a click handler to link, and when that is clicked, it will load a form to allow the user to edit the content. and the form is submitted by AJAX, and will display a success message when it's done.
The outline is below; needless to say, this is messy. I could have each of the callback as a class method. What other ways are there to refactor nested functions? I am also interested to see if there are ways that variables declare in a parent function still retain its value down to the nested function after refactoring
$('a.edit').click( function() {
// ..snipped..
// get form
$.ajax({
success: function() {
// add form
// submit handler for form
$(new_form).submit(function() {
// submit via ajax
$.ajax({
success: function(data) {
// display message
}
})
})
}}
)
}
I guess the interesting part of your question is how to refactor without loosing access to the closure variables. Here is my suggestion:
Version one: nested, with closures and variable access:
var a;
$('a.edit').click( function() {
var b;
$.ajax({
success: function() {
var c;
$(new_form).submit(function() {
var d;
$.ajax({
success: function(data) {
// a,b,c,d are all visible here.
// note that a references the same object for all calls of the success function, whereas d is a different variable for each call of submit.
// this behaviour is called closure: the 'enclosed' function has access to the outer var
}
})
})
}
})
})
Version two: less nested, but without closures and without variable access:
var a;
$('a.edit').click(onEdit);
var onEdit = function() {
var b;
$.ajax({success: onEditSuccess});
};
var onEditSuccess = function() {
var c;
$(new_form).submit(onSubmit);
};
var onSubmit = function() {
var d;
$.ajax({success: onSubmitSuccess});
}
var onSubmitSuccess = function(data) {
// a is visible (global var)
// b,c,d NOT visible here.
};
Version three: less nested and with unnamed functions and parameters to get access to the closure variables:
var a;
$('a.edit').click(function(){onEdit(a)});
var onEdit = function(a) {
var b;
$.ajax({success: function(){onEditSuccess(a,b)}});
};
var onEditSuccess = function(a,b) {
var c;
$(new_form).submit(function(){onSubmit(a,b,c)});
};
var onSubmit = function(a,b,c) {
var d;
$.ajax({success: function(data){onSubmitSuccess(data,a,b,c,d)}});
}
var onSubmitSuccess = function(data,a,b,c,d) {
// a,b,c,d are visible again
// nice side effect: people not familiar with closures see that the vars are available as they are function parameters
};
You can easily refactor this to make it much more readable. The key concept to grasp is that you can refer to named functions in callbacks as well as anonymous ones. So, for instance:
function clickHandler() {
alert("Link clicked");
}
$('a').click(clickHandler);
My preference is always to give the functions names according to what they do (e.g. loadImage, rather than the event that you intend to trigger them (e.g. clickLink. This makes your code clearer and makes later changes much easier. In this case, I would structure my code like this:
$(document).ready(function(){
$('a.edit').click(loadFormStart);
function loadFormStart() { // get form
$.ajax({
success: loadFormEnd
});
}
function loadFormEnd(data) { // add form & handler
$('new_form').submit(handleFormStart);
}
function handleFormStart() { // submit form
$.ajax({
success: handleFormEnd
});
}
function handleFormEnd(data) { // receive form data
//display message
}
});
I'd also advise you to read Code Organization on jqfundamentals which gives a similar approach to this using an object literal.
Interesting question. Personally I don't mind the above. Commenting is key, so you could consider qualifying the closing braces with some:
} //success: function(data)
}) //$.ajax({
}) //$(new_form).submit(
...etc
I would also look at aligning the brackets correctly (at first clance, your }} is a little mystifying).
If it comes to 'generic' nesting strategies, the only other suggestion I have is to move code out other functions. The of course means that you have the function decalred in memory, but may make it more readable.
You could also consider a specific strategy that relates to this code. For example, rather than manually binding a submit to new_form can you use the live function in some way to ensure that it is done automatically?
On a completely unrelated note, you should probably add some ; at the end of each of the bracketed lines!

Categories

Resources