Images in this post missing? We recently lost them in a site migration. We're working to restore these as you read this. Should you need an image in an emergency, please contact us at imagehelp@codebetter.com
Atlas Extender Crash Course – Part 2: Page Methods

Welcome in the second post in the series dedicated adventures with Atlas Extenders. This series consist of three parts:

  • Introduction – what Atlas Extender is and how to create our own validation extender,
  • Page methods – how to add AJAX server-side validation,
  • Integration with page – how to integrate extender with ASP.NET page to perform full form validation.

Previous part has introduced us into Atlas Extender where we have had created simple extender with client behaviour. As I mention before, we have to repeat extender declaration in aspx for each validator we want to extend. In fact, there is no problem with this, but we have to add an extra two lines for every validator, only to declare extender itself. The same way control tree grows, we have to use and remember number of id’s and so on. Nothing serious but it is not and elegant solution. In order to make it more friendly and elegant, we will modify our extender in way, which will allow us using only one extender and adding only properties for each validator:

    <cc1:ValidatorExtender ID="ValidatorExtender1" runat="server" ScriptPath="ValidatorExtenderBehavior.js" >

      <cc1:ValidatorProperties ErrorCssClass="error" TargetControlID="TestValidator" />

      <cc1:ValidatorProperties ErrorCssClass="error" TargetControlID="TestValidator2" />

    </cc1:ValidatorExtender>

This task is simpler than it looks like when I was writing the first part. We do not have to iterate through properties or do other weird and difficult task. In fact, there is nothing to do about this, just simple change declaration as shown above.

Let us then jump to the next part and see how things are going with server-side validation. As I mention in the first part, extender will not work with the CustomValidator, mainly because, by default this type of validator have no client script validation we can catch and use. Unfortunately, the CustomValidator is perfect for complex validation such as validation against a database. To keep this feature, and even make it better, we can use a Page Method, which is server-side method executed asynchronously in the background, what makes it perfect to perform server validation. So let us do some coding.

First, we need new properties in the extender: ServiceMethod where server-side method will be stored and UpdatingCssClass for class that will indicate that control is during validation. Let’s then add the following code in ValidatorProperties.cs

    /// <summary>

    /// The method name of the web service of page method to call.

    /// </summary>

    [DefaultValue("")]

    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters", Justification = "Assembly is not localized")]

    public string ServiceMethod

    {

      get

      {

        return GetPropertyStringValue("ServiceMethod");

      }

      set

      {

        SetPropertyStringValue("ServiceMethod", value);

      }

    }

 

    /// <summary>

    /// A CSS style to apply while the validation is in progress

    /// </summary>

    [DefaultValue("")]

    public string UpdatingCssClass

    {

      get

      {

        return GetPropertyStringValue("UpdatingCssClass");

      }

      set

      {

        SetPropertyStringValue("UpdatingCssClass", value);

      }

    }

and corresponding code in ValidatorBehavior.js

  var _updatingCssClass;  //updating css class for fields

  var _serviceMethod;  //web method with validation

 

  this.getDescriptor = function() {

    var td = Validator.ValidatorBehavior.callBaseMethod(this, 'getDescriptor');

    td.addProperty('ErrorCssClass', String); 

    td.addProperty('ServiceMethod', String);

    td.addProperty('UpdatingCssClass', String);

    td.addProperty('IsValid', Boolean);   

    return td;

  }

 

  this.get_ServiceMethod = function() {

      return _serviceMethod;

  }

  this.set_ServiceMethod = function(value) {

      if (_serviceMethod != value) {

          _serviceMethod = value;

          this.raisePropertyChanged('ServiceMethod');

      }

  } 

  this.get_UpdatingCssClass = function() {

    return _updatingCssClass;

  }

  this.set_UpdatingCssClass = function(value) {

    _updatingCssClass = value;

  }

In order to execute Page Method we will have to modify evaluationfunctionMockup function created before, but first let us look how this should work. Because executing of the Page Method can possibly be relatively long, like contacting database or making other difficult tasks, we have to do this asynchronously using Sys.Net.PageMethod.invoke() method from Atlas. This method runs selected server-side method asynchronously and assigns event handlers. Investigation of the Atlas client class library allows finding possible, however I am not entirely sure about last three parameters. I have estimated this syntax based on Sys.Net.WebMethod.invoke, which seems to be similar, but if anyone knows exact syntax, send me info please. This or another way possible syntax could be:

  Sys.Net.PageMethod.invoke = function(

    methodName,        //name of the method to execute

    params,            //parameters

    onMethodComplete,  //function to be executed when method has been completed

    onMethodTimeout,    //function to be executed on timeout

    onMethodError,      //function to be executed on error

    onMethodAborted,    //function to be executed when methos execution has been aborted

    userContext,        //user context

    timeoutInterval,    //timeout

    priority,

    useGetMethod

  )

