Ever since ASP.NET MVC Framework 2.0 has released I did not have a change to work with the new validation logic.
The DataAnnotations that are the only place where you have to keep logic related to validation, no more having to write the same logic in several places.

Today I wanted to give it a try, since I can delete lots of code from my views, if it works as promised.
I want to use jquery.validate.js (ships out of the box with Visual Studio 2010 and ASP.NET MVC Framework 2.0. But to my surprise only files that support the MicrosoftMvcValidation.js are available with the project template for a ASP.NET MVC 2 Web Application.

MicrosoftMvcJQueryValidation.js
With the ASP.NET MVC 2 RTM release a file named MicrosoftMvcJQueryValidation.js was shipped. I downloaded the RTM once again and got the MicrosoftMvcJQueryValidation.js file.

In my view I now add the following script tags:



Listing 1: Script tags

Enable validation on a view clientside
Than below script tags, I put the following call to enable validation on the client.

<% Html.EnableClientValidation(); %>

Listing 2: Html.EnableClientValidation

This call (listing 2) bubbles up the validation rules as Json data, the MicrosoftMvcJQueryValidation.js leverages jQuery to do the client side validation.

I added the code from listing 1 and 2 onto the  logon.aspx from a vanilla ‘file > new project > ASP.NET MVC 2 Web Application’ and it worked as I expected.

Contents MicrosoftMvcJQueryValidation.js
Because it took me some effort to get the MicrosoftMvcJQueryValidation.js I will put the code here:

/// 
/// 

// register custom jQuery methods

jQuery.validator.addMethod("regex", function(value, element, params) {
    if (this.optional(element)) {
        return true;
    }

    var match = new RegExp(params).exec(value);
    return (match && (match.index == 0) && (match[0].length == value.length));
});

// glue

function __MVC_ApplyValidator_Range(object, min, max) {
    object["range"] = [min, max];
}

function __MVC_ApplyValidator_RegularExpression(object, pattern) {
    object["regex"] = pattern;
}

function __MVC_ApplyValidator_Required(object) {
    object["required"] = true;
}

function __MVC_ApplyValidator_StringLength(object, maxLength) {
    object["maxlength"] = maxLength;
}

function __MVC_ApplyValidator_Unknown(object, validationType, validationParameters) {
    object[validationType] = validationParameters;
}

function __MVC_CreateFieldToValidationMessageMapping(validationFields) {
    var mapping = {};

    for (var i = 0; i < validationFields.length; i++) {
        var thisField = validationFields[i];
        mapping[thisField.FieldName] = "#" + thisField.ValidationMessageId;
    }

    return mapping;
}

function __MVC_CreateErrorMessagesObject(validationFields) {
    var messagesObj = {};

    for (var i = 0; i < validationFields.length; i++) {
        var thisField = validationFields[i];
        var thisFieldMessages = {};
        messagesObj[thisField.FieldName] = thisFieldMessages;
        var validationRules = thisField.ValidationRules;

        for (var j = 0; j < validationRules.length; j++) {
            var thisRule = validationRules[j];
            if (thisRule.ErrorMessage) {
                var jQueryValidationType = thisRule.ValidationType;
                switch (thisRule.ValidationType) {
                    case "regularExpression":
                        jQueryValidationType = "regex";
                        break;

                    case "stringLength":
                        jQueryValidationType = "maxlength";
                        break;
                }

                thisFieldMessages[jQueryValidationType] = thisRule.ErrorMessage;
            }
        }
    }

    return messagesObj;
}

function __MVC_CreateRulesForField(validationField) {
    var validationRules = validationField.ValidationRules;

    // hook each rule into jquery
    var rulesObj = {};
    for (var i = 0; i < validationRules.length; i++) {
        var thisRule = validationRules[i];
        switch (thisRule.ValidationType) {
            case "range":
                __MVC_ApplyValidator_Range(rulesObj,
                    thisRule.ValidationParameters["minimum"], thisRule.ValidationParameters["maximum"]);
                break;

            case "regularExpression":
                __MVC_ApplyValidator_RegularExpression(rulesObj,
                    thisRule.ValidationParameters["pattern"]);
                break;

            case "required":
                __MVC_ApplyValidator_Required(rulesObj);
                break;

            case "stringLength":
                __MVC_ApplyValidator_StringLength(rulesObj,
                    thisRule.ValidationParameters["maximumLength"]);
                break;

            default:
                __MVC_ApplyValidator_Unknown(rulesObj,
                    thisRule.ValidationType, thisRule.ValidationParameters);
                break;
        }
    }

    return rulesObj;
}

function __MVC_CreateValidationOptions(validationFields) {
    var rulesObj = {};
    for (var i = 0; i < validationFields.length; i++) {
        var validationField = validationFields[i];
        var fieldName = validationField.FieldName;
        rulesObj[fieldName] = __MVC_CreateRulesForField(validationField);
    }

    return rulesObj;
}

function __MVC_EnableClientValidation(validationContext) {
    // this represents the form containing elements to be validated
    var theForm = $("#" + validationContext.FormId);

    var fields = validationContext.Fields;
    var rulesObj = __MVC_CreateValidationOptions(fields);
    var fieldToMessageMappings = __MVC_CreateFieldToValidationMessageMapping(fields);
    var errorMessagesObj = __MVC_CreateErrorMessagesObject(fields);

    var options = {
        errorClass: "input-validation-error",
        errorElement: "span",
        errorPlacement: function(error, element) {
            var messageSpan = fieldToMessageMappings[element.attr("name")];
            $(messageSpan).empty();
            $(messageSpan).removeClass("field-validation-valid");
            $(messageSpan).addClass("field-validation-error");
            error.removeClass("input-validation-error");
            error.attr("_for_validation_message", messageSpan);
            error.appendTo(messageSpan);
        },
        messages: errorMessagesObj,
        rules: rulesObj,
        success: function(label) {
            var messageSpan = $(label.attr("_for_validation_message"));
            $(messageSpan).empty();
            $(messageSpan).addClass("field-validation-valid");
            $(messageSpan).removeClass("field-validation-error");
        }
    };

    // register callbacks with our AJAX system
    var formElement = document.getElementById(validationContext.FormId);
    var registeredValidatorCallbacks = formElement.validationCallbacks;
    if (!registeredValidatorCallbacks) {
        registeredValidatorCallbacks = [];
        formElement.validationCallbacks = registeredValidatorCallbacks;
    }
    registeredValidatorCallbacks.push(function() {
        theForm.validate();
        return theForm.valid();
    });

    theForm.validate(options);
}

// need to wait for the document to signal that it is ready
$(document).ready(function() {
    var allFormOptions = window.mvcClientValidationMetadata;
    if (allFormOptions) {
        while (allFormOptions.length > 0) {
            var thisFormOptions = allFormOptions.pop();
            __MVC_EnableClientValidation(thisFormOptions);
        }
    }
});

Listing 3: Contents MicrosoftMvcJQueryValidation.js

Henry Cordes
My thoughts exactly…

Currently rated 4.2 by 5 people

  • Currently 4.2/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

I used to like Syntaxhighlighter, but when I started using blogengine.net I did not implemented it. In blogengine.net a plugin is available from manoli.net.
The bad part is, that I have to go to the manoli.net website and generate html from my C#, SQL, Javascript and other code, every time I want to use code in my posts.

So I started to look into Syntaxhighlighter again. It matured quite a bit, but still lacks type support (manoli also does not support this, by the way!). Still, I like it a lot, I suppose it can save me lots of time.

Beauty of Code jQuery plugin

Lars Corneliussen wrote a jQuery plugin for SyntaxHighlighter and it makes the process of incorporating it on a html page a lot smoother. I used this with my first tryout to use the plugin in blogengine.net. I think it is an elegant way to use syntax highlighter, so i want to share, how it works. Ofcourse you have to reference jQuery in the head element of your page. In addition to that we add a reference to the jquery.beautyofcode.js (the code written by Lars).

	
	

Listing: 1

To initialize the highlight functionality, you have to add the following javascript code to a page:

$.beautyOfCode.init({
	brushes: ['Plain', 'CSharp', 'Xml', 'JScript', 'Css', 'Sql']}
);

Listing: 2

All I have to do now is to start using it, as followes:

    
	<code class="JScript">
		alert('Hi, I love highlighted syntax!');   
	</code>  

Listing: 3

The point is that my blog does not accept the pre tags with a nested code tag that has a class attribute. TinyMCE is used and somehow it strips the entire class attribute, thus the highlighting never takes place…

I am now using the default way, without jQuery. This works niceley, with a caveat: XML tags (html) are transformed to uppercase, when saving a post through the TinyMCE standard blogengine.net manner of creating and editing posts, but also when using Windows LiveWriter. I will try to find out more about this, because I really do not like uppercase tagnames!

The default syntax highlighter configuration

First you need to reference the scripts and stylesheets syntax highlighter needs to do the job, so in the head of the page you need to highlight code on, you add the following js and css references:













Listing: 4

Of course you only need to add the the brushes that you really use.

Next you need to instantiate the javascript object, you need to add the following javascript in a script block (also in the head of the page):

SyntaxHighlighter.config.clipboardSwf = 'scripts/clipboard.swf';
SyntaxHighlighter.all();

Listing: 5

The clipboard.swf (Flash file) is used to make it possible for the user that visits your site, to copy the code to his or her clipboard, without an alert from the Operating System. The .all() method initializes takes care of the actual highlighting.

Now I can use it, by letting syntax highlighter know what brush to use, like this:

    
	alert('Hi, I love highlighted syntax!');    

Listing: 6

The next few posts, I will try it out some more, but the first impression is a positive one.

Henry Cordes
My thoughts exactly…

Currently rated 4.5 by 2 people

  • Currently 4.5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Using jQuery with MS CRM 4.0

Published 11/21/2008 by Henry in CRM | Javascript | jQuery

Doing a MS CRM 4.0 project, where the requirement is to create a toolbar button on a detailform.
This button needs to open a window (prefferably a modal window). In this window users can make some selections and when submitting, some processing will be done using these selections, this is not important for this post.

When thinking about this requirement, I really wanted to use something like the ASP.NET AJAX Control ToolKit's Modal Popup. Also I am really impressed by jQuery, a JavaScript library, that is an elegant and easy to use abstraction over JavaScript. It simplifies document traversing, event handling, animation and Ajax interactions. I knew about jqModal a jQuery plugin that simplifies display of dialogs and modal windows.

NOT FULLY SUPPORTED
If you are a developer
working with MS CRM, you know that it is higly customizable, but within it's own boundries. An important matter on this project particularly is that customizations need to be supported by Microsoft. What I am about to explain is NOT FULLY SUPPORTED, because I will use external.js files, using a link element to import it in the page. But I want to explain also, that it is possible to do this without using external files, you could paste the contents of the 'jquery-1.2.6.min.js' file into the onload function of the detailform's property window.

I wanted to give it a try, because, well just think about the possibilities for a while. If you can use jQuery in CRM development, a lot of possibilities open up.

FIRST STEPS
So the first thing to do is to put the 'jquery-1.2.6.min.js' file in a '_customscript' folder in the CRMWEB root folder. Next I need a way to reference the file, so it's contents can be called from the page. Like I already posted in my blogpost External js file and CRM, I like to do this using the Msxml2.XMLHTTP object. The function reads the contents of a file and imports all functions found into the current namespace, so they are accessible.

   1:  function load_script (url) 
   2:  { 
   3:      var x = new ActiveXObject("Msxml2.XMLHTTP"); 
   4:      x.open('GET', url, false); x.send(''); 
   5:      eval(x.responseText); 
   6:      var s = x.responseText.split(/\n/); 
   7:      var r = /^function\s*([a-z_]+)/i; 
   8:      for (var i = 0; i < s.length; i++) 
   9:      { 
  10:          var m = r.exec(s[i]); 
  11:          if (m != null) 
  12:              window[m[1]] = eval(m[1]); 
  13:      } 
  14:  } 
  15:   
  16:  load_script("/_customscript/jquery-1.2.6.min.js"); 
  17:  load_script("/_customscript/jqModal.js"); 
  18:  load_script("/_customscript/jqDnR.js"); 
  19:  load_script("/_customscript/customscript.js"); 

Listing 1

As you can read in listing 1, the 'jqModal.js' file is also referenced, this holds the jqModal library, I also load 'jqDnR.js' a jQuery library that supports dragging and resizing. I also load 'customscript.js', this is a custom javascript file that will hold the function that is called when the button on the details window is clicked.

CSS STYLESHEET
Because we do not want to mess with the stylesheets used by CRM we also need to add a stylesheet containig the modal window's styles to the filesystem. I made a css folder below the _customscript folder, the folder where I put the javascript libraries. You could add these styles through Dom manipulation in JavaScript also, to keep the customization supported by Microsoft!
I use the following code, you can do this in the OnLoad function of the details form, or in a separate function, that you call from the Onload function. I use the function that also checks if the reference exists already, but for simplicity put this in the On Load after listing 1.

   1:  var styleSheet = '/_customscript/css/modalWindow.css';
   2:  var head = document.getElementsByTagName("head")[0];
   3:  var linkElement = document.createElement('<link rel="stylesheet" type="text/css" href="'+ styleSheet +'" />');
   4:  head.appendChild(linkElement);

Listing 2

In listing 2 I create a link element with a reference to a style sheet and append this element to the head element of the html page (CRM's detail form).

Listing 3 will display the contents of the css style sheet:

   1:  .jqmWindow  
   2:  { 
   3:  	display: none; 
   4:  	position: fixed; 
   5:  	top: 17%; 
   6:  	left: 50%; 
   7:  	margin-left: -300px; 
   8:  	background-color: #E4EEF9; 
   9:  	border-width: 1px; 
  10:  	border-style: solid; 
  11:  	border-color: #0a6cce; 
  12:  	padding: 0px; 
  13:  	width: 374px; 
  12:  	height: 264px; 
  13:  } 
  14:  
  15:  .jqmOverlay { filter: alpha(opacity=70); opacity: 0.7; }
  16:  
  17:  .jqmTitle 
  18:  { 
  19:  	cursor: move; 
  20:  	background-color: #6793CC; 
  21:  	border: solid 1px #6793CC; 
  22:  	color: #FFFFFF; 
  23:  	font-weight: bold; 
  24:  	width: 100%; 
  25:  } 
  26:  
  27:  .jqmTitleText { float: left; position: relative; }
  28:  
  29:   /* Fixed posistioning emulation for IE6  
  30:      Star selector used to hide definition from browsers other than IE6  
  31:      For valid CSS, use a conditional include instead */  
  32:  * html .jqmWindow 
  33:  { 
  34:    position: absolute; 
  35:    top: expression((document.documentElement.scrollTop || document.body.scrollTop) + 
  36:         Math.round(17 * (document.documentElement.offsetHeight || document.body.clientHeight) / 100) + 'px'); 
  37:  } 
  38:  
  39:  .jqmIFrame { width: 100%; height: 100%; }
  40:  
  41:  .jqmClose 
  42:  {  
  43:    width: 20px; 
  44:  	font-weight: bold; 
  45:    float: right; 
  46:  	cursor: pointer; 
  47:    color: #FFFFFF; 
  48:  	background: #6793CC; 
  49:    border: 1px solid #6793CC; 
  50:  }  

Listing 3

CREATE DIV (MODAL WINDOW)
Next we need to add the div to CRM's details page. I wanted to this using the DOM, but for some reason it would not work. If anybody knows the reason why, please let me know!
Instead i used  the following approach, again: you can do this in the OnLoad function of the details form, or in a separate function, that you call from the Onload function.
In the real world project I use the function, but for simplicity in this example, I just put this in the On Load after listing 2.

   1:  // Not using dom to add new elements, because for some reason that does not work!
   2:  var body = document.getElementsByTagName("body")[0];
   3:  var modalDiv = document.createElement('<div id="dialog" class="jqmWindow" ></div>');
   4:  var titleDiv = document.createElement('<div id="jqmTitle" class="jqmTitle"></div>');
   5:  var modalTitle = document.createElement('<span id="jqmTitleText" class="jqmTitleText"></span>');
   6:  modalTitle.innerText = 'Documentenuitvoer';
   7:      
   8:  var modalButton = document.createElement('<button class="jqmClose" ></button>');
   9:  modalButton.innerText = 'X';
  10:      
  11:  var modalIFrame = document.createElement('<iframe id="jqmContent" class="jqmIFrame" frameborder="0" scrolling="no"></iframe>');
  12:  titleDiv.appendChild(modalButton);
  13:  titleDiv.appendChild(modalTitle);
  14:      
  15:  modalDiv.appendChild(titleDiv);
  16:  modalDiv.appendChild(modalIFrame);  
  17:      
  18:  body.appendChild(modalDiv);

Listing 4

As you can read in listing 4 first I get the body element and assign it to the 'body' variable. Than I create two div elements, the reason I do not create a div element and use setAttribute to set the attributes, but create the element in one line using a string containing the whole tag is that I could not get it to work, again let me know if you do! The first div will be our modal window. The second with id 'jqmTitle' is the header. Next I Create a span that will contain the text for the header, a (close) button that will be placed in the header on the right side.
And an iframe, in this iframe any (custom) page can be loaded.

INITIALIZE MODAL WINDOW USING jQUERY
To initialize the modal window, we have to add listing 5 to the OnLoad function of the details form in CRM after listing 4.

   1:  $().ready(function() {
   2:    $('#dialog').jqm();
   3:    $('#dialog').jqm().jqDrag('#jqmTitle'); 
   4:  });

Listing 5

In Listing 5 we use jQuery syntax. We attach the code to the OnReadyState eventhandler of the document. when the document is fully loaded the element with id 'dialog' will be registered as a jqModal window by line 2. Line 3 registers the dragging functionality of the element with id 'jqmTitle' inside the modal window (our header div).

CUSTOMIZE ISV.CONFIG XML
Now we only have to do two more things, the first is that we have to change isv.config.xml to create the button on our detail form, I use the contact and need the modal window functionality on the contact entity's form. I made the following change to the isv.config.xml:

   1:  <!-- Microsoft Customer Relationship Management Entities (Objects) -->
   2:  <Entities>
   3:      <Entity name="contact">
   4:        <MenuBar>
   5:          <CustomMenus>
   6:            <Menu>
   7:              <Titles>
   8:                <Title LCID="1043" Text="Documenten" />
   9:              </Titles>
  10:              <MenuItem JavaScript="ShowModalWindow();">
  11:                <Titles>
  12:                  <Title LCID="1043" Text="Plaats document in DocumentUitvoer" />
  13:                </Titles>
  14:              </MenuItem>
  15:            </Menu>
  16:          </CustomMenus>
  17:        </MenuBar>
  18:      </Entity>
  19:  </Entities>

Listing 6

Listing 6 displays part of the xml, the IsvConfig element holds an element configuration , which holds the Entities element. I need the contact Entity. The MenuBar element holds CustomMenus, which holds Menu. I added a Menu element with Title "Documenten" (LCID="1043" which means dutch). The Menu element holds MenuItem elements. I added a MenuItem element With a dutch Title and a JavaScript attribute that has the value 'ShowModalWindow()'. When we import the isv.config.xml and publish the customization changes, we see the menuitem is added.

CREATE JAVASCRIPT FUNCTION THAT SHOWS MODAL WINDOW 
In listing 1 on line 19 we saw that the external javascript file 'customscript.js' is loaded in the OnLoad function of the details form. 'customscript.js' is a custom  javascript file that holds the 'ShowModalWindow()' function.
As I mentiod earlier we had to do 2 more things, the first was the customization of the isv.config.xml and the second is to create this function: 

   1:  var customPagesVirtualDir = 'Custompages';
   2:   
   3:  function ShowModalWindow()
   4:  {
   5:      var contactId = crmForm.ObjectId;
   6:      var popUpUrl = '/' + customPagesVirtualDir + '/Popup.aspx?contactid=' + contactId;
   7:      $('#jqmContent').html('').attr('src', popUpUrl);
   8:      $('#dialog').jqmShow({modal: true});
   9:  }

Listing 7

Addition:  

I added line 1 after Imane pointed out the variable customPagesVirtualDir was not declared, thanks for telling me!

Listing 7 displays the 'ShowModalWindow()' function, the crmForm.ObjectId is assigned to a variable contactId. A variable popupUrl is filled with the url to a custom aspx page. Line 7 is jQuery syntax and assigns the url to the src attribute of the iframe inside the modal window. Line 8 opens the modal window and assigns true to the modal property, So the behaviour of the dialog will be like a modal dialog.

EXAMPLE

CRM's detail form contact entity
Picture 1: Contact details form

In the menu a button is added:


Picture 2: Menu item

 When a user clicks on the menu-item, the function ShowModalDialog is called in the external js file that is referenced in the OnLoad function of the page where the jQuery plugin loads the modal window (client-side!) and the users sees picture 3:

jQuery and jqModal in action on a contact details form
Picture 3: jQuery and jqModal in action on a contact details form

CONCLUSION
Personally I think this is awsome, jQuery is real easy to use and complex matters seem simple. It is a joy to use jQuery with CRM. This combination opens up a world of possibilities. Ofcourse CRM is a web application and customizable, so you could expect external libraries targeted to web applications to work with it. But for me the experience to open a modal dialog like this on a CRM details window without hacking CRM code...
Well I will stop rambling, you can judge yourself.

Henry Cordes
My thoughts exactly...

Currently rated 3.8 by 16 people

  • Currently 3.75/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5