Wednesday, March 30, 2011

Aligning misaligned validation message to component when the component line-wraps elements

Say your jQuery control emits table tags, and as some of you may already know, table always forced an implicit line-wrap (or line-break), so if you want to keep the table and another element on same line, the work-around is to put the table-emitting component inside td tag and the other elements(e.g. unobtrusive validation) on another td tags

An example with misaligned validation message:







To amend that, we could do this:

<div class="editor-label">
  @Html.LabelFor(model => model.CountryId)
 </div>
 <div class="editor-field" >                                                
  
  <table>
  <td>
  @Html.AjaxComboBoxFor(x => x.CountryId, null, "/Home/CountryLookup", "/Home/CountryCaption")
  </td>
  <td>
  @Html.ValidationMessageFor(x => x.CountryId) 
  </td>
  </table>
 
 
</div> 


Using that, the unobtrusive validation message's alignment will be rendered properly









But that approach will eventually violate DRY principle, so we need to amend our approach. We will use templated razor delegate

@{
    Func<dynamic, object> align =
        @<table>
            <tr>
            @foreach (var s in item)
            {
                <td>@s</td>
            }
            </tr>
            </table>;
}

To use:

<div class="editor-label">
 @Html.LabelFor(model => model.CountryId)
</div>
<div class="editor-field" >                                                            
 @align( new[]{ 
   Html.AjaxComboBoxFor(x => x.CountryId, null, "/Home/CountryLookup", "/Home/CountryCaption"),
   Html.ValidationMessageFor(x => x.CountryId) 
   })                    
</div>

Thursday, March 24, 2011

ASP.NET MVC 3's Razor tag nuances

Guidelines obtained from http://haacked.com/archive/2011/01/06/razor-syntax-quick-reference.aspx

@{
    ViewBag.Title = "Index";
}

<h2>Index</h2>

@for (int i = 0; i < 10; ++i)
{
    <input type="button" id="hello@(i)" value="button@(i)" />
}


<script>

$(function() {

    @for (int i = 0; i < 10; ++i)
    {
        <text>
        $("#hello@(i)").click(function() {
            alert("@i");
        });
        </text>
    }
});

</script>



Output:

<!DOCTYPE html>
<html>
<head>
    <title>Index</title>
    <link href="/Content/Site.css" rel="stylesheet" type="text/css" />
    <script src="/Scripts/jquery-1.4.4.min.js" type="text/javascript"></script>
</head>

<body>
    
<h2>Index</h2>

    <input type="button" id="hello0" value="button0" />
    <input type="button" id="hello1" value="button1" />
    <input type="button" id="hello2" value="button2" />
    <input type="button" id="hello3" value="button3" />
    <input type="button" id="hello4" value="button4" />
    <input type="button" id="hello5" value="button5" />
    <input type="button" id="hello6" value="button6" />
    <input type="button" id="hello7" value="button7" />
    <input type="button" id="hello8" value="button8" />
    <input type="button" id="hello9" value="button9" />


<script>

$(function() {

        
        $("#hello0").click(function() {
            alert("0");
        });
        
        
        $("#hello1").click(function() {
            alert("1");
        });
        
        
        $("#hello2").click(function() {
            alert("2");
        });
        
        
        $("#hello3").click(function() {
            alert("3");
        });
        
        
        $("#hello4").click(function() {
            alert("4");
        });
        
        
        $("#hello5").click(function() {
            alert("5");
        });
        
        
        $("#hello6").click(function() {
            alert("6");
        });
        
        
        $("#hello7").click(function() {
            alert("7");
        });
        
        
        $("#hello8").click(function() {
            alert("8");
        });
        
        
        $("#hello9").click(function() {
            alert("9");
        });
        
});

</script>
</body>
</html>

Monday, March 21, 2011

Proof of concept for expression tree

I'm trying to get the name of strong-typed member:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;

using Extensions;
using Et;

namespace Et
{
    class Program
    {
        static void Main(string[] args)
        {
            var p = new Person();

            Console.WriteLine("Extension method:");
            ((HtmlHelper<Person>)null).TextBoxFor(x => x.Lastname);
            ((HtmlHelper<Person>)null).TextBoxFor(x => x.Age);

            Console.WriteLine("\nNon-extension method:");
            InputFor<Person, string>(x => x.Lastname);
            InputFor<Person, int>(x => x.Age);

            Console.ReadLine();
                   
        }


