Sending data from classes? - javascript

I'm looking into deferred and custom events. I'm not sure what method would suit my application.
I have a class that calls another class. Inside this class a user can drag files on to the window.
Once the file has been dragged on, I wish to send details about the files to my main class.
I've thought about using deferred, but the user needs to drag files over and over again, and as of my understanding this can only be used once.
So in my main class I:
this.dropBox = new DropBox();
Then in the DropBox class I have:
$(window).on('drop', this.drop);
But what should I put in my 'drop' method. Every time something is dropped I wish to 'alert' my main class about it and act upon it. How can I 'listen' for the event. Should I use deferred, custom event or something else?

There are typically two options for this:
Delegate
A delegate should implement a certain "interface", a set of functions that handle certain events.
function DropBox(delegate)
{
this.delegate = delegate;
$(window).on('drop', $.proxy(this, 'drop'));
}
DropBox.prototype.drop = function(e) {
// do stuff with event
this.delegate.drop(e);
}
// inside main instance
this.dropBox = new DropBox(this);
// delegate interface
this.drop = function(e) {
// handle file drop
};
Callback
If the delegate only needs one function, you can use a callback as well:
function DropBox(dropEventHandler)
{
this.dropEventHandler = dropEventHandler;
$(window).on('drop', this.drop);
}
DropBox.prototype.drop = function(e) {
this.dropEventHandler(e);
};
// inside main instance
var self = this;
this.dropBox = new DropBox(function(e) {
// handle file drop
// use 'self' to reference this instance
});

Why not just give a callback over to the DropBox?
Well, like this code in the main class:
this.dropBox = new DropBox(function(fileInfo) {
// this code can be executed by the DropBox Object multiple times!
});
And the DropBox:
window.DropBox = function(callback) {
this.userHasDroppedFiles = function(fileinfo) {
// do stuff
callback(fileinfo); // give the fileinfo back with the callback!
}
}
Also, there are no classes in JavaScript! You have only Objects, and you can use constructor-functions combined with prototypes to generate a class like behaviour, but you will never actually have classes like in Java, C# or similar languages. Keep this always in mind.
Some JS frameworks build their own class layer on top of the main JS possibilities, then you may have Framework classes, but also never native JavaScript classes, because native JavaScript classes dont exist!

Related

Observe a JS event, when you only know PART of the event name?

