I'm creating a control for Google maps v2. While creating my control I've found a design challenge and want to find an appropriate solution. Here's the goods.
A custom Google control inherits from GControl;
myControl.prototype = new GControl();
Next I need to overload the initializer so here it is.
myControl.prototype.initilize = function (map) {
//do some work and return stuff
};
Now within my custom controls initlize function I create a couple elements which, using the GEvent class, I subscribe to various events. To make my callback functions managable, I included them into the controls prototype.
myControl.prototype.onEvent = function(e){
//do some work;
//modify the members of the current myControl instance
};
Within my callback function "onEvent" I want to modify members within my control. What is the best way to access my control from the function? The keyword "this" cannot be used because that is a reference to the element that was clicked, in my case a div. And I can't access the members through the prototype because I need a specific instance of the object. The only viable solution I've considered is to create my control globally in one of my scripts. Is this the best method?
The easiest thing that I can think, it to define your onEvent method within your constructor, there you will have quick access to the current object instance, and you will not have to modify your public API:
function MyControl () {
var instance = this; // store a reference to 'this'
this.onEvent = function (e) {
// use the instance variable
instance.otherMethod();
};
}
Note that in this approach, the onEvent property will exist physically in your object instances (obj.hasOwnProperty('onEvent') = true).
Edit: You can simply use the GEvent.bind function:
GEvent.bind(map, "click", myObj, myObj.onEvent);
The above bind method will enforce the context, so the this keyword inside myObj.onEvent will point to the myObj object instance when the event is triggered, it will work with your code without problems.
I'm not familiar with how you subscribe to events using GEvent, so I'll make that part up. Do this:
myControl.prototype.onEvent = function(e, instance) {
// do some work
// modify members of 'instance'
};
function wrap(handler, instance) {
return function(e) {
handler(e, instance);
}
}
GEvent.Register('Event', wrap(instance.onEvent, instance));
Related
I'm creating a custom object on click and then trying to access it's prototype's methods.
$(function(){
$('.addtask').on("click", function(){
var new_task = new Task();
$('.deletetask').on("click", function(){
new_task.deleteTask();
});
function Task(){
this.html="<li>Add Description<span class='deletetask'></span></li>";
}
Task.prototype = {
constructor: Task,
deleteTask: function(){
this.remove()
},
}
});
});
Now when i click on class deleteTask it says:
TypeError: new_task.deleteTask is not a function
new_task.deleteTask();
How can I access the prototype method deleteTask() ??
You're setting the prototype of Task after you've created your new Task. You're not changing the prototype of your already created instances by doing so, as the prototype is referenced at the instance creation.
Move the Task.prototype = { assignation before the creation of your new Task.
If you really want to add a function to all instances after they're created, change the prototype of the constructor instead of replacing it :
Task.prototype.deleteTask = function(){...
You should move the definition of Task and its prototype methods before the initial .on call. This will incidentally fix the problem identified by #dystroy whereby you're trying to set the prototype of Task after creating an instance of it.
As written the Task function is recreated every time the click handler is invoked, and therefore the Task objects created on each click will be instances of different classes, which makes using the prototype moot. Prototype methods are an effective way of sharing methods amongst multiple instances, and achieving inheritance, but you're using neither.
Alternatively, leave the Task function where it is but abandon using prototype methods, e.g.:
function Task() {
this.html = "<li>Add Description<span class='deletetask'></span></li>";
this.removeTask = function() {
this.remove();
}
}
(NB: this.remove is also undefined at this point?)
I'm trying to define a base class in JavaScript that performs a lot of common functionality upon creation. Part of that functionality is to create a component and register callback handlers to that component.
The problem I'm having is how to override the function that's being used for that callback handler in child classes that extend my base class.
In concrete terms I have a BasicPage component that creates a Table component. The BasicPage also defines a default onRowClick function that gets registered with the newly created Table.
Now I want to create a PageWithSections component that extends BasicPage (via a call(..) statement) and overrides onRowClick. The problem is the registration of the click handler with the table happens within the constructor of the base class. At the time of that registration, onRowClick hasn't been overridden yet, so the reference is to the base classes version of onRowClick.
I've made a jsBin that illustrates the problem.
http://jsbin.com/uDEjuzeQ/9/edit
If you click on each box once, in order, I want the message box display to be:
No messages yet;
row clicked;
row clicked; BasicPage onRowClicked;
row clicked; PageWithSections onRowClicked
What is the proper way to override a function up the constructor chain and bind the overridden function to something during construction of a base object?
UPDATE
This question original referenced a prototype chain, but in truth the prototypes are not actually being used in this example.
The question was updated to reflect that. This ends up being more of a question about late binding.
The biggest issue I see is that your _onRowClicked (the callback you pass into the Table) is not actually defined in a prototype anywhere.
You are not actually using prototypical inheritance - you are defining the methods inside the constructor, and calling one constructor from another.
Try refactoring your code such that some of the default behaviour for BasicPage is defined in BasicPage.prototype (which is currently not referenced/altered at all). At that point, a solution that uses prototypical inheritance might suddenly become obvious. :)
Here is some code that should work:
var BasicPage = function(){
this.name="BasicPage";
document.body.onclick=this._getClick(this);
};
BasicPage.prototype._getClick=function(me){
return function(e){
console.log("target is:,",e.target);
console.log("this is:",me.name);
}
};
var PageWithSections = function(){
//initialise parent and it's instance members
BasicPage.call(this);
//override name
this.name="PageWithSections";
};
PageWithSections.prototype=Object.create(BasicPage.prototype);
PageWithSections.prototype.constructor=PageWithSections;
var sect = new PageWithSections();
document.body.click();
The following code demonstrates how you could extend the onclick handler without copy and pasting the BasicPage code you already have:
var BasicPage = function(){
this.name="BasicPage";
document.body.onclick=this._getClick(this);
};
BasicPage.prototype._getClick=function(me){
return function(e){
console.log("re used code from BasicPage");
console.log("target is:,",e.target);
console.log("this is:",me.name);
}
};
var PageWithSections = function(){
//initialise parent and it's instance members
BasicPage.call(this);
//override name
this.name="PageWithSections";
};
//set prototype chain
PageWithSections.prototype=Object.create(BasicPage.prototype);
PageWithSections.prototype.constructor=PageWithSections;
//extend _getClick
PageWithSections.prototype._getClick=function(me){
var fn=BasicPage.prototype._getClick.call(me,me);
return function(e){
//do BasicPage click code
fn(e);
//extended code
console.log("with a little extra from PageWithSections");
};
};
var sect = new PageWithSections();
document.body.click();
More info on prototype and constructor functions here. The introduction should be very helpful and if you have time I would suggest reading all to get a good understanding of JS prototype.
My co-worker came up with one possible solution. As #cloudfeet said, it's not prototypal, but it works.
Basically he set the binding to a different instance function that in turn, called the _onRowClick function which at the time of execution would have been overridden.
http://jsbin.com/uDEjuzeQ/16/edit
Let us say there are some widgets:
function Widget1(initData) {
....
}
Widget1.prototype.render = function() {
....
}
function Widget2(initData) {
....
}
Widget2.prototype.render = function() {
....
}
What I need to do is the following:
$.subscribe('/launch', function(widgetName, initData) {
// create a new object of the widget and then call the render method
});
I dont want to write multiple if-else blocks as the number of widgets may become very large. One option is to use eval(), but I believe that there may be better techniques. I am using JQuery framework, so don't want to include any other frameworks that may have a specific feature to support this. A pure Javascript solution will be appreciated.
Yep, there is a way. When you create a function, it's created as a property of the this object in that context. So if you declare those Widget functions in the global scope, they become properties of the window object. As you probably know, you can access a property both as object.property or object['property']. So, if they are global, you can do something like:
$.subscribe('/launch', function(widgetName, initData) {
var widget = new window[widgetName](initData);
});
EDIT: As T.J. Crowder said, I was horribly wrong. What I said about the function being created as a property of this applies when you're on the global scope (I want to say "only when you're on the global scope", but since I'm not 100% sure I'm gonna leave it as that).
As the comment from Esalijia said:
$.subscribe('/launch', function(widgetName, initData) {
// create a new object of the widget and then call the render method
widget = new window[widgetName]();
});
Or you can bind the scope of where Widget* is defined using .bind() and have them available to the $.subscribe method.
So I'm using this pretty standard jquery plugin pattern whereby you can grab an api after applying the jquery function to a specific instance.
This API is essentially a javascript object with a bunch of methods and data.
So I wanted to essentially create some private internal methods for the object only to manipulate data etc, which just doesn't need to be available as part of the API.
So I tried this:
// API returned with new $.TranslationUI(options, container)
$.TranslationUI = function (options, container) {
// private function?
function monkey(){
console.log("blah blah blah");
}
// extend the default settings with the options object passed
this.settings = $.extend({},$.TranslationUI.defaultSettings,options);
// set a reference for the container dom element
this.container = container;
// call the init function
this.init();
};
The problem I'm running into is that init can't call that function "monkey". I'm not understanding the explanation behind why it can't. Is it because init is a prototype method?($.TranslationUI's prototype is extended with a bunch of methods including init elsewhere in the code)
$.extend($.TranslationUI, {
prototype: {
init : function(){
// doesn't work
monkey();
// editing flag
this.editing = false;
// init event delegates here for
// languagepicker
$(this.settings.languageSelector, this.container).bind("click", {self: this}, this.selectLanguage);
}
}
});
Any explanations would be helpful. Would love other thoughts on creating private methods with this model too.
These particular functions don't HAVE to be in prototype, and I don't NEED private methods protected from being used externally, but I want to know how should I have that requirement in the future.
// Edited based on Matthew's comment
So I tried moving the prototype definition based on Matthew's comment. This seems to work now, but still not sure if this is the correct way to be doing this. Thoughts? Obviously it would be cleaner if I move the prototype object into a separate area
$.TranslationUI = function (options, container) {
function monkey(){
console.log("blah blah blah");
}
// extend the default settings with the options object passed
this.settings = $.extend({},$.TranslationUI.defaultSettings,options);
// set a reference for the container dom element
this.container = container;
$.extend($.TranslationUI.prototype,
{
init : function(){
monkey();
// editing flag
this.editing = false;
// init event delegates here for
// languagepicker
$(this.settings.languageSelector, this.container).bind("click", {self: this}, this.selectLanguage);
}
}
);
// call the init function
this.init();
};
So while this works, the crappy part is that I'm re-initing prototype every time that constructor runs. I'm sure that's not efficient. But not sure how else to have the prototype methods have access to private functions/variables of a certain instance.
The error is because monkey is not defined in the scope you're calling $.extend from.
Alright. So found an answer on stackoverflow, confirmed by Crockford's site.
javascript - accessing private member variables from prototype-defined functions
Essentially, you can't really get access to private functions from the prototype methods. You can via 'privileged' functions, which in turn call private variables and functions, but then you are basically creating a crapload of getters and setters, which might just be doubled in your prototype "public" methods.
So its kind of a lot of work, especially if your stuff doesn't TRULY need to be private.
Have a look at my answer and some of the others here:
call function inside a nested jquery plugin
The HtmlObject provides all the necessary functionality to register managed event handlers for script and DOM events, but what if the class you need to listen to doesn't exist as a DOM element, but a scripting variable (referenced via ScriptObject) instead?
A javascript object doesn't support the concept of attached events. However it may support the concept of a property holding a reference to function that if assigned will be called at a certain point.
I take you have such an object?
If so you use the ScriptObject SetProperty method using the name of the property that should hold a reference to a function and a delegate to Managed method matches the signature that the Javascript object will call.
Caveat the following is untested at this point but should put you on the right path.
//Javascript in web page.
var myObj = new Thing();
function Thing()
{
this.doStuff = function()
{
if (this.onstuff) this.onstuff("Hello World");
}
}
// C# code in a Silverlight app.
class SomeClass
{
private ScriptObject myObject;
public SomeClass(ScriptObject theObject)
{
myObject = theObject;
myObject.SetProperty("onstuff", (Action<string>)onstuff);
}
function void onstuff(string message)
{
//Do something with message
}
}
As stated by AnthonyWJones, Silverlight can't attached to JavaScript events. The right thing to do in this situation is to do the following:
Enable scripting access in Silverlight:
Mark the class with the
ScriptableType attribute, or mark
the specific methods with
ScriptableMember
Call
HtmlPage.RegisterScriptableObject in
the constructor.
Once everything is set up in the Silverlight code, here's what you do in JavaScript:
Obtain a reference to the JavaScript
object and register an event handler
Use document.getElementById to get
the Silverlight control
Call .Content.. in the
JavaScript event handler. For
example,
silverlight.Content.Page.UpdateText(text).
So basically, all event handling is performed in JavaScript, but the JavaScript event handlers can be used to call functions in Silverlight.