        public static void InputFor<T, TProp>(Expression<Func<T, TProp>> s)
        {
            Console.WriteLine(s.Body.ToString());
            Console.WriteLine(((MemberExpression)s.Body).Member.Name);
        }

        
    }

    public class Person
    {
        public string Lastname { get; set; }
        public int Age { get; set; }
    }

    public class HtmlHelper<T>
    {
    }

}

namespace Extensions
{
    public static class Helpers
    {
        public static void TextBoxFor<T,TProp>(this HtmlHelper<T> sx, Expression<Func<T, TProp>> s)
        {
            Console.WriteLine(s.Body.ToString());            
            Console.WriteLine(((MemberExpression)s.Body).Member.Name);
        }
    }

}



Output:
Extension method:
x.Lastname
Lastname
x.Age
Age

Non-extension method:
x.Lastname
Lastname
x.Age
Age


I'll use this proof-of-concept to make an HtmlHelper for jQuery AJAX ComboBox tomorrow

[UPDATE: March 24, 2011]

Good thing ASP.NET MVC is opensource, I was able to find the more robust way to generate an ID from strongly-typed model. The function resides in ExpressionHelper class, the function is GetExpressionText, which accepts a LambdaExpression.

So this:

namespace JqueryAjaxComboBoxHelper
{

    public static class InputExtensions
    {
        public static MvcHtmlString ComboBoxFor<TModel, TProperty>
            (this HtmlHelper<TModel> htmlHelper, 
             Expression<Func<TModel, TProperty>> expression)
        {

            // string fieldName = ((MemberExpression)expression.Body).Member.Name;

            string fieldName = ExpressionHelper.GetExpressionText(expression);

            var tagBuilder = new TagBuilder("div");
            tagBuilder.MergeAttribute("id", fieldName);
            tagBuilder.MergeAttribute("class", "ac_area");

            return new MvcHtmlString(tagBuilder.ToString());
        }
    }
}

And this:
Html.AjaxComboBoxFor(model => model.Address.City);

That will generate this:
<div class="ac_area" id="Address.City">


Whereas my proof-of-concept code (string fieldName = ((MemberExpression)expression.Body).Member.Name;) can only obtain City:

<div class="ac_area" id="City">

Wednesday, March 16, 2011

ASP.NET MVC 3's Remote attribute for Model is plain cool! B-)

On your HomeController:

[HttpPost]
public JsonResult CheckIfUnique(string Username)
{
 return Users.Any(name => name.ToUpper() == Username.ToUpper()) ?
                // any non-true value is considered an invalid input
  Json(Username + " is already taken. Try another user name")
  :
  Json(true);
}


IList<string> Users
{
 get
 {               
  return new string[]
  {
    // common names
   "Michael", "John", "Jacob"
  };
 }
}


On your model:

[Required] 
[Remote("CheckIfUnique","Home", HttpMethod="post")] 
public string Username { get; set; }

That will call the CheckIfUnique method of HomeController automagically while you are entering an input. Gone are the days for you to manually code a javascript that invokes the controller/method and check if your input has a duplicate. It's all in the Model Luke! May the force(validations) be with you!

Overlooked property getter on custom Model Binder of ASP.NET MVC will make your property unavailable on BindProperty method

If your custom property binder is not invoked on BindProperty method, chances are that you forgot to include a getter for your property. Though in my code's case, I remove the getter in purpose, so the consuming programmer cannot accidentally use the raw submitted values.

This code ...

[BindingName("show_field")]
public string ShowFieldRaw 
{ 
   set 
   { 
       _showFieldRaw = value; 
       ShowFields = _showFieldRaw.Split(','); 
   }  
}


...won't get invoked on BindProperty:

public class PropertyBinder : DefaultModelBinder
{

 protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor)
 {
  base.BindProperty(controllerContext, bindingContext, propertyDescriptor);

  /* if (propertyDescriptor.Name == "ShowFieldRaw")
   throw new Exception(controllerContext.HttpContext.Request["show_field"]); */
  
  foreach (var p in propertyDescriptor.Attributes)
  {
   if (p.GetType() == typeof(BindingNameAttribute))
   {
    var b = p as BindingNameAttribute;
    object value = controllerContext.HttpContext.Request[b.Name];
    

    if (propertyDescriptor.PropertyType == typeof(int))
     propertyDescriptor.SetValue(bindingContext.Model, Convert.ToInt32(value));
    else
     propertyDescriptor.SetValue(bindingContext.Model, value);

    break;
   }
  }//foreach
  
 }
}// class PropertyBinder

So you must modify your model and put a getter on the property to make that property be included on BindProperty method:

[BindingName("show_field")]
public string ShowFieldRaw 
{ 
    set 
    { 
        _showFieldRaw = value; ShowFields = _showFieldRaw.Split(','); 
    } 
    get 
    { 
        return _showFieldRaw; 
    }  
}


Related to error on jQuery AJAX Combobox I debugged: http://code.google.com/p/asp-net-mvc-backed-jquery-ajax-combobox/downloads/list

Tuesday, March 8, 2011

Sqlite error of "Unable to find the requested .Net Framework Data Provider."

If you encountered this error...


Unable to find the requested .Net Framework Data Provider. It may not be installed.


...there are two ways around that error, the easiest way is to just install the .NET Framework Data Provider for Sqlite

Or if your system/network administrator won't allow you to install additional software, download the libraries only, after extracting it, add System.Data.SQLite.DLL and System.Data.SQLite.Linq.dll to your project's reference, then add this config file in your web.config (just after the <configuration> tag):

<system.data>
 <DbProviderFactories>
   <remove invariant="System.Data.SQLite"/>
   <add name="SQLite Data Provider" invariant="System.Data.SQLite"
     description=".Net Framework Data Provider for SQLite"
     type="System.Data.SQLite.SQLiteFactory, System.Data.SQLite, Version=1.0.66.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139" />
 </DbProviderFactories>
</system.data>


Rationale found here: http://sqlite.phxsoftware.com/forums/t/239.aspx

The default MS providers have their XML entries in the machine.config file in Windows\Microsoft.Net\Framework\v2.0.50727\config

It was decided early on that this was not a good place to put additional providers. It would've put a burden on anyone wanting to use DbProviderFactories on a client installation, and messing around with the machine.config is generally not a good way to go anyway.

Solution found here: http://stackoverflow.com/questions/4901198/how-to-make-entity-framework-ctp5-work-with-sqlite

Sqlite Error on ProviderIncompatibleException was unhandled by user code

If you encountered this error:

A null was returned after calling the 'GetService' method on a store provider instance of type 'System.Data.SQLite.SQLiteFactory'. The store provider might not be functioning correctly.

Just add the System.Data.SQLite.dll to your project reference

Get it here: http://sourceforge.net/projects/sqlite-dotnet2/files/SQLite%20for%20ADO.NET%202.0/1.0.66.0/SQLite-1.0.66.0-binaries.zip/download

Using Flexigrid for ASP.NET MVC


In this post is a working code how to show tabular data on grid via Flexigrid and ASP.NET MVC's controller


The code for Model:



using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.Entity;

namespace FlexigridUsingMvc2.Models
{
    public class Customer
    {
        public string CustomerID { get; set; }
        public string CompanyName { get; set; }
        public string ContactName { get; set; }
        public string ContactTitle { get; set; }
    }




    public class EntContext : DbContext
    {
        public DbSet<Customer> Customers { get; set; }
    }
}

This is the code for Controller:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using FlexigridUsingMvc2.Models;

using FlexigridUsingMvc2.Extensions;

namespace FlexigridUsingMvc2.Controllers
{
  public class HomeController : Controller
  {