I've inherited some JS (that I can't change) that fires a bunch of events:
jQuery(document).trigger('section:' + section);
// where "section" changes dynamically
And I want to observe for ALL of these events, and parse out the value for section, and do something different depending on it's contents.
If it didn't change I could do this:
jQuery(document).on('section:top', doStuff );
But how do I observe an event if I only know the first part of that event name?
You cannot listen for all events in the style of $().on('section:*'), unfortunately. If you can change the code, I would do the following:
jQuery(document).trigger({
type: 'section',
section: section
});
Then you listen for it and don't need to parse anything out
jQuery(document).on('section', function(e){
if (e.section === 'top') {
// Something happened to the top section
}
});
If you want to minimize your code changes, leave the old event in there, that way existing code will be unaffected.
A different approach would be to use event namespaces.
jQuery(document).trigger('section.' + section);
jQuery(document).on('section', function(e){
if (e.namespace === 'top') {
// Something happened to the top section
}
});
I, however, prefer the first approach because event namespaces are most commonly used for a different purpose: to be able to remove events without being forced to keep a reference to the handler itself. See http://css-tricks.com/namespaced-events-jquery/ and http://ejohn.org/apps/workshop/adv-talk/#13. I prefer to use styles that other developers are used to, if they do the job.
I'm really not sure about your use case but you could overwrite $.fn.trigger method:
(function ($) {
var oldTrigger = $.fn.trigger;
$.fn.trigger = function () {
if (arguments[0].match(/^section:/)) {
doStuff(arguments[0].split(':')[1]);
}
return oldTrigger.apply(this, arguments);
};
})(jQuery);
var section = "top";
jQuery(document).trigger('section:' + section);
function doStuff(section) {
alert(section);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
Here's what I ended up doing.
It's a combination of Juan Mendes's solution, and using a method from the prototype library
Originally, there was a function that ran this code:
myObject.adjustSection(section) {
jQuery(document).trigger('section:' + section);
}
// I couldn't edit this function
So I extended the function with prototype's wrap method, since my project used prototype as well as jQuery.
// My custom function wrapper
// extend adjustSection to include new event trigger
myObject.prototype.adjustSection = myObject.prototype.adjustSection.wrap(
function(parentFunction, section) {
// call original function
parentFunction(section);
// fire event w/section info
jQuery(document).trigger({
type: 'adjustSection',
section: section
});
}
);
Then, it runs the original one, but also fires my custom event that includes the section info.
Now, I can do this to observe that event and get the section type:
jQuery(document).on('adjustSection', function(event) {
event.section; // contains the section I need
});
Of course, this means I have to utilize both prototype and jquery within the same scope, which isn't the best thing in the world. But it worked.

Event based editor like in StarCraft 2 Editor (algorithm)

I'm trying to create an algorithm for an event based editor like in StarCraft 2 Editor that can support:
Create UI
Play sounds
Handle keyboard/mouse inputs
Display messages
Button(or some referenced UI object) is pressed etc.
Pretty much the same thing as in StarCraft 2 Editor (of course not the 3D stuff too)
So far I'm thinking to use JSON , add every event in an object and then loop through them and create an event using the addEventListener() method.
The JSON Events Object(of course it will be created by the user in the editor with no programming):
var Events={
//your event's names here
onReady:{ //on page ready to manipulate
displayMessage:{//just a simple popup
text:"Hello user!",
title:"Welcome!",
type:"normal",
},
createButton:{ //creates a buton on the screen
text:"Click me!",
id:"myButton"
}
},
onClick:{
id:"myButton" ,//the id of the button we just created
actions:{ //the actions applied after we click the button
displayMessage:{//just a simple popup
text:"You pressed me!",
title:"Button",
type:"error",//show the message as an error
}
}
}
}
I found some softwares (GameMaker,Construct 2,GameDevelop) that have an event based editor if you would like to get an idea about what I'm talking about (if you don't already know about StarCraft 2 Editor)
My question is:
What is the best algorithm that I can use to achieve this?
Sounds like a job for jQuery UI.
When the user creates a custom area in your editor all it's attributes are stored inside an object (that you can save as JSON) that would then be applied to a div as param when loading the map (using html-attributes.
function create_areas(areas){
var map = $('#map_area');
for(var i=0;i<areas.length;i++){
map.append($('<div>', area[i].params));
}
}
whereas params would look something like this:
params = {
width: 100,
height: 200,
....
mousedown: function(){ play_music('hello'); },
keydown: function(e){ alert('you pressed ' + e.keyCode; }
}
also the jQuery UI tools like draggable and resizeable should ease up building your editor.
I'd model this more after backbone's event system:
events: {
'click selector': handler,
'mouseover selector': handler2,
...
}
Handlers can be any javascript function, this would allow you to create a bunch of pre-defined functions like displayMessage.
Then you could curry your own handlers, which would allow your users to specify configuration if they need it.
Example:
var events = {
'click element': displayMessage({
text:"Hello user!",
title:"Welcome!",
type:"normal",
}),
'mouseover pizza': createButton({...})
}
function displayMessage(options) {
var options = options;
return function() {
//display message logic
}
}
Then you can supply a compose function among other helpers (look up promises perhaps?) to combine your functions together:
var events = {
'click element': compose(
displayMessage({
text:"Hello user!",
title:"Welcome!",
type:"normal",
}),
createButton({})
),
'mouseover pizza': createButton({...})
}
This could work out?
Caveat: it might be better if events was an array that contained objects. That way you can have multiple click handlers on some selector without collisions.
The way I see this there are really severall choices you need to make. I would, although I prefer JSON as a data construct not limit myself to this subset of an actuall programming language. And engener this the other way around.
You have events, handlers and options. Where a option, or better a option list is the user inputed data, the handlers are the actual action, and the events are triggers to set some action off.
If you read this carefully you will notice this is the exact description of the basic structure of most jQuery-Scripts or Event-Driven Software in generall. Only the users options in jQuery are (since it is a DOM Framework) most often the context of a single DOM-Element. So, here we are and I would suggest to simply borrow the theorie behind this and make use of promisses wich make a very clear and great way to generate code!
So my call to any event chain would look like this.
...when(chainObject['event'])
.then(function(event) {
//call handler
handlers[chainObject[selectedHandler]].call(event.context, chainObject['options']);
//apply next element(s) in chain, this is the current promise
appendNextElement(chainObject['followingHandlers'], this);
})...
Notice how apply makes it easy for you to change the environement and in turn behaviour of any hanlder based on what the user and event did. And promisses make error handling very easy!
This of course applies to only one node in your chain. So what should a data structure look like to let you generate this kind of code?
One node in your structure would look like this:
{
event: 'click',
selectedHandler: 'sohwText',
options: {
'text': 'helloWorld'
},
followingChain: {...OTHER HANDLERS....}
}
The important thing to notice is that like a good structured functional programm you are looking at a tree and not at a simple list of events. So every actual DOM Element holds many of these
var eventTree = {
'.someButton': [..Handlers of this button...],'
'.someOtherButton': [..Handlers of the other button...],
}
And there we go. You have a context (the button), a event, user input and a handler.
The resulting app should not only work, but will be styled for any experienced JavaScript-Programmer to expand or mod.

Ajax Client Control - memory leak

I am creating an Ajax Client Control in ASP.Net. By inheriting from IScriptControl and then adding the relavant javascript class (which would inherit from a javascript control). I have found a memory leak in the following code:
Type.registerNamespace("mynamespace");
myClass = function (element) {
myClass.initializeBase(this, [element]);
}
myClass.prototype = {
initialize: function () {
myClass.callBaseMethod(this, 'initialize');
var me = this;
$(document).ready(function () {
me._initializeControl();
me._hookupEvents();
});
},
dispose: function () {
//Add custom dispose actions here
myClass.callBaseMethod(this, 'dispose');
},
//...other code ...
_hookupEvents: function () {
var me = this;
var e = this.get_element();
$("#viewRates", e).click(function () {
me.openDialog();
});
},
//...other code...
myClass.registerClass('myClass', Sys.UI.Control);
if (typeof (Sys) !== 'undefined') Sys.Application.notifyScriptLoaded();
_hoookupEvents is a function in my javascript file. The leak is related ot the line me.openDialog. If I remove this line, there is no leak. However, I need this line to be able to call a function from the class (I cannot just use 'this' in the function because it would refer to the button). Is there a better way to do this? Or maybe I just need to call some methods in the dispose function to clean such things?
The memory leak at this code can happen on this line, as you also note
$("#viewRates", e).click(function () {
me.openDialog();
});
when you call it with UpdatePanel, or in general call it for the same component and with out first clear the previous events for the click, the previous handler stay on, and here we have two cases.
To register the same click event more than ones.
To update the dom with ajax, and not previous clear that handlers, as results the previous code stay for ever (for ever == until you leave the page).
In general the solution is to clear any previous handler for the click,
before add a new one.
when initialize a new ajax call with UpdatePanel and before get the new response.
Use a function like that to remove the click and clear the resource for the handler.
this.get_events().removeHandler('click');
I'm extremely hesitant to call it a memory leak if there are only 2 instance of myclass. If there are 2,000 instances of myclass there's DEFINITELY a leak.
I'd search real hard for any dynamic instantiation statements that you have, that create myClass on certain conditions. That is what i see a lot (creating classes in loops at application init, perhaps a form submit can trigger instantiation and it wasn't fully QA'd to see if you can get a submission to create multiple objects, etc).

How do I dispose of a prototype in JS?

I've got a thorny issue and although the answer may be obvious, I cannot see how to do what I'm trying to do.
I have created a script library for my application that uses JS prototypes and templating to dynamically instantiate DOM elements AND to wire those elements up with handlers. An example is the following:
var ppane = new AWP.iuiPanel(theObject, { title: 'Select filter(s)', idDisplay: 'block', idString: params.sender._options['title'] });
AWP.iuiPanel is a class defined as a function prototype, e.g:
AWP.iuiPanel = function() { <i'm a constructor> }
AWP.iuiPanel.prototype = { <a bunch of methods here> }
The methods inside the instance create a DOM element (in this case a floating panel) and establish event bindings for it, wire up its control elements, etc.
The advantage of going down this path is that through a single call to create a new instance of a class I can also build the associated DOM element, and once instantiated, the class methods that have been wired up will execute against the element to do things like position it relative to a target object, respond to relevant browser events, etc.
The problem I have is when I want to dispose of this construct. I can dispose the DOM element easily. But I then still have the class instance in memory with methods wired to browser events looking for the DOM element that has been disposed. I need to be able to dispose not only of the DOM element, but also of the class instance, and I cannot figure out how to do that.
How can one dispose of a function prototype once declared? This seems like it ought to be simple, but I'm finding it to be decidedly not so.
For background info, here is an example of a class as I am defining it:
This is necessarily pseudo-code(ish)...
AWP.trivialExample = function(someDomRef, someOptionSet) {
this._id = someOptionSet['name'];
this._width = someOptionSet['width'];
this._width = someOptionSet['height'];
this._domRef = someDomRef;
this._object = '';
this.constructDOM();
this.wireEvents();
}
AWP.trivialExample.prototype = {
constructDOM: function() {
// build a complex DOM element relative to a provided DOM ref using the
// desired and height. This uses a template and I won't give a precise example
// of such a template.
jQuery("#aTemplate").tmpl(someJSONData).appendTo("body");
},
positionRelative: function() {
// this function would get the location of a specific DOM ref and always maintain
// a relative position for the DOM element we just constructed
},
wireEvents: function() {
// hook up to events using JQuery (example)
jjQuery(window).resize(this.positionRelative);
}
}
The above is a trivial example that would take in a DOM object reference, and then it would dynamically construct a new DOM element and it would wire up to browser events to always maintain relative position between these two objects when the page is sized.
When I dispose of the new object, I also need to dispose of the class instance and I cannot find a simple way to do that.
All help appreciated.
Thanks;
A suggestion on the event listeners referencing a deleted DOM node:
just as you have a 'wireEvents', you should have a corresponding 'unwireEvents' in case you decide to stop using the object. addEventListener() needs to be used in conjuction with removeEventListener() in this case. You should modify your prototype to remove Event listeners when the corresponding DOM Node is 'disposed', as you say.
AWP.iuiPanel.prototype = null; // ?

jQuery access to 'this' object with events and triggers

I have two classes orchestrated by a main class and I would like to know how to gain access to the correct 'this' object when events are fired among these classes. Here's what I have:
// My main class that orchestrates the two worker classes
function MainClass()
{
this.workerOne = new ChildWorkerOne();
this.workerOne.bindBehaviors.apply(this.workerOne);
this.workerTwo = new ChildWorkerTwo();
this.workerTwo.bindBehaviors.apply(this.workerTwo);
// a custom event I'm creating and will be triggered by
// a separate event that occurs in workerTwo
$(document).bind("customEvent", this.onCustomAction);
}
MainClass.prototype.onCustomAction = function(event, data)
{
// I want to call a method that belongs to 'workerOne'.
this.workerOne.makeItHappen();
// However, the 'this' object refers to the 'Document' and
// not the 'MainClass' object.
// How would I invoke 'makeItHappen' here?
};
ChildWorkerOne.prototype.makeItHappen = function()
{
// Do a bunch of work here
};
ChildWorkerTwo.prototype.bindBehaviors = function()
{
$(div).click(function(e){
$.post(url, params, function(data)
{
// do a bunch of work with this class and then
// trigger event to update data with ChildWorkerOne
$(document).trigger("customEvent", [data]);
}
});
};
I don't want to merge ChildWorkerOne and ChildWorkerTwo because they are two separate entities that don't belong together and MainClass conceptually should orchestrate the ChildWorkerOne and ChildWorkerTwo. However, I do want to invoke the behavior of one in the other.
What's the best way to go about doing this?
You need to persist the this value, you can do it in many ways, jQuery 1.4+ provides you the $.proxy method, e.g.:
//...
$(document).bind("customEvent", $.proxy(this.onCustomAction, this));
// or
$(document).bind("customEvent", $.proxy(this, 'onCustomAction'));
//...

Categories