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

ClientEvents multiple events for same eventtype suggestion

5 Answers 143 Views
ComboBox
This is a migrated thread and some comments may be shown as answers.
This question is locked. New answers and comments are not allowed.
NG
Top achievements
Rank 1
NG asked on 03 Nov 2010, 05:52 AM
I notice if two or more events been set for the same eventtype, the latest event in this particular eventtype's set of events will use to overwrite the rest.

For example
Html
    .Telerik()
    .DropDownList()
    .Name("Company")
    .DataBinding(binding => binding.Ajax().Select("Company", "ComboBox"))
    .ClientEvents(ce => ce
    .OnDataBound("autoWidth")
    .OnDataBound("boundEmpty")
    .Render();
boudEmpty() fired, but autowidth() never fire and overwriten by boundEmpty(), no different with I take out .OnDataBound("autoWidth").

Come to the fact this Overwriting Behaviour is not useful for me, and in some situation I might required workaround to fire both events in sequence instead.

What i suggest in here, is new setting for ClientEvents to allow ClientEvents render in different mode. For example default mode overwriting mode, or multiple handling mode.

Thanks

5 Answers, 1 is accepted

Sort by
0
Chris
Top achievements
Rank 1
answered on 19 Oct 2011, 06:57 PM
I second this! I create an extension method in my library to set defaults for the grid we use throughout the site. One of the defaults is to set client handlers for databinding and databound for ajax grids to mask the grid with a "loading" message (and perform some other UI effects). When additional capabilities are required for a specific page, these defaults get wiped out and have to be coded in the page, which makes it impossible to have just one place to perform site-wide required grid functionality.

0
Dimo
Telerik team
answered on 20 Oct 2011, 07:38 AM
Hello,

Generally such a feature is a little cumbersome to implement and I don't think it is commonplace or often needed. You can achieve the same behavior like this:

.ClientEvents(ev => ev.OnDataBound("function(e) { autoWidth(e, this); boundEmpty(e, this); }"))

Since OnDataBound accepts a string parameter, which is a Javascript function reference, now you can attach either of the two Javascript handlers or both of them, depending on conditions evaluated in the server code. The Javascript handlers below will handle all cases. The tricky moment is the this (the context), which changes when you hook up several handlers.


function autoWidth(e, that)
{
    if (!that) {
        that = this;
    }
 
    // use that instead of this to get the Grid element
}
 
function boundEmpty(e, that)
{
    if (!that) {
        that = this;
    }
 
    // use that instead of this to get the Grid element
}


Regards,
Dimo
the Telerik team
If you want to get updates on new releases, tips and tricks and sneak peeks at our product labs directly from the developers working on the Telerik Extensions for ASP.MET MVC, subscribe to their blog feed now
0
Chris
Top achievements
Rank 1
answered on 21 Oct 2011, 05:20 AM
Actually, the context isn't the tricky part...you can just make the original call, changing the context:

.ClientEvents(ev => ev.OnDataBound("function(e) { autoWidth.call(this,e); boundEmpty(e).call(this,e); }"))

No need to change the signature of your handlers in that case to accept an extra parameter:

function autoWidth(e)
{
    // this is always this, no need for that!
 
//    if (!that) {
//        that = this;
//    }
  
    // always use this (never that) to get the Grid element
}
  
function boundEmpty(e)
{
    // this is always this, no need for that!
 
//    if (!that) {
//        that = this;
//    }
  
    // always use this (never that) to get the Grid element
}

I love javascript!

But, this doesn't solve the problem. I'd still have to touch every Grid I've got defined if I ever needed to make an adjustment in any hierarchical chaining of defaults. Also, it doesn't seem too out of place as a request since the whole idea of events and event handlers is to be able to wire up as many listeners as you need without regard to who else may be listening. Being limited to one listener (or one definition) is, well, limiting. :)

Basically, what I do is have a generic set of common functionality every one of my grids should implement. Then, depending on the area or subsite/subapp, there may be an additional set of functional elements. For example, I wanted all my grids to mask and unmask during all AJAX calls, showing a mask similar to what ExtJS provides. But then, in an area of the app tied to financial data, I want all the grids to automatically add classes and format currency columns as different colors based on positive, negative, or 0 values. Rather than coding these function calls into the client events on every grid with currency data (since it's financial, it would be all of them), I just want to wire up all my area-wide default scripts with a single call, and also not impact any page specific additions.

As an example:

@(Html.Telerik.Grid(Model).Name("MyFinancialGrid")
     .Columns(
             .
             .
             .
      )
     .DataBinding(db => db.Ajax.Select("GetList", "FinancialSource") )
     .SetSiteWideDefaults()
     .SetFinancialDefaults()
     .ClientEvents(e => e.OnDataBound("somePageLocalHandler"))
)

Where SetSiteWideDefaults() and SetFinancialDefaults() are extension methods that each set their own OnDataBound client event handlers.

So, in this case, I couldn't add a handler for the same event from different library methods as each successive one would wipe out the prior. 

The solution I came up with after my post was actually pretty simple, if a tad hacked. And I don't think it would all that cumbersome to modify the Telerik code to implement this for all ClientEventBuilders. Mine was hackish simply because you don't expose as a public property the "GridClientEvents events" private member in "GridClientEventsBuilder", nor are the methods in that class marked virtual (making runtime subclassing modifying the vtables impossible). I simply implemented extension methods that, horrifically, have to use reflection to gain access to the aforementioned private member in GridClientEvents. I understand it may break in upgrades, but it should be easy enough to modify, and hopefully it can be removed one day if the feature gets implemented. Of course, this could be achieved by simple overloads in the GridClientEventsBuilder class.

Here's my main extension:

public static GridClientEventsBuilder AppendHandler(this GridClientEventsBuilder bldr, string eventName, string handlerName)
{
    // add handler as comma separated values to any existing handler before convering to a function call with an internal method
 
    GridClientEvents events = GetGridClientEvents(bldr);
 
    PropertyInfo prop = typeof(GridClientEvents).GetProperty(eventName, BindingFlags.Instance | BindingFlags.Public);
    ClientEvent ce = (ClientEvent)prop.GetValue(events, null);
 
    ce.HandlerName = string.IsNullOrWhiteSpace(ce.HandlerName) ? handlerName : ce.HandlerName + ", " + handlerName;
 
    string handlerCalls = string.Join(".call(this, e); ", ce.HandlerName.Split(new char[] { ',' })) + ".call(this, e); ";
    SetInlineCodeBlockWithAppend(ce, handlerCalls);
 
    ce.HandlerName = null;
 
    return bldr;
}

The idea is simply to take any handler names and call them one after another in a function in the InlineCodeBlock (which, if that already has a function call, these just would get appended)--that's what "SetInlineCodeBlockWithAppend" does.

The sticky part I'm not happy with is that nasty little manner of gaining access to the "GridClientEvents events" member in "GridClientEventsBuilder" (any reason that can't be a public property in a future release?):