    [HttpPost]
    public JsonResult List(int page, int rp, string qtype, 
                           string letter_pressed, string query)
    {

      using (var db = new EntContext())
      {
        if (letter_pressed == "ALL" ) letter_pressed = "";

        var CustomerFiltered =
          db.Customers
          .Where(x =>                 
              (
                (
                  !string.IsNullOrEmpty(letter_pressed)
                  && x.CompanyName.StartsWith(letter_pressed)
                )
                ||
                string.IsNullOrEmpty(letter_pressed)
              )
              &&
              (
                (
                  !string.IsNullOrEmpty(query)
                  && 
                  (
                    (qtype == "CompanyName" && x.CompanyName.Contains(query))
                    ||
                    (qtype == "ContactName" && x.ContactName.Contains(query))
                    ||
                    (qtype == "ContactTitle" && x.ContactTitle.Contains(query))
                  )
                )
                ||
                string.IsNullOrEmpty(query)
              )
            );
        

        return Json(
          new
          {
            page = page,
            total = CustomerFiltered.Count(),
            rows =
              CustomerFiltered              
              .OrderBy(x => x.CompanyName)
              .LimitAndOffset(pageSize: rp, pageOffset: page)
              .ToList()
              .Select(x =>
                new
                {
                  id = x.CustomerID,
                  cell = new string[] { x.CompanyName, x.ContactName, x.ContactTitle }
                })              
          });
        
        
      }//using
    }//List

    public ActionResult Index()
    {
      return View();
    }


  }
}

Point of interest in Controller's code is line number 68. Those have to match the colModel of Flexigrid, names doesn't need to match, only their respective positions in flexigrid's colModels

The code for View:
<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<dynamic>" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">


    <link href="<%: Url.Content("~/Content/Site.css")%>" rel="stylesheet" type="text/css" />
    <script src="<%: Url.Content("~/Scripts/jquery-1.4.1.min.js")%>" type="text/javascript"></script>
    <script src="<%: Url.Content("/Scripts/flexigrid/flexigrid.pack.js")%>" type="text/javascript"></script>
    <link href="<%: Url.Content("~/Scripts/flexigrid/css/flexigrid.css")%>" rel="stylesheet"
        type="text/css" />
    

    <script type="text/javascript">
        $(document).ready(function () {
        
        
                


            
            $("#fff").flexigrid
            (
            {
                url: '/Home/List/',
                dataType: 'json',
                colModel: [                
                { display: 'Company', name: 'CompanyName', width: 200, sortable: false, align: 'left' },
                { display: 'Contact', name: 'ContactName', width: 280, sortable: false, align: 'left' },
                { display: 'Contact Title', name: 'ContactTitle', width: 220, sortable: false, align: 'left' }
                ],
                buttons: [
                { name: 'Add', bclass: 'add', onpress: add },
                { name: 'Edit', bclass: 'edit', onpress: edit },
                { name: 'Delete', bclass: 'delete', onpress: del },                                                
                { separator: true },
                { name: 'A', onpress: sortAlpha },
                { name: 'B', onpress: sortAlpha },
                { name: 'C', onpress: sortAlpha },
                { name: 'D', onpress: sortAlpha },
                { name: 'E', onpress: sortAlpha },
                { name: 'F', onpress: sortAlpha },
                { name: 'G', onpress: sortAlpha },
                { name: 'H', onpress: sortAlpha },
                { name: 'I', onpress: sortAlpha },
                { name: 'J', onpress: sortAlpha },
                { name: 'K', onpress: sortAlpha },
                { name: 'L', onpress: sortAlpha },
                { name: 'M', onpress: sortAlpha },
                { name: 'N', onpress: sortAlpha },
                { name: 'O', onpress: sortAlpha },
                { name: 'P', onpress: sortAlpha },
                { name: 'Q', onpress: sortAlpha },
                { name: 'R', onpress: sortAlpha },
                { name: 'S', onpress: sortAlpha },
                { name: 'T', onpress: sortAlpha },
                { name: 'U', onpress: sortAlpha },
                { name: 'V', onpress: sortAlpha },
                { name: 'W', onpress: sortAlpha },
                { name: 'X', onpress: sortAlpha },
                { name: 'Y', onpress: sortAlpha },
                { name: 'Z', onpress: sortAlpha },
                { name: 'ALL', onpress: sortAlpha }
                ],
                searchitems: [
                { display: 'Company', name: 'CompanyName', isdefault: true },
                { display: 'Contact', name: 'ContactName' },
                { display: 'Contact Title', name: 'ContactTitle' }
                ],
                singleSelect: true,
                sortname: "CompanyName",
                sortorder: "asc",
                usepager: true,
                title: 'Companies',
                useRp: true,
                rp: 10,
                showTableToggleBtn: false,
                width: 800,
                height: 255
            }
            );
            

            


        });
        function sortAlpha(com) {
            jQuery('#fff').flexOptions({ newp: 1, params: [{ name: 'letter_pressed', value: com }, { name: 'qtype', value: $('select[name=qtype]').val()}] });
            jQuery("#fff").flexReload();
        }



        function edit(com, grid) {

            // alert(com); // Edit
            
            if ( $('.trSelected',grid).length == 1 ) 
            {
                var items = $('.trSelected',grid);
                alert(com + ' ' + items[0].id.substr(3));
            }
        }


        function add(com, grid) {

            alert(com); 

            /*if ($('.trSelected', grid).length == 1) {
                var items = $('.trSelected', grid);
                alert(items[0].id.substr(3));
            }*/
        }


        function del(com, grid) {

            // alert(com); // Delete

            if ($('.trSelected', grid).length == 1) {
                var items = $('.trSelected', grid);
                alert(com + ' ' + items[0].id.substr(3));
            }
        }
        
    </script>


    <title>Index</title>



