Showing posts with label jqGrid. Show all posts
Showing posts with label jqGrid. Show all posts

Nov 26, 2010

SharePoint 2010 Custom Web Part with jqGrid

The development of custom web parts in SharePoint often brings some questions like – how will I do sorting and searching. How will I support and implement paging, can I have modal pop up with details information presented?
Well, if you need to develop all these features from scratch, you will end up with tons of javascript files and code behind methods for building the complex interface. Most likely you will also have css file with a quite big content. And since the web part is pretty much custom control, debugging what it renders is pain in the butt.
I had the same concerns and decided to implement my web part using jqGrid, which pretty much gives out-of-the-box the sorting, paging and filtering. Plus it is easy configurable in few lines javascript.
Here I am going to explain the steps and twists I did in my implementation.

The solution is not specific to SP2010, so you can implement it easily in SP 2007 too. It demonstrates the usage of jqGrid, with pop ups and hyperlinks in the cells. The content of the pop up is created dynamically on the server side depending on the clicked row.

I am using WSPBuilder for project template and the PoC scenario is: Based on Customers SPList we want to create a web part which presents its data, and adds a modal dialog representing orders history for every customer from the list. We’d like our web part to support – sorting, paging, and filtering, and to have decent look-and-feel.


After creating the structure of the project, the solution should pretty much looks like on the screen shot below:


Following the samples of jqGrid we need to build similar output (as html) in order to get this working.
Check the demo of jqGrid for details at http://www.trirand.com/blog/jqgrid/jqgrid.html.

HTML
...
<table id="list2"></table>
<div id="pager2"></div>

This can be easily achieved in the CreatedChildControls method. By using HtmlTable, HtmlTableRow, HtmlTableCell, HtmlGenericControl the needed output can be generated at server side.

There is a little twist as it comes to the pop ups. The pop ups should be user controls placed in the ControlTemplates folder. Ideally, they should keep some presentation and persistence logic in their code behind. In order to get them smoothly displayed in consistent to jqGrid manner, we need to use jquery dialog function. Also, in order to display data related to particular SPListItem, we need to pass the SPListItem.ID as a parameter of the function which takes care of the visualization.
For rendering the jqGrid next js functions and styles are used:
• jquery-1.4.2.min.js
• jquery-ui-1.8.5.custom.min.js
• /jqGrid/js/i18n/grid.locale-en.js
• /jqGrid/js/jquery.jqGrid.min.js
• SampleWebPart.js
• OrdersHistoryDetail.js

• jquery-ui-1.8.5.custom.css
• jqGrid/css/ui.jqgrid.css

We can inject all of our javascript functions and styles using RegisterStartupScript of ClientScriptManager.

clientScript.RegisterStartupScript(typeof(Page), "jQueryJQWebPart_UI", JQueryGUILibrary);

public string JQueryGUILibrary
{
get
{
return @"<script src='" + _jqueryMainFilePath + "' type='text/javascript'></script>" +
@"<script src='" + _jqueryUIFilePath + "' type='text/javascript'></script>" +
@"<script src='" + _jqueryGridLocalePath + "' type='text/javascript'></script>" +
@"<script src='" + _jqueryGridPath + "' type='text/javascript'></script>" +
@"<script src='" + _jqGridWebPartFilePath + "' type='text/javascript'></script>" +
@"<script src='" + _jqGridWebPartOrdersHistoryFilePath + "' type='text/javascript'></script>" +
@"<link href='" + _jqueryUICss + @"' rel=""stylesheet"" type=""text/css"" />" +
@"<link href='" + _qGridCss + @"' rel=""stylesheet"" type=""text/css"" />";

}
}
Since our pop ups are ASCX controls, we need to load them and add them to the rendered content “hidden”. Once the user calls the pop up visualization, we can display them and load their content from SP content database.
Now it comes the tricky part. We need not only a way to display the ascx control by passing its id to the dialog() function. We need also to secure a mechanism for displaying its content dynamically on the server side, depending on which row from the grid is chosen.

Bear in mind that control’s client id is generated on the server side by the time control is added to the web part output. And on the client side it will be something like: ctl100_....We’d like to avoid this, so in this sample I inject the generated client ids in the rendered content.

Next code injects the function that visualizes the user control in the host aspx of the web part.
public string PopUpJsFunctions
{
get
{

return @"<script type=""text/javascript"">
//<![CDATA[
function displayUserControl(contentDivId, selectedListItemID)
{
CallServer(selectedListItemID);
$('#' + contentDivId).dialog();

}
//]]>
</script>";
}
}
Now, the question is how to get the specific content of the user control and to display it in the user control dialog prior to its visualization. Here comes in help the ICallbackEventHandler, which implementation provides asynchronous post backs to the server by sending only user defined information, rather the all data passed on “full” post back. In our case we will pass the SPListItem.ID, which we keep in the grid’s store, and will return the specific content of the details pop up. This content fully depends on the SPListItem in the correspondent SPList and will be generated on the server side. It looks exactly like what we are looking for. So, let’s make our web part implements the ICallbackEventHandler interface.