As I mention, we have to modify evaluationfunctionMockup function to run Page Method. This modification id quite simple:

  this.evaluationfunctionMockup = function(val) {

    var value = "";

    var isValid = val.defaultEvaluationfunction(val); //call original validation function

    if (typeof(this.control.element.controltovalidate) == "string") {

        value = ValidatorGetValue(this.control.element.controltovalidate);

    }

    var controltovalidate = $(this.control.element.controltovalidate);

    //set visual style for assigned field according to validation result

    if (!isValid) {

      controltovalidate.className = _errorCssClass;

      controltovalidate.title = this.control.element.defaultErrorMessage;

    }

    else

    {

      if (_serviceMethod) {

        var args = {Value:value, IsValid:isValid};

        this.validate(val, args);

        isValid = false;

      }

      else

      {   

        controltovalidate.className = controltovalidate.defultClassName;

        controltovalidate.title = "";

      }

    }

    return isValid;  //return validation result for further processing

  }

At very beginning, default client validation is checked. I have assumed that the client side validation have priority over the server. If validator is valid, and the extender’s property ServiceMethod has been set, arguments are prepared and this.validate() function is executed. This function changes the CSS class as defined in the UpdatingCssClass property and calls the Page Method. Because, standard validator adds onChange event validation to the validated control we should disable the control to avoid executing another validation until current one will complete.

  this.validate = function(sender, args) {

    _currentCallID = ++_callID;

    if (_serviceMethod) {

      var controltovalidate = $(this.control.element.controltovalidate);

      controltovalidate.disabled = true;

      controltovalidate.className = _updatingCssClass;

      // Call the helper web service

      Sys.Net.PageMethod.invoke(

        _serviceMethod,

        { 'Value' : args.Value, 'IsValid' : args.IsValid },

        Function.createDelegate(this, this._onMethodComplete),

        Function.createDelegate(this, this._onMethodTimeout),

        Function.createDelegate(this, this._onMethodError),

        Function.createDelegate(this, this._onMethodAborted),

        _currentCallID

      );

    }       

  }

When Page Method execution will complete, _onMethodComplete() function will be called, where the validation is finalised. I’ve assume that validation is successful Page Methods returns an empty string while when validation will not succeed than validation message will be returned. The control is set back enabled, then control’s CSS class is set according to the validation result, and finally validator property isvalid is set to indicate the validation result.

  this._onMethodComplete = function (result, response, userContext) {

    if (userContext != _currentCallID)

      return;

    // Time has passed; make sure the element is still accessible

    if (this.control && this.control.element) {

      var controltovalidate = $(this.control.element.controltovalidate);

      controltovalidate.disabled = false;

      if (result == "")

      {

        this.control.element.isvalid = true;

        controltovalidate.className = controltovalidate.defultClassName;

        controltovalidate.title = "";

      }

      else

      {

        controltovalidate.className = _errorCssClass;

        controltovalidate.title = result;

        this.control.element.isvalid = false;

      }

    }

  }

Functions for other three events that could possibly happen during Page Method execution are quite basic:

  this._onMethodError = function(result, response, userContext) {

      if (userContext != _currentCallID)

        return;

      this.control.element.innerHTML = "WebService call failed: " + response.get_statusCode();       

  }

 

  this._onMethodTimeout = function(request, userContext) {

      if (userContext != _currentCallID)

        return;

      this.control.element.innerHTML = "Webservice call timed out.";

  }

 

  this._onMethodAborted = function(request, userContext) { 

      if (userContext != _currentCallID)

        return;

      this.control.element.innerHTML = "Webservice call aborted.";

  }

Therefore, we have done. In order to test, let us change declaration in the page

      <cc1:ValidatorProperties ErrorCssClass="error" TargetControlID="TestValidator" ServiceMethod="ServerSideValidation" UpdatingCssClass="updating" />

and add validation method to the page code behind.

  [System.Web.Services.WebMethod]

  public string ServerSideValidation(string value, bool isValid)

  {

    System.Threading.Thread.Sleep(2500);

    if (value != "1234")

      return "value from server should be '1234'";

    else

      return "";

  }

 

As you probably have already noticed, our extender can now validate using Page Methods, but form will not care about results of this validation. Application is rather limited do far. In the next part, I will show how to stop form from postback until all validation calls complete.


Posted 10-10-2006 4:11 PM by Jimmy

[Advertisement]

About The CodeBetter.Com Blog Network
CodeBetter.Com FAQ

Our Mission

Advertisers should contact Brendan

Subscribe
Google Reader or Homepage

del.icio.us CodeBetter.com Latest Items
Add to My Yahoo!
Subscribe with Bloglines
Subscribe in NewsGator Online
Subscribe with myFeedster
Add to My AOL
Furl CodeBetter.com Latest Items
Subscribe in Rojo

Member Projects
DimeCasts.Net - Derik Whittaker

Friends of Devlicio.us
Red-Gate Tools For SQL and .NET

NDepend

SlickEdit
 
SmartInspect .NET Logging
NGEDIT: ViEmu and Codekana
LiteAccounting.Com
DevExpress
Fixx
NHibernate Profiler
Unfuddle
Balsamiq Mockups
Scrumy
JetBrains - ReSharper
Umbraco
NServiceBus
RavenDb
Web Sequence Diagrams
Ducksboard<-- NEW Friend!

 



Site Copyright © 2007 CodeBetter.Com
Content Copyright Individual Bloggers

 

Community Server (Commercial Edition)