</head>
<body>
    <div>
        <table id="fff" style="display:none"></table>
    </div>
</body>




</html>


Sample code: http://code.google.com/p/using-flexigrid-on-aspnet-mvc/downloads/list

Just change the Web.config's connectionString's Data Source

Monday, March 7, 2011

Short-circuiting logic. Which one would you rather maintain?

Which one would you rather maintain?

Explicit logic?
(
    !string.IsNullOrEmpty(letter_pressed)
    && x.CompanyName.StartsWith(letter_pressed)
)
||
string.IsNullOrEmpty(letter_pressed)

Implicit logic?
string.IsNullOrEmpty(letter_pressed)
||
x.CompanyName.StartsWith(letter_pressed)

Does lesser code always trumps the longer code?

Saturday, March 5, 2011

Passing anonymous type to ASP.NET MVC's View

We could take two approaches for passing anonymous type to View. One is to use a ViewModel, another one is to use dynamic(albeit you will lose intellisense and type-safety)

Approach #1 Using a ViewModel:

If your view is expecting a model of Person type (e.g. @model IEnumerable<TestEf.Models.Person>) and you pass this to your View:

var r = (from p in db.Persons
   join q in db.Qualifieds on p.PersonId equals q.PersonId into pQ
   from l in pQ.DefaultIfEmpty()
   orderby p.Lastname
   select new { Person = p, IsQualified = l != null })
  .Skip(0).Take(10) // do the paging logic here

return View(r);

That will result to an error. The best way to handle anonymous type while maintaining strongly-typed Model is to introduce a ViewModel, so in our example here, we need to create this class on Models:

public class QualifiedStatus
{
    public Person Person { get; set; }
    public bool IsQualified { get; set; }
}


This will be the code for our controller:

var r = (from p in db.Persons
   join q in db.Qualifieds on p.PersonId equals q.PersonId into pQ
   from l in pQ.DefaultIfEmpty()
   orderby p.Lastname
   select new QualifiedStatus { Person = p, IsQualified = l != null })
  .Skip(0).Take(10) // do the paging logic here

return View(r);


And this will be the code in our View's header:

@model IEnumerable<TestEf.Models.QualifiedStatus>

Approach #2:

If we don't want to introduce ViewModels for Linq operation that results to anonymous type, we must devise a way to persist the anonymous type in a dynamic(C# 4 feature)-friendly structure. In this regard, we could use Json, as it is easy to parse-back so-to-speak(in fact, there's no need for us to parse back the string, there are many Javascript serializer/deserializer libraries that can do the heavylifting for us). And fortunately someone already written a Json deserializer that leverages C# 4's dynamic feature.


So given this:

var r = (from p in db.Persons
   join q in db.Qualifieds on p.PersonId equals q.PersonId into pQ
   from l in pQ.DefaultIfEmpty()
   orderby p.Lastname
   select new { Person = p, IsQualified = l != null })
  .Skip(0).Take(10) // do the paging logic here


We must be able to pass it directly to MVC's view, to make our API simple, we could make an extension method that allows serializing an object to Json string:

return View((object)r.JsSerialize());

By the way, we cannot pass the string result of JsSerialize directly to View, otherwise the View string overload function will be the one being invoked in our code, and that string is a url, it will be an error to pass Json as url :-) So in our code, we must cast string to object

