# Wednesday, March 23, 2011

I’m working on the latest Entity Framework RC 4.1 that comes with Code First. In my quest I learned how the Data Annotations work on empty fields. Something I thought I knew but after being schooled by my simple MVC3 project I had a little help on twitter to set me straight.

I asked on twitter – Do Data Annotation validators run when the field is empty? Required does but my experiment on others don't seem to?

I mean, of course, duh, required would but it didn’t seem like the other validators were.  I was two validators.  The first was the String Length and I had set the Minumim and Maximum Length.  The other was the regular expression validator that wanted 4 digits.  When I emptied the fields, the validator appeared to not run because I didn’t get an error displayed.  If I entered a character then the validation appeared. 

What’s going on? It would make sense that for the String Length validator with a Minimum Length set that when nothing is entered it should fire right?  Clearly, we are below the minimum length.  I began to assume that the validators other than the Required didn’t run if the field is empty.  I always thought they did run. 

Well, @srkirkland to the rescue.  He set me straight.

@klabranche When a field is empty other vals will still run, just return valid always. Convention is non-required vals return true w/ null

…and @srkirkland would know, he runs the OSS Data Annotations Extensions project.

Convention is non-required validators will return true with nulls

I feel as though I should have known this but clearly I didn’t. Maybe, just maybe I had it way deep in the recesses of my mind. A lot of good that did me. :-) How about you?

For the purpose of this post let’s fire up reflector and see for ourselves!

regular expression annotation reflector code

All Data Annotations wield their magic in the IsValid method.  As you can see below the Regular Expression Annotation checks to see if the string coming in is null or empty and if it is return true.

public override bool IsValid(object value)
{
    this.SetupRegex();
    string str = Convert.ToString(value, CultureInfo.CurrentCulture);
    if (string.IsNullOrEmpty(str))
    {
        return true;
    }
    Match match = this.Regex.Match(str);
    return ((match.Success && (match.Index == 0)) && (match.Length == str.Length));
}

How about one more? String Length

string length annotation reflector code 

public override bool IsValid(object value)
{
    this.EnsureLegalLengths();
    int num = (value == null) ? 0 : ((string) value).Length;
    return ((value == null) || ((num >= this.MinimumLength) && (num <= this.MaximumLength)));
}

Again, you can see if the initial object coming in is null then return true.

Why this convention

I found a reference on Phil Haack’s blogs:

http://haacked.com/archive/2009/11/19/aspnetmvc2-custom-validation.aspx

Notice that if the value is null, we return true. This attribute is not intended to validate required fields. I’ll defer to the RequiredAttribute to validate whether the value is required or not. This allows me to place this attribute on an optional value and not have it show an error when the user leaves the field blank.


Where I went wrong

Back to the String Length validator. If it would fire for an empty string then we are essentially making the String Length validator a required field validator also.  This may seem logical and was my assumption. In reality, if this was true we would be making the String validator also a required validator. Having the convention on all non-required validators return true when their field is empty ensures those validators are not forcing an implicit required.  How fun would that be for a field we wanted to ensure was a certain format but could also be empty?

So there you have it.  I was ready to blame Code First or the Entity Framework, surely not my own assumptions. :-)

Happy Coding!

Posted 03.23.2011  #    Comments [0]  | 
# Friday, February 25, 2011

Visual Studio 2010 or Visual Web Developer Express 2010 or Visual Phone Developer Express 2010 that matches the language version of Silverlight Tools 4 must be installed before installation of Silverlight Tools can continue.

I was running into the above error when I tried to install Silverlight 4 Tools for Visual Studio 2010 whether I did it from the downloaded install or via the Web Platform Installer.

Downloaded Silverlight4_Tools.exe error message:

image 

WPI:

image

If you open the first log file you will find the error at the bottom:

Installation Blockers:
    1) Visual Studio 2010 or Visual Web Developer Express 2010 or Visual Phone Developer Express 2010 that matches the language version of Silverlight Tools 4 must be installed before installation of Silverlight Tools can continue.  Silverlight Tools is available in other languages at <A HREF="http://go.microsoft.com/fwlink/?LinkId=177432">http://go.microsoft.com/fwlink/?LinkId=177432</A>.

When I looked online for this error I was only seeing references to the Beta and Release Candidates of Visual Studio 2010.

After a little thought, I decided to open up the readme file for the SP1 Beta of Visual Studio 2010 that I installed a while back and there was the answer:

2.1.4. Error when you try to install Silverlight 4 Tools for Visual Studio 2010 on Visual Studio 2010 SP1 Beta

An error message may appear when you try to install Silverlight 4 Tools for Visual Studio 2010 on Visual Studio 2010 SP1 Beta. 

To resolve this issue:

Because Silverlight 4 Tools for Visual Studio 2010 (http://www.microsoft.com/downloads/en/details.aspx?FamilyID=b3deb194-ca86-4fb6-a716-b67c2604a139&displayLang=en) is included in Visual Studio 2010 SP1 Beta, you do not have to install Silverlight 4 tools separately.  Also, you do not have to uninstall Silverlight 4 Tools for Visual Studio 2010 before you install Visual Studio SP1 Beta.

There you have it.  If you installed VS 2010 SP1 Beta then you already have the Silverlight 4 Tools.  How’s that for a descriptive error message. :-)

Happy Coding!

Posted 02.25.2011  #    Comments [6]  | 
# Monday, January 17, 2011

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

Background

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");
        else
            $(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)
        Spellcheck();*/
        
}

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. :-)

TheBadAndUgly

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");
    else
        $(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");
        else
            $(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]  |