/// <summary>
/// This is a hack method that uses reflection to expose private members in a telerik class.
/// </summary>
private static GridClientEvents GetGridClientEvents(GridClientEventsBuilder bldr)
{
    FieldInfo fld = typeof(GridClientEventsBuilder).GetField("events", BindingFlags.Instance | BindingFlags.NonPublic);
    return (GridClientEvents)fld.GetValue(bldr);
}


So with all that wired up, I can create a strongly typed extension method like this:

public static GridClientEventsBuilder OnDataBound(this GridClientEventsBuilder bldr, string handlerName, bool withAppend)
{
    return withAppend ? bldr.AppendHandler("OnDataBound", handlerName) : bldr.OnDataBound(handlerName);
}


which lets me do this on my grid:

@(Html.Telerik.Grid(Model).Name("MyFinancialGrid")
     .Columns(
             .
             .
             .
      )
     .DataBinding(db => db.Ajax.Select("GetList", "FinancialSource") )
     .SetSiteWideDefaults()
     .SetFinancialDefaults()
     .ClientEvents(e => e.OnDataBound("somePageLocalHandler", true))
)


This gives me the best flexibility, as I can wipe out any previous handlers for OnDataBound by specifying "false". The" true" let's me append the handler and leave any others previously set untouched.

So my "SetSiteWideDefaults" looks like this:

public static GridBuilder<T> SetSiteWideDefaults<T>(this GridBuilder<T> gbuilder) where T : class
{
 
    return
        gbuilder
            .Pageable(config => { config.Style(GridPagerStyles.PageInput | GridPagerStyles.NextPrevious | GridPagerStyles.PageSizeDropDown | GridPagerStyles.Status); config.PageSize(50, new int[] { 10, 25, 50, 100, 200, 500, 1000 }); })
            .Sortable()
            .ClientEvents(e => e
                    .OnDataBinding("myLib.telerik.gridUtils.GridClientBindingMaskDelegate", true)
                    .OnDataBound("myLib.telerik.gridUtils.GridClientBindingUnMaskDelegate", true)
                    )
            .Filterable()
            .Groupable();
}


And then my other extension method (SetFinancialDefaults) wires up it's own handlers to various events.

Those extension methods are compiled into libraries and just referenced in my various web apps (I also include my custom javascript libraries along with a VirtualPathProvider). That way everything sits nicely in a class library and can be written once and reused many times. And if my defaults ever need to change, I change it one place and it propagates to all my grids.

It took < 170 lines of code to provide the functionality and extension methods for all the On... methods that are exposed by GridClientEventsBuilder, doing so for the two overloads for each (one with a "string handlerName" parameter and the other with a "Func<object,object> inLineCodeBlock" param. I didn't do the "Action codeBlock" param as it's never used in our code).

Of course, this was a rush job and is all Grid specific. I didn't have a chance to look beyond that to make it generic for any ClientEventBuilder (such as the combo box one the original post is about). At some point I may refactor that to work for any builder, but for now, I'm focused on the Grid. But, I can see the possibilities being much simpler if the Telerik code base was modified to handle this. It seems it would be relatively easy to modify the ClientEvent class and add a couple of methods--one to remove a handler and one to add a handler...and then expose the appropriate builder methods to allow for that. But, that's also a cursory idea for a solution, so it could very well be a bit more complex than that to make the modifications. But I'd still love to see it in there, as that's truly what events are about--wiring up multiples without impacting or caring about what else is listening.


0
Dimo
Telerik team
answered on 21 Oct 2011, 08:08 AM
Hello Chris,

Thanks for the input. Certainly, the discussed behavior will serve your purpose, however, we don't see that much benefit in investing time to implement it, compared to other tasks with higher priority in our to-do list. If there are more requests to support multiple event hooking in the future, we will consider adding it out-of-the-box.

Best wishes,
Dimo
the Telerik team
If you want to get updates on new releases, tips and tricks and sneak peeks at our product labs directly from the developers working on the Telerik Extensions for ASP.MET MVC, subscribe to their blog feed now
0
Chris
Top achievements
Rank 1
answered on 21 Oct 2011, 11:24 PM
Absolutely--makes perfect sense! You guys are always adding great, impressive features. Thanks for all you do (and for the open source license on the MVC extensions!). Keep up the good work!

Tags
ComboBox
Asked by
NG
Top achievements
Rank 1
Answers by
Chris
Top achievements
Rank 1
Dimo
Telerik team
Share this question
or