On the View side of things, we put this on View's header(actually this is optional, dynamic is the default):
@model dynamic

And put this code:

@{ dynamic r = ((string)Model).JsDeserialize(); }


We could now code our View in the usual way, albeit without the type-safety and without the comfort of intellisense(if lacking this is not enough to scare you, I don't know what could :p bottomline, use strongly-typed model, use ViewModels):

@foreach (var item in r) {
 <tr>
  <td>
   @item.Person.Lastname
  </td>
  <td>
   @item.Person.Firstname
  </td>
  <td>
   @item.Person.FavoriteNumber
  </td>
  <td>
   <input type="checkbox" disabled="disabled" @(item.IsQualified ? "checked" : "") />
  </td>
 </tr>
}


See things in action: http://code.google.com/p/sample-poc-for-passing-anonymous-type-to-aspnet-mvc-view/downloads/list

Just change Web.config's connection string:
connectionString="Data Source=C:\Users\Michael\Documents\Visual Studio 2010\Projects\TestEf\TestEf\Database\Ent.sqlite; Version=3"


If you want to use Sql Server instead, change the connection's providerName to System.Data.SqlClient and the connectionString accordingly, here's the DDL:
create table Person
(
PersonId bigint identity(1,1) not null primary key,
Lastname varchar(50) not null,
Firstname varchar(50) not null,
FavoriteNumber int not null
);

create table Qualified
(
QualifiedId bigint identity(1,1) not null primary key,
PersonId bigint references Person(PersonId) not null
);

insert into Person(Lastname, Firstname, FavoriteNumber) values
('Harrison','Jorg', 7),
('Lennon','Jan', 9),
('McCartney','Pol',8);


insert into Qualified(PersonId) values
(1),(3);

UPDATE April 17, 2011

Updated to Entity Framework 4.1. And also, the code is not dependent on installing Sqlite.NET provider anymore, uses installer-less Sqlite .NET provider, just add the System.Data.SQLite.DLL and System.Data.SQLite.Linq.dll directly; necessary Web.config entry for uninstaller-less Sqlite .NET provider is also added to Web.config
http://code.google.com/p/sample-poc-for-passing-anonymous-type-to-aspnet-mvc-view/downloads/detail?name=TestEf-4.1.zip&can=2&q=

Friday, March 4, 2011

ASP.NET MVC + jQuery: The new whole nine yards for Microsoft app(web) development

What is the easiest way to validate a model without requiring a round-trip to server? The answer folks is to use the venerable jQuery and its most compatible companion, ASP.NET MVC.

An aside, did you know that your inputs are already a model-material just by conforming to certain naming conventions for your control's name? And did you know also that the easiest way to achieve this is by using ASP.NET MVC's Html helpers and not coding the HTML manually? :-) I digress.

Sorry folks, jQuery and ASP.NET is not a match made in heaven, the server controls' id in ASP.NET are mangled to no end for it to be conveniently accessible from jQuery's simplified model-pushing (heheh I made up the term, the actual jQuery function is .serialize() ) mechanism. In short, use ASP.NET MVC, strongly recommended :-)


So here's our model:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;

namespace MvcApplication3.Models
{
    public class Person
    {
        [Required(ErrorMessage="Last Name required")]
        public string Lastname { get; set; }

        [Required(ErrorMessage="First Name required")]
        public string Firstname { get; set; }

        [Required(ErrorMessage="Age Required")]
        [Range(0,120, ErrorMessage="Age must be between 0 and 120")]
        public int Age { get; set; }


        // sourced here: http://www.regular-expressions.info/email.html
        [Required(ErrorMessage="Email required")]
        [RegularExpression(@"[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?",
            ErrorMessage="Not a valid email")]
        public string Email { get; set; }
    
    }
}

And this is our view, coupled with client-side validation awesomeness (pertinent lines: 19, 20, 24, 78, 81, 89):

