how to extend/inherit class

classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

how to extend/inherit class

dncarreira
We are trying to extend a control, mimicking the ZoomPan control. We extend the Control class, and modify slightly the original code from ZoomPan.
 
Then when we modify the buttonDown method, we get errors using new properties or methods that are not in the original PanZoom class.
For instance, if we create a new property like 'var pTest: 1' and try to use it inside buttonDown, this property is allways 'undefined'.
 
For example, create a copy of PanZoom.js and rename it to PanZoom2.js, then alter it like this:
 
OpenLayers.Control.PanZoom2 = OpenLayers.Class.create();
OpenLayers.Control.PanZoom2.X = 4;
OpenLayers.Control.PanZoom2.Y = 4;
OpenLayers.Control.PanZoom2.prototype =
  OpenLayers.Class.inherit( OpenLayers.Control, {
 
    /** @type int */
    slideFactor: 50,
    pTest: 1,
 
    /** @type Array of Button Divs */
    buttons: null,
 
    /** @type OpenLayers.Pixel */
    position: null,
 
    /**
     * @constructor
     */
    initialize: function() {
        OpenLayers.Control.prototype.initialize.apply(this, arguments);
        this.position = new OpenLayers.Pixel(OpenLayers.Control.PanZoom2.X,
                                             OpenLayers.Control.PanZoom2.Y);
    },
/*... same code as in ZoomPan is included here except for the slightly different buttonDown method ...*/
    buttonDown: function (evt) {
        if (!OpenLayers.Event.isLeftClick(evt)) return;

        switch (this.action) {
            case "panup":
                this.map.pan(0, -50);
                break;
            case "pandown":
                this.map.pan(0, 50);
                break;
            case "panleft":
                alert('New property:' + this.pTest);
                break;
            case "panright":
                this.map.pan(50, 0);
                break;
            case "zoomin":
                this.map.zoomIn();
                break;
            case "zoomout":
                this.map.zoomOut();
                break;
            case "zoomworld":
                this.map.zoomToMaxExtent();
                break;
        }
If you then create a PanZoom2 control in your map and try to pan left you'll see that this.pTest is undefined.
If you try to use a new method you get an error "this.method is not a function".
Is there a correct way to solve this?
 
It's curious that it works inside the constructor. It seems there is something I'm not aware of in the subclassing mechanism...
 
Thank you.
Duarte Carreira


_______________________________________________
Users mailing list
[hidden email]
http://openlayers.org/mailman/listinfo/users
Reply | Threaded
Open this post in threaded view
|

Re: how to extend/inherit class

Erik Uzureau
Hi Duarte,

Good news and bad news for you :-)

Good news is that you are going about making your control in the right
way -- there is nothing wrong with the inheritance or the class
structure or any of that.

Bad news is that the behaviour you are experiencing is actually not a
bug, but rather a problem of context.

If you look at the definition of the _addButton() function [1] in the
PanZoom.js file, you will see that we are binding the buttonDown
function to the actual 'btn' DOMElement. This means that when you
enter the buttonDown function, your "this" variable is not the PanZoom
control, but rather the actual DOMElement for that particular button,
and therefore when you say this.pTest, javascript is asking the left
button's image element for its pTest property, which it does not have.

If you want to have a reference back to the PanZoom class from inside
the buttonDown() function, then you are going to have to tack on a new
property to the 'btn' object. It's kind of messy, but it should work:


/** in addButton() **/

        btn.onmousedown = this.buttonDown.bindAsEventListener(btn);
        btn.ondblclick  = this.doubleClick.bindAsEventListener(btn);
        btn.onclick  = this.doubleClick.bindAsEventListener(btn);
        btn.action = id;
        btn.map = this.map;
        btn.slideFactor = this.slideFactor;
>>    btn.control = this;

/** and then in buttonDown() **/

                break;
            case "panleft":
>>                alert('New property:' + this.control.pTest);
                break;
            case "panright":


The concept of contexts and binding is probably the most complicated
thing to understand in all of JavaScript (with the possible exception
of trying to make your page that works perfectly in FF work in IE as
well :-)

I found these two links that might help. They are for the prototype
interface, which OL used to use entirely and now only uses partially:

http://wiki.script.aculo.us/scriptaculous/show/Function.prototype.bindAsEventListener
http://www.sergiopereira.com/articles/prototype.js.html#Reference.Extensions.Function

Hope that helps!
--Erik


[1]
    /**
     * @param {String} id
     * @param {String} img
     * @param {OpenLayers.Pixel} xy
     * @param {OpenLayers.Size} sz
     *
     * @returns A Div (an alphaImageDiv, to be precise) that contains the
     *          image of the button, and has all the proper event handlers
     *          set.
     * @type DOMElement
     */
    _addButton:function(id, img, xy, sz) {
        var imgLocation = OpenLayers.Util.getImagesLocation() + img;
        var btn = OpenLayers.Util.createAlphaImageDiv(
                                    "OpenLayers_Control_PanZoom_" + id,
                                    xy, sz, imgLocation, "absolute");

        //we want to add the outer div
        this.div.appendChild(btn);

        btn.onmousedown = this.buttonDown.bindAsEventListener(btn);
        btn.ondblclick  = this.doubleClick.bindAsEventListener(btn);
        btn.onclick  = this.doubleClick.bindAsEventListener(btn);
        btn.action = id;
        btn.map = this.map;
        btn.slideFactor = this.slideFactor;

        //we want to remember/reference the outer div
        this.buttons.push(btn);
        return btn;
    },


On 11/28/06, Duarte Carreira <[hidden email]> wrote:

>
>
> We are trying to extend a control, mimicking the ZoomPan control. We extend
> the Control class, and modify slightly the original code from ZoomPan.
>
> Then when we modify the buttonDown method, we get errors using new
> properties or methods that are not in the original PanZoom class.
> For instance, if we create a new property like 'var pTest: 1' and try to use
> it inside buttonDown, this property is allways 'undefined'.
>
> For example, create a copy of PanZoom.js and rename it to PanZoom2.js, then
> alter it like this:
>
> OpenLayers.Control.PanZoom2 = OpenLayers.Class.create();
> OpenLayers.Control.PanZoom2.X = 4;
> OpenLayers.Control.PanZoom2.Y = 4;
> OpenLayers.Control.PanZoom2.prototype =
>   OpenLayers.Class.inherit( OpenLayers.Control, {
>
>     /** @type int */
>     slideFactor: 50,
>     pTest: 1,
>
>     /** @type Array of Button Divs */
>     buttons: null,
>
>     /** @type OpenLayers.Pixel */
>     position: null,
>
>     /**
>      * @constructor
>      */
>     initialize: function() {
>         OpenLayers.Control.prototype.initialize.apply(this,
> arguments);
>         this.position = new OpenLayers.Pixel(OpenLayers.Control.PanZoom2.X,
>
> OpenLayers.Control.PanZoom2.Y);
>     },
> /*... same code as in ZoomPan is included here except for the slightly
> different buttonDown method ...*/
>  buttonDown: function (evt) {
>  if (!OpenLayers.Event.isLeftClick(evt)) return;
>
>  switch (this.action) {
>  case "panup":
>  this.map.pan(0, -50);
>  break;
>  case "pandown":
>  this.map.pan(0, 50);
>  break;
>  case "panleft":
>  alert('New property:' + this.pTest);
>  break;
>  case "panright":
>  this.map.pan(50, 0);
>  break;
>  case "zoomin":
>  this.map.zoomIn();
>  break;
>  case "zoomout":
>  this.map.zoomOut();
>  break;
>  case "zoomworld":
>  this.map.zoomToMaxExtent();
>  break;
>  }
>
> If you then create a PanZoom2 control in your map and try to pan left you'll
> see that this.pTest is undefined.
> If you try to use a new method you get an error "this.method is not a
> function".
> Is there a correct way to solve this?
>
> It's curious that it works inside the constructor. It seems there is
> something I'm not aware of in the subclassing mechanism...
>
> Thank you.
> Duarte Carreira
>
>
> _______________________________________________
> Users mailing list
> [hidden email]
> http://openlayers.org/mailman/listinfo/users
>
>
>
_______________________________________________
Users mailing list
[hidden email]
http://openlayers.org/mailman/listinfo/users