I have tooled around with a few AJAX frameworks over the last year or so, especially to give a little dash of fancy to win support for our project. Partly because I am lazy and just because it has high-visibility, I have used the Ajax Control Toolkit that emerged along with the ASP.NET Ajax framework. While I enjoy writing javascript and dug into it fairly deeply to understand how it thinks about objects, prototyping and so on, I am not a fan of reinventing the wheel and would prefer avoiding lots of javascript coding.
Lately, however, I have been somewhat frustrated with some bugs and assumptions the Ajax Control Toolkit controls there make about the environment a typical ASP.NET developer codes in and so have begun looking at other frameworks more seriously. I want to assume that ViewState is OFF and DataBinding happens EARLY in a Page Lifecycle when I develop controls (Page_Load is too late with ViewState off).
Now I need an AutoComplete control. I think the AutoComplete functionality is a good litmus test for AJAX frameworks. At least it taught me alot about what to look for in a robust framework. So off to the web I went to evaluate a slough of frameworks and am pleased by the diversity of approaches to this function, even if some of their solutions are not suitable for my need.
Here's what I WANT:
- Be able to encapsulate the AutoComplete control in an User Control (ascx)
- Be able to assign properties to a control that act as arguments for the callback function
- Minimal javascripting...I'm ok with wiring up the control to the client callback needs but don't want to be parsing JSON or stuff like that
- Minimal configuration (HTTP Handlers)
- At least moderately performant...this isn't a super high-traffic application so I can tolerate some performance hit
- Prefer to attach a control to an asp:TextBox, but can use a 'wrapped' TextBox if required
The frameworks I evaluated that offer some kind of AutoComplete control are :
- ASP.NET Ajax Control Toolkit AutoCompleteExtender
- AJAX.NET by Michael Schwarz AutoComplete
- Anthem.NET YUIAutoComplete
- GAIA AutoComplete
- Entech's AutoSuggestMenu
- Capxous
Here are what my experiences were with each of these:
FREE
Since MS decided to require static PageMethods they essentially shut down any access to the page lifecycle for use during a callback. Implementing ICallbackEventHandler is not difficult but sends me into 're-inventing the wheel' shivers. There has been lots said about this dramatic change in their API and I understand their reasoning behind it, but it still makes simple requirements have complex solutions.
While the AutoComplete control itself is nice and configurable and performant, the fact that it locks me into a specific argument list without allowing me any kind of properties from my control to bear on my callback query makes this control almost useless to me.
Another deal-breaker for me is its inability to be embedded in an ASCX while using PAgeMethods in the code-behind. WebServices are the only choice here.
FREE
I really like how Michael Schwarz has put together his framework. It's mature and widely used. I was stoked to see him splitting off his JSON parser too.
This would work just fine in a usercontrol and simply requires an attribute flag on the callback method and it isn't required to be static. Also, you must register the class with the manager. I really like that the method signature is not locked down, too.
There isn't too much javascript required and what is required is reasonable and straightforward.
There is an handler in teh config required...no biggie.
The sample uses an <input> html control, but using a server control is of course possible to attach AutoComplete functionality control.
Frankly, I had a hard time getting this going... I didn't have it set up in an isolated environment so I am sure I was doing something wrong. This looks feasible but I don't have time right now to see why I was getting javascript errors in teh autocomplete.js file...it couldn't add a namespace even though all the ashx files were brought over.
FREE
This was the most interesting one I found and really was the big winner except for one problem...somehow it keeps all my Asp.NET Ajax controls from initializing and breaks them. Other Anthem controls don't have this problem and I have posted a bug report but haven't heard back...
This control uses the Yahoo User Interface (YUI) toolkit and so the .js files are hosted on Yahoo. I like this idea. I really like that he demo's the control on masterpage, aspx, and ascx formats.
You can easily embed this in an usercontrol and allows access to the Page LifeCycle per my requirements. All that is needed to register the callback method is an attribute flag on it.
This was definitely the ticket for me...if it didn't break the rest of my controls! Hopefully, I can dig around and figure out why this is happening and submit a patch or someone else can figure it out.
FREE if used on OSS, Commercial ($) license for closed projects
The controls these guys are putting together are by far the most sophisticated and beautiful controls.
This control meets all my criteria and really the license is not too costly. I think their controls might be a bit 'heavy' with decorations and css images which slows their controls down, but this could probably be easily trimmed down with a custom Skin that strips all that out.
FREE
This is a nifty little control that integrates nicely with the ASP.NET Ajax Framework. Demo #4 actually is a great demonstration of using page values to bear on the callback method, working around the static PAgeMethod issue.
I need to sit down and figure out what they are doing with their javascript in Demo #4 for this to be useful to me. To get around the lack of access to page lifecycle requires more javascripting than I'd prefer. It's not mind-boggling, tho, even to a relative javascript rookie.
Interestingly, their documentation looks very good so this might still be a feasible option. One thing that might stick me, though, is using in an ASCX. From what it appears, though, a web service would work fine with their control while still having some dynamic property options.
$$$ Commercial license required
The price tag on this makes it pretty much a no-go for me...one control for $370 isn't workable at my company.
This would prbably fit the bill and I like the templating that it seems to use for dsiaplying the query results. Clever stuff.
For now, I am just dealing with unfiltered data in my autocomplete control and am using the Ajax Toolkit control with a webservice. It is familiar and easy to use. Since I am including this in an ascx it'll be easy to port over to a better implementation later.
Things I learned on this investigation:
- Using ICallbackEventHandler
- JSON
- The costs of callbacks and determining when AJAX is useful...or not
- Web Developers still live in denial of a browser being a poor substitute for a windows application :)
UPDATE :
I am using a modified version of the control at http://www.codeproject.com/Ajax/AJAXAutoSuggest.asp . To work in a non-ViewState environment I had to do a few tweaks but it works the best I have found while still giving me access to the page lifecycle.
Does anyone else think it's funny that Microsoft redirects everyone to a page called 'deafulto.aspx' on their msn.com site which serves as millions of peoples' home page and then embeds a flash object that doesn't work on Internet Explorer 6.0, thereby causing all of these users to be unable to get onto the internet unless they know how to work around the issue?
Turns out they have a special 'try office 2007' object that looks neat but keeps lots of people from getting on the internet.
In an earlier post I journaled some discoveries I was making while tooling around with using lazy loading in NHibernate. Steve asked a great question that (if I understand correctly) justifies demonstrating a test to see how NHibernate delivers proxy interfaces using the 'proxy="IMyInterface"' approach:
Can you elaborate on suggestion #2 above? Making the setter protected doesn't eliminate the need to declare a setter in the interface, and if you do that, the concrete class has to make the setter public.
What am I missing here - how can this really work without exposing the property publicly through the interface?
From what I understand, NHibernate is actually returning a object that, while implementing the proxy interface you determine, it is still a subclass of the object in your mapping. A test and its output should explain all this.
First the interface..note that there are no setters:
namespace Cei.eMerge.Core.Domain.HumanResources
{
public interface IJobPosition : IDomainObject<int>
{
string Name { get; }
string Description { get; }
}
}
And it's implementation:
namespace Cei.eMerge.Core.Domain.HumanResources
{
public class JobPosition : DomainObject<int>, IJobPosition
{
protected JobPosition():this(string.Empty,string.Empty)
{
}
public JobPosition(string name, string description)
{
_name = name;
_description = description;
}
private string _name;
private string _description;
public string Name
{
get { return _name; }
}
public string Description
{
get { return _description; }
}
}
}
My mapping...note the use of access='nosetter.camelcase-underscore' and remember that the proxy object is a SUBCLASS of JobPosition
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="@core.assembly@"
default-access="nosetter.camelcase-underscore">
<class name="Cei.eMerge.Core.Domain.HumanResources.JobPosition"
table="JobPosition" proxy="Cei.eMerge.Core.Domain.HumanResources.IJobPosition" lazy="true" >
<id name="Id" access="property" unsaved-value="0" >
<column name="Id"/>
<generator class="native" />
</id>
<property name="Name">
<column name="Name" not-null="true" unique-key="JobPositionName_Enterprise" sql-type="nvarchar(50)"/>
</property>
<property name="Description">
<column name="Description" not-null="false" unique="false" sql-type="nvarchar(100)"/>
</property>
</class>
</hibernate-mapping>
Now a test
[Test]
public void CanProxyJobPosition()
{
UnitOfWork.Current.Dispose();
IJobPosition jp = new JobPosition("JobTest", "BLAH");
int jpId = 0;
using(UnitOfWork.Start())
{
ISession session = ((Cei.eMerge.Common.NHibernateUnitOfWorkAdapter)UnitOfWork.Current).Session;
session.SaveOrUpdate(jp);
jpId = jp.Id;
}
using(UnitOfWork.Start())
{
ISession session = ((Cei.eMerge.Common.NHibernateUnitOfWorkAdapter)UnitOfWork.Current).Session;
Console.WriteLine("Getting object...");
object fromDb = session.Load(typeof (JobPosition), jpId);
Console.WriteLine("Is Initialized = " + NHibernateUtil.IsInitialized(fromDb) );
Console.WriteLine("Type of object is " + fromDb.GetType().ToString());
Console.WriteLine("Attempting to have proxy fetch property value..");
Console.WriteLine(((IJobPosition) fromDb).Name);
Assert.AreEqual("JobTest",((IJobPosition)fromDb).Name);
}
}
And the output
NHibernate: INSERT INTO JobPosition (Name, Description) VALUES (@p0, @p1); @p0 = 'JobTest', @p1 = 'BLAH'
NHibernate: select @@IDENTITY
Getting object...
Is Initialized = False
Type of object is ProxyInterfaceSystemSystemObject_Cei_eMerge_Core_Domain_HumanResourcesIJobPosition_NHibernate_ProxyINHibernateProxy_System_Runtime_SerializationISerializable
Attempting to have proxy fetch property value..
NHibernate: SELECT jobpositio0_.Id as Id46_0_, jobpositio0_.Name as Name46_0_, jobpositio0_.Description as Descript3_46_0_ FROM JobPosition jobpositio0_ WHERE jobpositio0_.Id=@p0; @p0 = '1'
JobTest
Here's a cute little bug that killed a few hours today.
I placed an Ajax Control Toolkit Accordion Control onto my pretty complex page today and the control wouldn't render. The <div> for the control would render for the control but it's panes were not rendering.
Having other Ajaxed controls I thot perhaps I had some kind of conflict somewhere. So I stripped those down (these were working fine). Nope.
ViewState is turned off for my website so I thot maybe that was it. Nope.
I finally whittled the problem down to a call to '_view.DataBind()' that is done in the OnInit() event from my Presenter. When I commented that out, POOF! there's my accordion control.
So I went to the source code's Sample Web Site that is included with the Toolkit and put this on the Accordion.aspx sample page:
<script runat="server">
protected override void OnInit(EventArgs e)
{
base.OnInit(e); this.DataBind();
}
</script>
Sure enough, the accordion was gone from the sample. So I am off to dig into the databinding they override and fix it. I have opened an isse on CodePlex
here .
So if you are getting issues with Toolkit controls that have child controls, check the databinding routine...
UPDATE
I fixed this by revising the OnDataBinding method of the Accordion.cs file:
/// <summary>
/// DataBind the Accordion to its panes
/// </summary>
/// <param name="e">EventArgs</param>
protected override void OnDataBinding(EventArgs e)
{
base.OnDataBinding(e);
if (DataSource != null) //Revised by Mike Nichols for preventing hiding of control when no datasource is present
{
// reset the control state
ClearPanes();
ClearChildViewState();
// and then create the control hierarchy using the datasource
CreateControlHierarchy(true);
ChildControlsCreated = true;
}
}
MIKE