@model MvcApplication3.Models.Person

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <title>Index</title>
</head>
<body>
    <script src="@Url.Content("~/Scripts/jquery-1.4.4.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>

    @* add these two scripts *@
    <script src="@Url.Content("~/Scripts/MicrosoftAjax.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/MicrosoftMvcValidation.js")" type="text/javascript"></script>


    @* add this too *@
    @{ Html.EnableClientValidation(); }

    @using (Html.BeginForm(null, null, FormMethod.Post, new { id = "See" } ))
    {
        @Html.ValidationSummary(true)
        <fieldset>
            <legend>Person</legend>
    
            <div class="editor-label">
                @Html.LabelFor(model => model.Lastname)
            </div>
            <div class="editor-field">
                @Html.EditorFor(model => model.Lastname)
                @Html.ValidationMessageFor(model => model.Lastname)
            </div>
    
            <div class="editor-label">
                @Html.LabelFor(model => model.Firstname)
            </div>
            <div class="editor-field">
                @Html.EditorFor(model => model.Firstname)
                @Html.ValidationMessageFor(model => model.Firstname)
            </div>
    
            <div class="editor-label">
                @Html.LabelFor(model => model.Age)
            </div>
            <div class="editor-field">
                @Html.EditorFor(model => model.Age)
                @Html.ValidationMessageFor(model => model.Age)
            </div>
    
            <div class="editor-label">
                @Html.LabelFor(model => model.Email)
            </div>
            <div class="editor-field">
                @Html.EditorFor(model => model.Email)
                @Html.ValidationMessageFor(model => model.Email)
            </div>
    
            <p>
                <input type="submit" id="submitter" value="Create" />
            </p>
        </fieldset>
    }
    


    <script type="text/javascript">


        $("#submitter").click(function (event) {

            // this prevent the roundtrip to server            
            event.preventDefault();

            // this prevent ajax-sending if there's still an invalid logic on model
            if (!$("#See").validate().form()) return;

            $.ajax(
                {
                    url: '/Home/Create/',
                    type: 'POST',
                    dataType: 'json',
                    // .serialize is a free beer
                    data: $("#See").serialize(),
                    // whereas if there's no .serialize, you have to send individual inputs manually
                    // i.e.
                    // data: { Lastname : $("#Lastname").val(), Firstname : $("#Firstname").val() },

                    success: function (r) {
                        alert(r.Message);
                    }
                });

        });


        


    </script>
</body>
</html>


And this is the controller (pertinent lines: 24, 33, 41):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MvcApplication3.Models;

namespace MvcApplication3.Controllers
{
    public class HomeController : Controller
    {
        //
        // GET: /Home/

        public ActionResult Index()
        {
            return View();
        }


        public JsonResult Create(Person p)
        {

            if (ModelState.IsValid)
            {
                try
                {
                    // use your ORM of choice here to persist your model 

                    // return feedback to jQuery
                    return Json(new { Message = "Your name is " + p.Lastname + ", " + p.Firstname });
                }
                catch (Exception ex)
                {
                    return Json(new { Message = ex.Message });
                }
                
            }
            else
                // alert the user to tell the developer of an overlooked code
                return Json(new { Message = "Please contact the developer to enable client-side validation" });
        }

    }
}



Get the actual code here: http://code.google.com/p/sample-poc-for-jquery-aspnet-mvc-validation/downloads/list

Happy validating folks! :-)

Wednesday, March 2, 2011

Scrub the last digit

Was working with a colleague this afternoon, we were given an algorithm to translate to code(hahah stating the obvious), our code must check if the customer's account number is valid, the code must do a checksum of the account number and verify it against the last digit.

So one part of the algorithm is, given a number how should we scrub the last digit? If a programmer is well-acclimatized with string approach, he/she will tend to use string slicing(e.g. substring, left, etc) to come up with a solution.

However, you can also use some math approach to tackle this simple problem :-)

using System;

namespace ScrubLastDigit
{
    class Program
    {
        static void Main(string[] args)
        {

            Console.WriteLine(GetNumberStr(1763428));
            Console.WriteLine(GetNumberMath(1763428));


            Console.WriteLine(GetNumberStr(23423476));
            Console.WriteLine(GetNumberMath(23423476));

            Console.ReadLine();
            
        }

        // stringly-typed programming. abhored
        static int GetNumberStr(int n)
        {
            string s = n.ToString();
            return int.Parse( s.Substring(0, s.Length - 1) );
        }

        static int GetNumberMath(int n)
        {
            return n / 10;
        }

    }
}