Tuesday, March 8, 2011

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;
        }

    }
}