# Monday, January 17, 2011

One can never know as much as a team knows. Learning from others is a wonderful team and individual trait.


About a year ago our team was working on an intranet project. As the lead developer and I discussed the features needed, we decided to handle some tasks on the front end with jQuery. Neither one of us had much experience with jQuery and we had a tight deadline to meet. 

The Bad & The Ugly

Since we were really under the gun we split up the work and I tackled the client side jQuery. Warning – Avert eyes now if you wish not to see be subjected to ugly code!

var Editor1 = '#Editor1';
var Editor1CountLimit = 5000
var Editor1InfoArea = '#Info';

var WhyThisStratArea = '#txtVchWhyThisStrategicArea';
var WhyThisStratAreaCountLimit = 500;
var WhyThisStratAreaInfoArea = '#txtVchWhyThisStrategicAreaInfo';

var ProgramDescriptionArea = '#txtVchProgramDescription';
var ProgramDescriptionAreaCountLimit = 1000;
var ProgramDescriptionAreaInfoArea = '#txtVchProgramDescriptionInfo';

var Activity = '#txtVchActivity';
var ActivityCountLimit = 3000;
var ActivityInfoArea = '#txtVchActivityInfo';

var OtherInfo = '#txtVchOtherInfo';
var OtherInfoCountLimit = 1000;
var OtherInfoArea = '#txtVchOtherInfoInfo';

var OutcomeMandated = '#txtvchOutcomeMandated';
var OutcomeMandatedCountLimit = 1000;
var OutcomeMandatedArea = '#txtvchOutcomeMandatedInfo';

var ServiceLevelMandated = '#txtVchServiceLevelMandated';
var ServiceLevelMandatedCountLimit = 1000;
var ServiceLevelMandatedArea = '#txtVchServiceLevelMandatedInfo';

var ChangeInLevelofService = '#txtvchChangeInLevelofService';
var ChangeInLevelofServiceCountLimit = 1000;
var ChangeInLevelofServiceArea = '#txtvchChangeInLevelofServiceInfo';

