This is a migrated thread and some comments may be shown as answers.

[Solved] this is not available in a function of a component that handles an event

5 Answers 160 Views
Ajax
This is a migrated thread and some comments may be shown as answers.
rh
Top achievements
Rank 1
rh asked on 22 Nov 2009, 07:39 AM
I've started creating components and registering event handlers for the telerik controls as functions in my components. The problem is that the this keyword in my function does not point to my class so I can't reference other functions in my class. I've read that there are some addHandler methods out there that people have written to resolve this issue but wanted to ping the Telerik community to see if there is a best practice for dealing with this for telerik and/pr ms ajax solutions.

Example addHandler method to resolve this:

flexible javascript events

There is also this that I can try as well:

bindAsEventListener

I can use one of these (although I would want to find the version without the memory leak) but wanted to see if there is a better way to register telerik events to functions in custom ms ajax components that make the this keyword reference the class the function is contained within.



5 Answers, 1 is accepted

Sort by
0
rh
Top achievements
Rank 1
answered on 22 Nov 2009, 04:45 PM
Note that I have tried the Sys.UI.Dom.addHandler, aka $addHandler, registration process and that doesn't even register the event. It doesn't give an error but the event doesn't fire my event handler. Not even sure if we're suppose to be able to use $addHandler with Telerik controls but figured I'd try it.

Still trying to figure out how to get the this in my function reference the class that the function is a part of. I'm hesitant to use the addEvent/removeEvent method in the first link because of memory leaks. I guess I could try the solution provided by prototype.js but it seems to redundant to have that huge script just for the observeEvent capability.
0
rh
Top achievements
Rank 1
answered on 22 Nov 2009, 06:44 PM
OK, I've created a little sample project to illustrate the issue. It is a fairly simple project that contains an InputAddress user control. To keep it simple I've dumbed down the address and just have city, state, and zip code. In fact, I don't even worry about the state in this example. In my real world I have some complexity around dealing with state and county drop downs with load on demand to keep viewstate down as well as firing events for other parts of my application that need to register to a change in an address.

The idea here is to capture the valueChanged event of the zip code, do a look up on the zip code (simulated here but in the real world do a database lookup), and populate the city, state, and county. Or give a warning if the zip code is not found. I've had this working with "standard" javascript (i.e. not object oriented javascript) for quite some time but when I have many addresses on a page then that creates some serious markup bloat in regards to the amount of javascript that is downloaded to the browser. This is because the inline javascript is copied for each instance of the address.

So I've been working on refactoring it into the MS Ajax object oriented approach but have run up against a wall with handling events and referencing members of my class. That is illustrated in this example. In Firebug you can set a breakpoint in the AddressApplication.js file in the getCityStateCounty method in the prototype. This method is called when the valueChanged method of the zip code textbox is fired. The issue is that I want to pass a reference to another function in my class when I call the web service so the web service calls back into this particular instance of the class when it completes. I can't do that because the this keyword doesn't reference my class.

I suspect I am going to run into the same issue by passing this.onGetCityStateCountyComplete from the web service. In that method I want to reference some instance variables in my class which I suspect I may not be able to because of the same problem. Hopefully there is a solution for both but I'd take a solution for handling Telerik events first and try to find a workaround for handling web serivce call events second.

I'd also be interested in feedback on my approach to creating my classes from my usercontrols. This is done in the PreRender of my usercontrol. I check to make sure that the usercontrol is visible before injecting the javascript because if it is not visible there is no reason to send the javascript across the wire.

Some questions I would have on this are:

Is my approach of leveraging the init to create the object then leveraging the load to register my event handlers to the Telerik controls a good one or is there a better more efficient approach?

Should I also check for IsPostBack and IsCallBack before sending the initialization script?

Any other feedback to the approach.


NOTE: Well, after typing all the above up I tried to upload my zipped project file and found out that only images are supported in the forum. Ha! I guess I can create a support ticket but felt this was more of a community question because hopefully others can learn from it and not sure it is really an issue with the Telerik controls as much as it is with javascript and probably my lack of knowledge.

Also, there is a little bug in the forum where if you try to upload a file and it is not of the correct type so you clear out the textbox for the filename it still won't let you reply to the thread and continues to give you the file is not of correct type error. So, I uploaded a little image that has nothing to do with the thread to get around that. Please ignore it.

I'll post some of the code here as an alternative:

The js file:

 
Type.registerNamespace('Sample.UI'); 
 