public class SampleJQueryWebPart : WebPart, ICallbackEventHandler

public string GetCallbackResult()
{
return DynamicOrdersHistoryForm(selectedListItemID);
}

public void RaiseCallbackEvent(string eventArgument)
{
selectedListItemID = Convert.ToInt32(eventArgument);
}

The dynamically generated html is very simple, and it only aims to demonstrate the PoC.

On the OnLoad in our custom web part we can inject the client side functions with its callback.

//dynamic callback on clicking (...) in Orders history column for each row in the jquery grid
String cbReference = clientScript.GetCallbackEventReference(this, "arg", "ReceiveServerData", "");
String callbackScript = "function CallServer(arg, context) {" + cbReference + "; }";
clientScript.RegisterClientScriptBlock(this.GetType(), "CallServer", callbackScript, true);

That is!

Another tricky moment is the way we feed the jqGrid with json formatted result. Since it is ASPX web forms alike, the most convenient and reasonable way to do so is by using generic handlers.
In order to make the generic handler working in our case we need to do next:
Create a new ashx file in your Layouts folder (in our case in specific folder within Layouts)
Remove its cs file and place it in the Code folder of the solution.
Go to the markup and do next change:

<%@ WebHandler Language="C#" CodeBehind="GetSampleGridContent.ashx.cs" Class="CustomWebPart.SharePointRoot.TEMPLATE.LAYOUTS.JQWebPart.GetSampleGridContent" %>
Becomes:
<%@ WebHandler Language="C#" Class="CustomWebPart.Code.GenericHandlers.GetSampleGridContent, CustomWebPart, Version=1.0.0.0, Culture=neutral, PublicKeyToken=59c2732ac8e0deaf"" %>

In order go get paging working properly we need a wrapper object which will present the data in expected by the jqGrid format:
public class CustomersData
{
public int Total { get; set; }
public int Page { get; set; }
public int Records { get; set; }
public List Rows { get; set; }
}

CustomerEntity class should have the very same properties as those enumerated in colModel of the grid.
I won’t go into details of the implementation. You can download the code of this article and review it.

I have one more class called CustomersGUItHelper, which provides search, filter functionality and instantiates the CustomersData object which is serialized in json format and returned to the GUI by our generic handler.
MemoryStream stream = new MemoryStream();

DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(CustomersData));

ser.WriteObject(stream, jsonData);
stream.Position = 0;

StreamReader sr = new StreamReader(stream);

var json = sr.ReadToEnd();

context.Response.Write(json);
context.Response.End();

On the client side we have major 2 javascripts only:
• SampleWebPart.js
This file is contains the definition of the jqGrid.
• OrdersHistoryDetail.js
For the PoC I have created this files contains only the callback function on the client site. Ideally it should contain also the validation functions, and the save functions (if we assume they are implemented with ajax post).
function ReceiveServerData(result, context) {
var divContent = $("#" + varDetailsControlContainerId);
divContent.html(result);

}
Here are the steps for installing and uninstalling our solution:
Install steps:
1)Add-SPSolution

C:\Users\kbochevski\Desktop\jqGridSPWebPart\CustomWebPart\CustomWebPart\CustomWebPart.wsp

2)

Install-SPSolution -Identity 6195c1de-8e41-4537-a66d-e93b10d22f25 -GACDeployment -Local -WebApplication SPKaloyan -Force

3) Go to http://your_web_app/_layouts/newdwp.aspx, find the web part, mark it and click “Populate the gallery”
4) Create sample page and add the web part in it.

http://your_web_app/SitePages/jqGridSampleWebPart.aspx

Uninstall steps:
1)
Uninstall-SPSolution -Identity 6195c1de-8e41-4537-a66d-e93b10d22f25 -Local -WebApplication SPKaloyan

2)
Remove-SPSolution
6195c1de-8e41-4537-a66d-e93b10d22f25

After deploying the solution and creating a sample page which hosts the custom web part, it should look lie this:


We can review the orders history for every customer in the web part by clicking the (…) hyperlink in the “Orders History” column.


We can filter (search) the customers by Name. This is configurable in SampleWebPart.js. I have implemented this functionality for one column only.


Our custom web part gets its data from the custom list called Customers.


The same concept may be applied to another ajax frameworks like extjs for example. You can now create custom web parts based on ajax controls, which gives you decent look-and-feel and powerful client side functionality.

