February 2009 Entries

A couple of weeks ago, I was working on some client side UI elements on one of my projects.  I was able to apply some things that I have learned recently (some more recently than others).

The problem is that I have a domain object that tracks the selected days of the week.  Currently the object uses a List<DayOfWeek> to manage the selections.  (This is not exactly the most safe thing because a client could set the list to null.  However, that's a different subject.)  What I was looking for was an easy way to initialize all of the check boxes on the client side when the user chose to create a new schedule or modify an existing one.  Also, I need to apply the changes to the check boxes whenever the user committed his changes.

The solution turned out to be fairly straight forward.  Initially, I had multiple check boxes and quickly discovered how cumbersome that would be.  I could only work with the checkboxes on a one-by-one basis.  Though I had never the use for it before, I dug up the ASP.Net CheckBoxList to see if it would meet my needs.  It turned out to be a winner.

The aspx:

    1 <asp:CheckBoxList ID="WeeklyDaysOfWeek" runat="server" RepeatLayout="Flow" RepeatDirection="Horizontal">
    2     <asp:ListItem Text="Mon" Value="Monday"></asp:ListItem>
    3     <asp:ListItem Text="Tue" Value="Tuesday"></asp:ListItem>
    4     <asp:ListItem Text="Wed" Value="Wednesday"></asp:ListItem>
    5     <asp:ListItem Text="Thu" Value="Thursday"></asp:ListItem>
    6     <asp:ListItem Text="Fri" Value="Friday"></asp:ListItem>
    7     <asp:ListItem Text="Sat" Value="Saturday"></asp:ListItem>
    8     <asp:ListItem Text="Sun" Value="Sunday"></asp:ListItem>
    9 </asp:CheckBoxList>


The simplest piece was getting the results from the client to my domain object.  With a little bit of Linq, this was cake:

    1 schedule.ScheduleCriteria.DaysOfWeek = (from ListItem li in WeeklyDaysOfWeek.Items
    2                                         where li.Selected
    3                                         select li.Value.ConvertTo<DayOfWeek>()).ToList();

By the way, ConvertTo is an extension method I wrote for "string" to convert to an Enum. 

Regarding the Linq query, it really simplifies grabbing selected items.  This can be applied to a ListBox with SelectionMode = Multiple as well.  In this case, I am looking at all of the items in the CheckBoxList, grabbing only those that are Selected and converting the Value to an Enum.  Previously, without Linq, we would have had to manually construct a list by manually looping the items and adding selected items to the list.

The next piece is getting the data from the domain object into the UI component.  I had two scenarios.  The first was in the context of creating a new schedule.  In this case, weekdays should be selected by default.  (You could argue that default logic doesn't belong in the UI.  Again, off topic).  The second scenario involved editing an existing schedule.  To enhance usability, I am displaying the "Add/Edit Schedule" dialog with a couple of update panels supported by jQuery.

If the user clicks "Add", then the partial postback results in emitting jQuery statements that update all of the input controls with their defaults.  For the CheckBoxList in particular:

    1 for( int i = 0; i < WeeklyDaysOfWeek.Items.Count; i++ )
    2 {
    3     builder.AppendFormat( "jQuery('#{0} > input:checkbox:eq({1})').attr('checked',{2});",
    4                          WeeklyDaysOfWeek.ClientID,
    5                          i,
    6                          (i < 5) ? "true" : "false" );
    7 }

To explain the jQuery, the selector is grabbing the Nth ("eq") checkbox (":checkbox") child of the WeeklyDaysOfWeek CheckBoxList.  Then, the "checked" attribute of first 5 (Mon-Fri) is checked, and unchecked for the last two (Sat, Sun).
So:
  • "#{0}"  <--  WeeklyDaysOfWeek.ClientID
    To select the parent control by ID
  • " > input:checkbox"
    To select all checkbox children
  • ":eq({1})"  <-- i (the index of the item we care about)
    To select a specific child by index
  • ".attr("checked",{2})  <--  (i < 5) ? "true" : "false"
    To check/uncheck the child
In the case where the user is editing an existing schedule, the loop is basically the same.  The only different is how to determine if each check box should be checked.  In this case, we simply check to see if the list contains the day of week that the check box represents:

    1 for( int i = 0; i < WeeklyDaysOfWeek.Items.Count; i++ )
    2 {
    3     var dow = WeeklyDaysOfWeek.Items[i].Value.ConvertTo<DayOfWeek>();
    4     builder.AppendFormat( "jQuery('#{0} > input:checkbox:eq({1})').attr('checked',{2});",
    5                          WeeklyDaysOfWeek.ClientID,
    6                          i,
    7                          schedule.ScheduleCriteria.DaysOfWeek.Contains( dow ).ToString().ToLower() );
    8 }

Anyway, I was very pleased (especially with the Linq piece) at how the combination of all of these components/tools came together to provide a simple solution to my problem.

Tags: ,
A couple of weeks back I attended a session on Unobtrusive JavaScript (UJS) and jQuery by John Teague.  This was probably the biggest thing I took from the Houston TechFest.  Recently, I found the opportunity to put my new knowledge to good use.  I haven't reach the unobtrusive part of John's presentation, but jQuery is my first step.

Documentation

I found the jQuery API documentation to be invaluable.  At first, as a reference, it was a little difficult to find what I need.  For example the "val()" function.  Not knowing the name of the function hindered my search on the documentation.  Of course, as tons of people have already been using jQuery, I did a quick search on the Net and found what I was looking for.

Selectors

One of the key components of jQuery is the selector syntax.  Already being very familiar with CSS and XPath, I found selectors in jQuery to be quite natural.  The "." (dot) represents a css class, the "#" represents an target by id.  If you know CSS selectors, you have more than enough to get started with jQuery selections. 

For more advanced selection, you can use attribute selectors, like $("input[type='checkbox']").  This feels very natural to me with my knowledge of XPath.  However, jQuery provides a lot of shortcuts.  In this example, you can simply use $("input:checkbox").  There are a lot of other filters you can use.  The jQuery API documentation covers them very well.

Chaining

Chaining of jQuery functions can be very useful.  In particular, when you don't want to "select" the same component more than once, you have a couple of options.  You could store the selection in a local variable (e.g.  var tmp = $("#mySelector")).  Or, you could simply chain commands.  Here is an example of using chaining to first change the selection on a drop down list, then trigger the "onchange/change" event:  $('#mySelector').val('somevalue').trigger('change'); 

Could that be any easier?!  I remember the days of writing my own javascript to find an option by value and "select" it.  With jQuery, my javascript is becoming much more elegant.

Cross-Browser Compatibility

The product I am working on right now is for a customer who is using IE6 as a corporate standard.  It's been annoying at best reading about how to do one thing or another with CSS only to find out that it doesn't quite work in IE6.  A simple example is the ":hover" selector.  It simply doesn't work in IE6 for anything other than anchors.  Well, jQuery has a hover function that allowed me to get the behavior I was looking for.  Now hovering over my GridView rows works like a charm.

Also, attaching events is hassle free.  You don't have to worry about what browser you are using.  Of course, this is perhaps the primary purpose of a library like jQuery, but it deserves mention.

Conclusion

I had been thinking, I need to learn more about jQuery and see how I can use it on my web applications.  Now, I wish I had taken the time to investigate the library sooner.  Hopefully, this post will inspire those of you on the fence to play with jQuery today.

Happy coding...

Tags:

I've been using ReSharper now for about one and a half weeks.  Now that I am writing some new code, I have more to share.

The most helpful and time saving feature that I have encountered so far is code generation.  In one case, I had added a couple of new properties to one of my entity objects.  In actuality, I add the code in my test first.  R# quickly recognized that the property didn't exist and gave me the option to create a field or property.  I chose the property option and presto, R# found my class and added the property.  It can do this with interfaces to.  This makes TDD that much easier.

I also started using the refactoring tools more as well, and they are very impressive.  I have used Refactor!Pro in the past.  One of the things I like about R!Pro was that it would let you chose where you want to place a newly extracted method.  I haven't seen that yet in R#.  Perhaps it's an option somewhere.

I still occasionally see some ASP.Net issues with resolving control references, but I basically ignore the red text and I modified the R# options to use VS intellisense instead of R#.  Then at least, I can get Intellisense even when R# thinks (incorrectly) that there is an error.

Recently I blogged that the Resharper install corrupted TestDriven.Net.  I followed up to say that certainly wasn't the case.  In fact, there was nothing wrong with TestDriven.Net.  The problem was between the keyboard and chair.

I kept getting an error about not being able to load one of my base test classes.  At compile time everything was fine.  I would get the error only when trying to run the test in TestDriven.Net.  R# could run the tests with no problem.  This led me to believe it was a problem with TD.Net.  Later I discovered that my builds were reporting zero tests.  I investigated to find that the same error loading my test class was occuring on the build server.  This ruled out an issue specifically with TD.Net.

I searched the web and couldn't find anything that would help.  The only clue I had was that after the initial error, there was another stating that NUnit 2.4.6 could not be loaded.  Since I am using NUnit 2.4.8, this was strange.  I thought to myself, "Why is the test runner trying to load 2.4.6?".  This should have keyed me into the problem right away, but it didn't.

Finally, I called a friend and he suggested I try running the tests in the NUnit GUI.  Well, I got an error just loading the test assembly.  Again, the error stated that the runner couldn't load NUnit 2.4.6.  Suddenly the problem became clear. 

I had recently introduced a shared testing library that we could use on multiple projects.  What I didn't realize at the time was that the shared solution was still referencing NUnit 2.4.6.  After updating the dependency to 2.4.8 and recompiling everything, TestDriven.Net and my build were both running tests successfully again.

I can't help but think that if I had a pair working with me, this would have been resolved much more quickly.