$(document).ready(function() {

    ChangeTextOnKeyUp(Editor1, Editor1CountLimit, Editor1InfoArea);

    //change the length when typed in.
    TrackKeyStrokesForTextArea(WhyThisStratArea, WhyThisStratAreaCountLimit, WhyThisStratAreaInfoArea);
    TrackKeyStrokesForTextArea(ProgramDescriptionArea, ProgramDescriptionAreaCountLimit, ProgramDescriptionAreaInfoArea);
    TrackKeyStrokesForTextArea(Activity, ActivityCountLimit, ActivityInfoArea);
    TrackKeyStrokesForTextArea(OtherInfo, OtherInfoCountLimit, OtherInfoArea);
    TrackKeyStrokesForTextArea(OutcomeMandated, OutcomeMandatedCountLimit, OutcomeMandatedArea);
    TrackKeyStrokesForTextArea(ServiceLevelMandated, ServiceLevelMandatedCountLimit, ServiceLevelMandatedArea);
    TrackKeyStrokesForTextArea(ChangeInLevelofService, ChangeInLevelofServiceCountLimit, ChangeInLevelofServiceArea);

    //setup the initial value for a text area...
    $(WhyThisStratAreaInfoArea).html($(WhyThisStratArea).val().length + ' Characters Used');
    $(ProgramDescriptionAreaInfoArea).html($(ProgramDescriptionArea).val().length + ' Characters Used');
    $(ActivityInfoArea).html($(Activity).val().length + ' Characters Used');
    $(OtherInfoArea).html($(OtherInfo).val().length + ' Characters Used');
    $(OutcomeMandatedArea).html($(OutcomeMandated).val().length + ' Characters Used');
    $(ServiceLevelMandatedArea).html($(ServiceLevelMandated).val().length + ' Characters Used');
    $(ChangeInLevelofServiceArea).html($(ChangeInLevelofService).val().length + ' Characters Used');

function ChangeTextOnKeyUp(ctl, limit, info) {

    var editor = $(ctl).contents().find('iframe').eq(1);

    $(editor).load(function() {

        //initial load, set value....
        var txt = $(this).contents().find('body').text();
        $(info).html(txt.length + ' Characters Used');

        //watch cut and paste
        $(this).contents().find('body').bind('cut paste', function(e) {
            var el = $(this);
            setTimeout(function() {
                //use a delay to wait for the text to update....
                var txt = $(el).text();
                if (txt.length > limit) {
                    $(info).html(txt.length + ' Characters Used').css("color", "red");
                else {
                    $(info).html(txt.length + ' Characters Used').css("color", "");
            }, 150);

        //watch key up
        $(this).contents().keyup(function() {

            var txt = $(this).contents().find('body').text();

            if (txt.length > limit) {
                $(info).html(txt.length + ' Characters Used').css("color", "red");
            else {
                $(info).html(txt.length + ' Characters Used').css("color", "");



function TrackKeyStrokesForTextArea(ctl, limit, info) {
    $(ctl).keyup(function() {
        var txt = $(ctl).val();
        if (txt.length > limit)
            $(info).html(txt.length + ' Characters Used').css("color", "red");
            $(info).html(txt.length + ' Characters Used').css("color", "");

    $(ctl).bind('cut paste', function(e) {
        var el = $(this);
        setTimeout(function() {

            var txt = $(el).val();
            if (txt.length > limit) {
                $(info).html(txt.length + ' Characters Used').css("color", "red");
            else {
                $(info).html(txt.length + ' Characters Used').css("color", "");
        }, 150);

function ValidateEditor1Length(source, args) {
    var editor = $(Editor1).contents().find('iframe').eq(1);
    var txt = editor.contents().find('body').text();
    var isValid = txt.length > 0 && txt.length <= Editor1CountLimit;
    args.IsValid = isValid;

function ValidateWhyThisStratAreaLength(source, args) {
    var txt = $(WhyThisStratArea).val();
    var isValid = txt.length > 0 && txt.length <= WhyThisStratAreaCountLimit;
    args.IsValid = isValid;
    /*if (!isValid)

function ValidateProgramDescriptionLength(source, args) {
    var txt = $(ProgramDescriptionArea).val();
    var isValid = txt.length > 0 && txt.length <= ProgramDescriptionAreaCountLimit;
    args.IsValid = isValid;

function ValidateActivityLength(source, args) {
    var txt = $(Activity).val();
    var isValid = txt.length > 0 && txt.length <= ActivityCountLimit;
    args.IsValid = isValid;
function ValidateOtherInfoLength(source, args) {
    var txt = $(OtherInfo).val();
    var isValid = txt.length > 0 && txt.length <= OtherInfoCountLimit;
    args.IsValid = isValid;
function ValidateOutcomeMandatedLength(source, args) {
    var txt = $(OutcomeMandated).val();
    var isValid = txt.length > 0 && txt.length <= OutcomeMandatedCountLimit;
    args.IsValid = isValid;
function ValidateServiceLevelMandatedLength(source, args) {
    var txt = $(ServiceLevelMandated).val();
    var isValid = txt.length > 0 && txt.length <= ServiceLevelMandatedCountLimit;
    args.IsValid = isValid;
function ValidateChangeInLevelofServiceLength(source, args) {
    var txt = $(ChangeInLevelofService).val();
    var isValid = txt.length > 0 && txt.length <= ChangeInLevelofServiceCountLimit;
    args.IsValid = isValid;

Ugly!  Oh ya, we’ve all seen it and we’ve all most assuredly done it at least once. :-)


So what is all this code doing? Basically only three things. It’s setting and tracking the max length allowed in a text area, updating the number of characters typed and showing a message/validating when an entry goes over it’s maximum length.

Why do all this when we can do this with validator controls? We wanted the UI to be more responsive and interactive for the users. We wanted the number of characters along with a warning to the user when they go over the limit before they try to save their work.

Now, I could give every excuse for this horrible looking code. We’ve all been here before.  Not enough time to do it right but we have to get it out the door none-the-less.  I knew the code was bad and could be improved upon.  Still, the code is ugly.

The Good

We had a new team member join our team about four months ago and we happened to be working on this project again.  His jQuery skills brought to bear on this project was just what we needed.

To fix MY monstrosity he created hidden fields tied to the text areas and got rid of the setup variables.  It may sound like we’re swapping one mess for another but it helped greatly in reducing the code needed as well as improved the readability and flow.

<asp:TextBox ID="txtWhyThisStrategicArea" cssClass="spellcheck" runat="server" TextMode="MultiLine"></asp:TextBox>
<input runat="server" type="hidden"  id="txtWhyThisStrategicAreaMaxLength" value="500" />
<input runat="server" type="hidden"  id="txtWhyThisStrategicAreaRequired" value="true" />
// Text field character counter
$('#txtProgramName, #txtWhyThisStrategicArea, #txtContactPerson).each(function () {

    var length = $(this).val().length;
    var limit = $('#' + $(this).attr('id') + 'MaxLength').val();
    var maxInfoArea = $('#' + $(this).attr('id') + 'MaxArea');
    var displayText = ' of ' + limit + '  Characters Used.';

    //set up the initial display
    if (length > limit)
        $(maxInfoArea).html(length + displayText).addClass("warning");
        $(maxInfoArea).html(length + displayText).removeClass("warning");

    //hook the keyup
    $(this).keyup(function () {
        var length = $(this).val().length
        if (length > limit)
            $(maxInfoArea).html(length + displayText).addClass("warning");
            $(maxInfoArea).html(length + displayText).removeClass("warning");


169 lines of code down to 24.  Deleting code, it’s a beautiful thing!

Knowledge, Learning and Sharing – Our Biggest Asset. 

Knowledge and learning are perhaps our biggest asset but are we open to receiving it and sharing it?

I am convinced our attitude in this arena is a huge factor of our success. We never think it’s a bad thing to go get lessons from someone better than us for other activities like learning an instrument.  Why wouldn’t we think the same way in our profession. Do we believe it will make us look weak or expose us in such a way that would jeopardize our job? It’s more dangerous to think this way than it is to actively engage with those better than ourselves to improve.

Stock Photo

Learning from others should be embraced by all.  When we close off our mind to being teachable we will suffer and so will the team.  Contributions from all team members is valuable.  Everyone from the Janitor to the CEO can provide valuable insight.  The more an individual and team thinks and acts like this the more likely they are to grow stronger mentally, improve their technical and social skills, and ultimately help produce better solutions for everyone. 

Happy Coding!

Posted 01.17.2011  #    Comments [2]  | 
# Thursday, November 11, 2010

This weekend I was trying to install the WebMatrix and it was hanging on the SQL CLR types install.  I’ve had a couple of times now when the Web Platform Installer doesn’t quite work and I don’t get any error.

I tried a couple of things like shutting down SQL Server, restarting WebPI, etc.  I found the easiest way around this is to use WebPI’s installation information window to see what the prerequisites are and run them manually.


WebPI downloads the install files to C:\Documents and Settings\[USERNAME]\Local Settings\Application Data\Microsoft\Web Platform Installer\installers.


Manually installing the prerequisites worked even though WebPI hung on the SQLCLRTypes install. 

It’s nice to know where WebPI downloads the install files in case you want to backup the files or install on another box without having to re-download them.  You could copy the installers directory to/from machines to avoid this.

The Web Platform Installer is a great little tool that has made life a lot easier but it’s not perfect.  Hopefully this little trick will help someone else out when WebPI doesn’t quite do the trick on it’s own.

Happy Coding!

Posted 11.11.2010  #    Comments [6]  | 
# Saturday, November 06, 2010

There are a lot of things that can sink an app but none more visible, yet often overlooked, than a poor user experience.sinking

User Experience Matters

Duh, of course!  Something we all know, right? But do we take care of the UI with the same passion as we do with code?  In order to ensure the user experience remains relevant and a pleasant experience requires refactoring as well.  Like code that can erode over time, the user experience can suffer the same fate.

 The Eroding UI Example

Our team has worked on an application that’s used throughout the organization to enter requests for budget.  The application has gone through several heavy revisions over the past few budget cycles and each time we have had a very tight timeline.  It’s always been a mad rush to make the modifications necessary to accommodate the changes in the process. 

Over time, the UI slowly began to mismatch how the users worked.  The UI grew into a large form and required all the fields to be filled in before a user could save any work.  The nature of how the user worked was very different.

The user process is highly iterative and collaborative within business units who are developing requests.  They often work on one section’s wording over and over until they have it just right and then move to the next section. 

Unfortunately, the UI didn’t handle this well.  Fields that required a lot of text (several paragraphs) were forced into a small text area that had to be scrolled to read.  Users would just print a report to see all the text to get around this.  With all fields being required, users entered garbage/placeholder information into fields in order to save their work.



New ideas were introduced into the process and the application grew.  A few more forms were added.  The flow was changed so that the user was required to enter this information and save it as a separate step from the main form.  The items were required but very much felt like a bolt on and not as important.


An Aside…

Who’s fault is this?  Mine.  It’s the job of the team leader to remove barriers to success and help foster and create situations that lead to success.  It took me a few tries to get us to the point where the team could work their magic properly instead of being hamstrung by constraints that could have been better managed by yours truly. 


This year we took the time to give the application some tender loving care.  The vision was to create logical sections of information.  Maximize space utilization for the sections and move away from strict validation to a routine that would allow saving work anytime while quickly showing what is left to be completed.

The team took the vision and ran with it.



The team created a new navigation section that is very pleasing on the eyes and more visible.  The team nearly eliminated the need to scroll vertically on the page. When scrolling does occur the team floated the navigation so that it will always be at the top of the screen.  The save and cancel operation work on the entire record, versus several forms that required saving separately.  Additionally, the user can save at anytime and reduced the number of absolutely required fields down to two.



The team created tabs to group similar information into sections.  The tabs have an icon representing if that section has been completed or not. 

Additional information is in each tab to help the user.  When a tab has a green check mark the tab includes a message stating it’s complete.


If the tab is incomplete, represented by the yellow warning sign, a message is shown stating what is needed and each element on the form that needs attention has the same warning icon and it’s own message.


With “softer” validation in place the user is able to work on a request in whatever order they need to and with a glance see what is incomplete.  They can also save their work in whatever state they want.  This lends itself well to the iterative drafting process of the initial budget development cycle.

Handling large text needs was simplified by giving them their very own tab.


More obtrusive (yet nice) messaging was added to the system to help ensure the user doesn’t accidently/inadvertently do something.


The real test - What are the users saying?

In the users tests we’ve conducted we received rave reviews.  We have a few weeks to go to finalize the application and go live with the entire user community.  It’s exciting!

It’s coming along great and I am extremely pleased with the team’s redesign and the large undertaking to replace the strict validation with a softer/gentler validation technique to serve the users better.  Kudos to the team!


The UI is not immune to change and requires the same level of care that code does.  The need to develop changes can erode the UI just like code.  The UI requires our attention and needs refactoring as well to stay relevant.

Happy Coding!

Posted 11.06.2010  #    Comments [0]  |