You can download the code related to this article here.

Read full article!

Mar 5, 2010

MVC.NET and IoC using Spring .NET

What I have developed so far in my previous articles(refer MVC Custom authorization) is just a double layered application – MVC.NET views and controllers and database layer using Linq to Entities, which is referred in the controllers. Hence the maintenance of the source code is pretty awkward. If we need to redesign or just to re-factor a piece of our application we will pretty much need to touch the code everywhere – controllers, DB layer class (we don’t even have such so far), models and views. And the testability is poor too. Well, imagine what it will be in a project with a team of 10 developers. In big projects maintenance is very important thing. And interface-based development is just one part of it, but it gives both better maintainability and better extensibility. It makes the code easier for unit testing; you can easier allocate a potential problem. Vice verse if you decide to save the usage of IoC Container because of the configurations overheads, you will soon find out that as your code and components grow the development and the refactoring tends to become a tedious and prone to errors process. IoC containers are powerful weapon, but used inappropriately they might be overkill for small projects that don’t tend to evolve period. But this is more related to discussing engineering and over engineering, rather than my goal in this post – using IoC containers (Spring.Net) with ASP.NET MVC.
In this post I will review the ASP.NET MVC and Spring .NET as a framework for dependency injection.
We will see how by making our application loosely-coupled we may end up with a sample replacement of an assembly without the need of rebuilding everything and redistributing it to a client.
Let’s review our current code (refer MVC Custom authorization article) and see how we can optimize it and which are the spots we most likely are going to perform changes in.


Firs of all we don’t have dedicated isolated piece of code to handle our business logic. That is a weak side for sure and in every application business requirements change during the project’s life cycle. Also, it will be good if we have a DAL layer which can serve as an abstraction layer regardless what stays behind it – SQL Server, oracle, another kind of database or just xml files for storing our data.

For the sake of the demo I will develop (evolve) the Admin page and will add functionality for amending users. While doing this I will try to demonstrate a solution for mentioned concerns above. The given screenshot below displays the achieved functionality that supports amending users.



Keeping the same development approach (as in samples from my previous articles) raises some questions:
• I don’t have a dedicated business layer and my logic is spread around the controllers. What am I going to do if this turns into very complex application? How will I support my code and how will I be able to do changes? Business process and definitions are the parts of the code that tends to change most frequently during a project’s lifecycle. That is for sure one of the pieces that is prone to changes the most.
• Controllers access my database layer which makes my application dependant on the entity model, since currently I have objects (entities) instances in my controllers. Hence changing it will lead to rebuilding the web ASP.NET MVC project, plus redeveloping (eventually) pieces of code spread along the controllers. It is unlikely to decide somewhere during the development process to change underlying data storage, but it is likely to change the functional statements (queries, usage of stored procedures, etc.) or data entity model structure. And if I have to make such changes the last thing I would “love” to do is recompiling the projects that use as a direct reference my DAL project. There are scenarios in which I will still need to rebuild my ASP.NET MVC project even if I use abstractions, but I think that my point of view is pretty clear.
• Unit testing the application is getting harder and harder
• Despite ASP.NET MVC implies separation of concerns in layers (Models, Views, Controllers), what we have right now (before redeveloping it) is not layered application from architecture point of view.

The current tight coupling makes project's growing more difficult, as development on a project proceeds it is getting harder and harder to make modifications to lower tiers without having an adverse affect on the tiers above it. What I want to improve is decoupling the tiers, obtaining better control over isolated pieces of code and improved separations of concerns.
The schema below shows how things should be (in my perspective) and what I am trying/going to achieve:

The layers should talk each other via contracts definitions, not via certain object implementations. Spring.NET as IoC container will take care of instantiating our objects’ implementations and injecting them in their consumers.

To accomplish our goals in the context of PoC sample we are using, we need to outline next pieces:
1. MVC.NET web project should refer directly (as project reference) only:
• Entities abstract definitions
• DAL repositories, BL services abstract definitions
Note: In the PoC code related to this article which you can download, there is a direct dependency between DataAccess.dll and MVC.NET project (MVCAuthenticationSample). That is only because the startup code of this PoC is the project from my previous article (refer MVC Custom authorization) and I didn’t remove the old code. It makes clearer the difference between using IoC and DI and not using it at all.

2. Business Layer is a set of many business module implementations or a single assembly that defines the business logic. It should directly refer only:
• Entities abstract definitions
• DAL repositories, BL services abstract definitions

3. Data Access Layer implements the repository pattern and it is responsible for accessing the database and persisting data. It must expose only entities definitions to its consumers. DAL should refer directly only:
• DataAccess library which contains the edmx
• Entities abstract definitions