Sample.UI.Address = function() { 
  Sample.UI.Address.initializeBase(this); 
 
  this._cityID = null
  this._stateID = null
  this._zipCodeID = null
 
  this._stateToLoadOnComplete = null
  this._zipCodeNotFoundPanel = null
 
Sample.UI.Address.prototype = 
  get_cityID: function() { 
    return this._cityID; 
  }, 
 
  set_cityID: function(id) { 
    this._cityID = id
  }, 
 
  get_stateID: function() { 
    return this._stateID; 
  }, 
 
  set_stateID: function(id) { 
    this._stateID = id
  }, 
 
  get_zipCodeID: function() { 
    return this._zipCodeID; 
  }, 
 
  set_zipCodeID: function(id) { 
    this._zipCodeID = id
  }, 
 
  get_cityCtl: function() { 
    return $find(this.get_cityID()); 
  }, 
 
  get_stateCtl: function() { 
    return $find(this.get_stateID()); 
  }, 
 
  get_zipCodeCtl: function() { 
    return $find(this.get_zipCodeID()); 
  }, 
   
  getCityStateCounty: function(sender, eventArgs) { 
    var zipCode = sender.get_value(); 
    EventHandlerWithThisKeyword.Shared.Services.AddressWebService.GetCityStateCounty(zipCode, this.onGetCityStateCountyComplete); 
  }, 
 
  onGetCityStateCountyComplete: function(results) { 
 
    var zipCodeNotFoundPanel = this.get_zipCodeNotFoundPanel(); 
    if (zipCodeNotFoundPanel) { 
      zipCodeNotFoundPanel.className = "hide"
    } 
 
    if (results != null) { 
      if (results.Found) { 
        this.set_city(results.City); 
        //this.set_stateAndCounty(results.State, results.County); 
      } 
      else { 
        if (zipCodeNotFoundPanel) { 
          zipCodeNotFoundPanel.className = "show"
        } 
      } 
    } 
    else { 
      if (zipCodeNotFoundPanel) { 
        zipCodeNotFoundPanel.className = "show"
      } 
    } 
  }, 
 
  initHandlers: function() { 
    this.get_zipCodeCtl().add_valueChanged(this.getCityStateCounty); 
    //$addHandler(this.get_zipCodeCtl(), 'valueChanged', this.getCityStateCounty); 
  } 
 
 
Sample.UI.Address.registerClass('Sample.UI.Address', Sys.Component); 
 
Sys.Application.notifyScriptLoaded(); 

The address control:

<asp:ScriptManagerProxy ID="smp" runat="server"
  <Scripts> 
    <asp:ScriptReference Path="~/js/AddressApplication.js" /> 
  </Scripts> 
  <Services> 
    <asp:ServiceReference Path="~/Shared/Services/AddressWebService.asmx" /> 
  </Services> 
</asp:ScriptManagerProxy> 
 
<div> 
  <telerik:RadTextBox ID="txtCity" runat="server" Skin="Office2007"></telerik:RadTextBox>  
  <telerik:RadComboBox ID="ddlState" runat="server" Skin="Office2007"></telerik:RadComboBox>  
  <telerik:RadTextBox ID="txtZipCode" runat="server" Skin="Office2007"></telerik:RadTextBox> 
  <div id="pnlZipCodeNotFound" runat="server" class="hide"
        <%--<asp:Image ID="imgZipCodeWarning" runat="server" ImageUrl="~/images/warning_small.png" /> --%> 
        <span class="error">Zip Code Not Found</span> 
    </div> 
</div> 

The address control code behind:

protected void Page_Load(object sender, EventArgs e) 
    { 
 
    } 
 
    protected void Page_PreRender(object sender, EventArgs e) 
    { 
      if (this.Visible) 
      { 
        string script = String.Format("Sys.Application.add_init(function() {{\r\n" + 
        "$create(Sample.UI.Address, {{\r\n" + 
        "id:\"{0}\",\r\n" + 
        "cityID:\"{1}\",\r\n" + 
        "stateID:\"{2}\",\r\n" + 
        "zipCodeID:\"{3}\",\r\n" + 
        "stateToLoadOnComplete:\"\",\r\n" + 
        "zipCodeNotFoundPanel:\"{4}\",\r\n" + 
        "}}, null, null, null);\r\n" + 
        "}});\r\n", this.ClientID, txtCity.ClientID, ddlState.ClientID, 
        txtZipCode.ClientID, pnlZipCodeNotFound.ClientID); 
 
        string scriptLoad = String.Format("Sys.Application.add_load(function() {{\r\n" + 
          "var d = $find(\"{0}\");\r\n" + 
          "d.initHandlers();\r\n" + 
          "}});\r\n", this.ClientID); 
 
        ScriptManager.RegisterStartupScript(this, this.GetType(), this.ClientID, 
          String.Format("{0}\r\n{1}", script, scriptLoad), true); 
      } 
    } 

The Web Service:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Web; 
using System.Web.Services; 
using System.Web.Script.Services; 
using BusinessEntities.Ajax; 
 
namespace EventHandlerWithThisKeyword.Shared.Services 
  /// <summary> 
  /// Summary description for AddressWebService 
  /// </summary> 
  ///  
  [ScriptService] 
  [WebService(Namespace = "http://EventHandlerWithThisKeyword/Services/")] 
  [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] 
  [System.ComponentModel.ToolboxItem(false)] 
  // To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.  
  // [System.Web.Script.Services.ScriptService] 
  public class AddressWebService : System.Web.Services.WebService 
  { 
 
    [WebMethod] 
    public CityStateCounty GetCityStateCounty(string zipCode) 
    { 
      if (zipCode == "11111") 
      { 
        return new CityStateCounty(false); 
      } 
      else 
      { 
        return new CityStateCounty(true, "CityName", 5, "CountyName"); 
      } 
    } 
  } 
 

And the business entity used by the web service:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
 
namespace BusinessEntities.Ajax 
  public class CityStateCounty 
  { 
    public bool Found; 
    public string City; 
    public int State; 
    public string County; 
 
    public CityStateCounty() 
    { 
      Found = false
      City = String.Empty; 
      State = -1; 
      County = String.Empty; 
    } 
 
    public CityStateCounty(bool found) 
    { 
      Found = found
      City = String.Empty; 
      State = -1; 
      County = String.Empty; 
    } 
 
    public CityStateCounty(bool found, string city, int state, string county) 
    { 
      Found = found
      City = city
      State = state
      County = county
    } 
  } 
 

The default.aspx to host the user controls:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="EventHandlerWithThisKeyword._Default" %> 
 
<%@ Register Assembly="Telerik.Web.UI" Namespace="Telerik.Web.UI" TagPrefix="telerik" %> 
<%@ Register TagPrefix="test" TagName="Address" Src="~/InputAddress.ascx" %> 
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
 
<html xmlns="http://www.w3.org/1999/xhtml" > 
<head runat="server"
    <title></title
    <link href="css/default.css" rel="stylesheet" type="text/css" /> 
</head> 
<body> 
    <form id="form1" runat="server"
    <telerik:RadScriptManager ID="RadScriptManager1" runat="server"
    </telerik:RadScriptManager> 
    <telerik:RadAjaxManager ID="RadAjaxManager1" runat="server"
    </telerik:RadAjaxManager> 
    <div> 
      Address 1<br /> 
      <test:Address ID="address1" runat="server"></test:Address> 
      <br /> 
      <br /> 
      Address 2<br /> 
      <test:Address ID="address2" runat="server"></test:Address> 
      <br /> 
      <br /> 
      Address 3<br /> 
      <test:Address ID="address3" runat="server"></test:Address> 
      <br /> 
      <br /> 
      Address 4<br /> 
      <test:Address ID="address4" runat="server"></test:Address> 
      <br /> 
      <br /> 
    </div> 
    </form> 
</body> 
</html> 
 



0
rh
Top achievements
Rank 1
answered on 22 Nov 2009, 07:17 PM
Tried the prototype.js solution using Event.observe

First, the last "stable" version 1.6.1 of prototype.js doesn't even compile without making some corrections. Perhaps they should run it in Firebug before releasing a version.

Second, the observe method throws an error after fixing all the compile errors.

So, for now, prototype.js isn't a solution. Not sure if it will be in the future either after experiencing these errors.

Ugh, looking forward to getting back to solving business problems not technical problems.....
0
rh
Top achievements
Rank 1
answered on 22 Nov 2009, 08:21 PM
Well, I tried the addEvent function identified here but it didn't work. I get an obj.addEventListener is not a function error so I guess the Telerik objects don't support either a attachEvent or addEventListener method.

For easy reference, the addEvent code is copied below:

 
function addEvent( obj, type, fn ) { 
  if ( obj.attachEvent ) { 
    obj['e'+type+fn] = fn; 
    obj[type+fn] = function(){obj['e'+type+fn]( window.event );} 
    obj.attachEvent( 'on'+type, obj[type+fn] ); 
  } else 
    obj.addEventListener( type, fn, false ); 
function removeEvent( obj, type, fn ) { 
  if ( obj.detachEvent ) { 
    obj.detachEvent( 'on'+type, obj[type+fn] ); 
    obj[type+fn] = null; 
  } else 
    obj.removeEventListener( type, fn, false ); 
 

0
Iana Tsolova
Telerik team
answered on 23 Nov 2009, 02:55 PM
Hello rh,

Please find our answers to your questions in the other support thread you have posted on the same subject.

Kind regards,
Iana
the Telerik team

Instantly find answers to your questions on the new Telerik Support Portal.
Watch a video on how to optimize your support resource searches and check out more tips on the blogs.
Tags
Ajax
Asked by
rh
Top achievements
Rank 1
Answers by
rh
Top achievements
Rank 1
Iana Tsolova
Telerik team
Share this question
or