Spring.net will instantiate real object implementations and inject them in their consumers as follows:
• MVC.NET consumes our business services which expose entities’ definitions
• Every business module (part of BL) consumes repositories which provide access to underlying storage (via Linq to Entities in this case)
• Repositories get instance of the data context (in this case)

I will use MvcContrib.Extras which has implementation of controller factories for Spring.Net.
Refer links at the bottom of this article for details.

Here is a list of all required external *dll files used in this PoC sample:
• antlr.runtime.dll
• Common.Logging.dll
• log4net.dll
• MvcContrib.dll
• MvcContrib.Spring.dll
• Spring.Core.dll
• Spring.Data.dll
• Spring.Web.dll


Let’s review what we have in our solution and how does it fit our design goals:


1. The DataAccess project holds the Entity model of the database and exposes DataContext for accessing and manipulating the database.
2. Definitions project contains only interfaces (contract definitions) of the services and repositories.
3. EntitiesDefinitions project contains interfaces which correspond to the entities from the Linq to Entities model.
Note: For the sake of the demo I created the interfaces in this project manually, but you can achieve it by using T4 templates.

4. SpringResources contains object definitions xml files used by Spring.net for DI. In this example I refer object definitions from this assembly. Don’t forget to mark configuration files as Embedded Resource on their build action.

Note: if you want referring the resources as non-qualified from the web.config file, you can include in the MVC.NET project the folder IoCConfig and change the web.config as below:

Also, bear in mind that in this scenario you can't access WebApplicationContext during Application_Start() yet. HttpApplication.Init() is the earliest possible stage for accessing the context. The reason for this is the fact that HttpModules have not been initialized yet on Application_Start and Spring.Context.Support.WebSupportModule responsible for loading resources will fail.So if you want to refer the resources as non-qualified (web protocol in Spring.Net source), you can call ConfigureIoC method in Init method of global.asax rather than in Application_Start().

5. MVCAuthenticationSample is the MVC.NET startup application
6. Admin and AdminNewImplementation are parts of our business layer. AdminNewImplementation is created for demonstrating how we can replace an assembly without the need of rebuilding its consumer application. I will talk more about this later.



Now it is time to configure our object definition resources and to instantiate the WebApplicationContext.



Something that worth stating from my perspective is the way we tell Spring.NET how to instantiate the DBContext when injecting it in our repositories. If we make it application scope, DBContext will be shared among repository instances for all users and this will inevitably lead to getting exceptions on calling SaveChanges() due to multi-user concurrent access to the very same instance of the DBContext.
And the alternatives are either “session” or “request” scope. The key for achieving this is to leverage Spring's WebApplicationContext. This ensures that all features provided by Spring.Web assembly, such as request and session-scoped object definitions are handled properly.





As you can see from the config file, I am using property injections, so in my consumer just need to define property with the appropriate name and type corresponding to those in resource definition file. Below is an example of declaring UserService as a property in my AdminController class.

The last things we have to do are configuring our web.config and to get WebApplicationContext and pass it to the controller factory defined in MvcContrib.Extras.




Before we start playing with the admin module, we need to set the build path of our assemblies which are going to be used by Spring.Net to the bin folder of our MVC.NET application.


We should do the same for next projects: SpringResources, DataAccessLayer, DataAccess

Now, at this point we can finally start using our application.
Let’s see where we stand if we decide to change the implementation of our business logic. In Admin module on adding new user I was automatically attaching role 1, which is “administrator” role. Refer database design in my article ref: “MVC Custom Authorization”. This grants ”out-of-the-box“ access of newly created user to certain functionality. Let’s change this and make our newly created users to have access to none of the modules. A real world scenario might be if we implement a new module for granting access to users in Admin module. Ok, so far so good. We can create new assembly “AdminNewAdministration” and change the business logic of the UsersService class, AddNewUser method. Or we can just modify the current and redeploying it. Let’s try the first approach.


Now we have to change the config file in the resource assembly, stating that new implementation is going to be used.


Once we are done here, we can rebuild our SpringResources project and deploy SpringResources.dll and AdminNewImplementation.dll to our server. Just copy both dll files to the bin folder of the application.

After restarting our IIS changes are taking place and if we add new user we can see that after logging in, he has no rights, which demonstrates that our new business logic implementation is in usage.



You can see that the newly created user has no access:


Article related links:
http://mvccontrib.codeplex.com/releases/view/37422
http://trirand.com/blog/jqgrid/jqgrid.html
http://www.trirand.com/blog/?page_id=6
http://jqueryui.com/themeroller/

I hope you will find this article helpful.

You can refer my new article for designing MVC.NET 3 applications, which also discusses the loosely-coupled approach here.

You can download the article's related code from here:source code


Read full article!