Posts Tagged ‘JQuery’
As I was developing a CMS system for a client, we had a very unique scenario (well maybe not that unique) where we had custom “parts” which you could add to a page. The “parts” were highly customisable, thus you could have 3 instances of the same “part” on one page. The html and javascript rendered by the part would conflict with each other, because they had the same id’s and registered events.
I needed something similar to the old ASP.NET Script manager and the way that old ASP.NET used the “clientId” instead of the “htmlId” when dealing with html objects. The “clientId” was a generated unique key that prevented duplicated user controls conflicting with one another.
I found quite a few implementations of ScriptManager (http://pietschsoft.com/post/2009/08/13/Simple-ScriptManager-for-ASPNET-MVC.aspx, http://aspmvccombine.codeplex.com/) for MVC, but none of them were solving my unique “htmlId” problem. If you are looking for a ScriptManager for MVC, then the two solutions I provided would be more than enough. If, however, you are in a similar situation as I am with the html controls, please read on…
I decided to combine knowledge obtained from the two ScriptManager implementations, and create my own light-weight solution addressing both problems.
Because I wanted the usage of this to be very simple, I created a fluent interface which I can use in all my parts…
This is what the end result looks like:
<% Html.ScriptManager()
.CreateClientScript(GetUniqueId())
.AddParam("SelectId", GetUniqueId("selectList"))
.AddParam("SourceUrl", Url.Action("Index", "Person"))
.Ready(() => {%>
$.getJSON(params.SourceUrl, function(result) {
$.populate_combobox(result, params.SelectId);
});
<%});%>
<% Html.ScriptManager().AddStaticMethod("populate_combobox", () =>{ %>
jQuery.populate_combobox = function(data, selectId) {
$.each(data, function() {
var option = new Option(this.text, this.value);
var dropdownList = $(selectId)[0];
if ($.browser.msie) {
dropdownList.add(option);
} else {
dropdownList.add(option, null);
}
});
}
<%});%>
<div id="<%=GetUniqueId() %>_part">
<select id="<%=GetUniqueId("selectList") %>"></select>
</div>
My “GetUniqueId()” function returns a combination of the PartId, PageId, but you can replace this with anything you fancy.
The javascript that gets generated by this is:
var ContentPart_AboutPage_82 = {
Init: function(params) {
$.getJSON(params.SourceUrl, function(result) {
$.populate_combobox(result, params.SelectId);
});
}
}
var ContentPart_AboutPage_83 = {
Init: function(params) {
$.getJSON(params.SourceUrl, function(result) {
$.populate_combobox(result, params.SelectId);
});
}
}
var ContentPart_AboutPage_84 = {
Init: function(params) {
$.getJSON(params.SourceUrl, function(result) {
$.populate_combobox(result, params.SelectId);
});
}
}
$(document).ready(function() {
ContentPart_AboutPage_82.Init({
SelectId:'ContentPart_AboutPage_82_selectList',
SourceUrl:'/Person'
});
ContentPart_AboutPage_83.Init({
SelectId:'ContentPart_AboutPage_83_selectList',
SourceUrl:'/Person'
});
ContentPart_AboutPage_84.Init({
SelectId:'ContentPart_AboutPage_84_selectList',
SourceUrl:'/Person'
});
});
jQuery.populate_combobox = function(data, selectId) {
$.each(data, function() {
var option = new Option(this.text, this.value);
var dropdownList = $(selectId)[0];
if ($.browser.msie) {
dropdownList.add(option);
} else {
dropdownList.add(option, null);
}
});
}
Update: I have gone even further and added the concept of “SharedVariables” to the ScriptManager where the “SourceUrl” above and even the Json request is fired only once!
Here is my ScriptManager Class:
public class ScriptManager
{
private readonly HtmlHelper _helper;
public IDictionary<string, ClientScript> ClientScripts
{
get
{
if (_helper.ViewContext.HttpContext.Items["ClientScripts"] == null)
_helper.ViewContext.HttpContext.Items["ClientScripts"] = new Dictionary<string, ClientScript>();
return (IDictionary<string, ClientScript>)_helper.ViewContext.HttpContext.Items["ClientScripts"];
}
}
public IDictionary<string, Action> Methods
{
get
{
if (_helper.ViewContext.HttpContext.Items["ScriptMethods"] == null)
_helper.ViewContext.HttpContext.Items["ScriptMethods"] = new Dictionary<string, Action>();
return (IDictionary<string, Action>) _helper.ViewContext.HttpContext.Items["ScriptMethods"];
}
}
public ScriptManager(HtmlHelper helper)
{
_helper = helper;
}
public ClientScript CreateClientScript(string key)
{
if(!ClientScripts.ContainsKey(key))
ClientScripts.Add(key, new ClientScript());
return ClientScripts[key];
}
public ScriptManager AddStaticMethod(string key, Action javascript)
{
if(!Methods.ContainsKey(key))
Methods.Add(key, javascript);
return this;
}
public void Render()
{
TextWriter writer = _helper.ViewContext.HttpContext.Response.Output;
writer.WriteLine("<script type=\"text/javascript\">");
foreach (var clientScript in ClientScripts.Keys)
{
writer.WriteLine("var " + clientScript + " = {");
writer.WriteLine(" Init: function(params) {");
ClientScripts[clientScript].Value();
writer.WriteLine(" }");
writer.WriteLine("}");
}
writer.WriteLine("$(document).ready(function() {");
foreach(var clientScript in ClientScripts.Keys)
{
var client = ClientScripts[clientScript];
writer.WriteLine(" " + clientScript + ".Init({");
foreach (var param in client.Params)
writer.WriteLine(" {0}:'{1}'{2}", param.Key, param.Value, IsLast(client.Params, param.Key) ? string.Empty : ",");
writer.WriteLine(" });");
}
writer.WriteLine("});");
foreach(var method in Methods.Values)
method();
writer.WriteLine("</script>");
}
private static bool IsLast(IDictionary<string, string> parameters, string key)
{
var keys = parameters.Keys.ToList();
return keys.Count > 0 && keys[keys.Count-1] == key;
}
}
Here is the ClientScript class:
public class ClientScript
{
private readonly IDictionary<string, string> _parameters = new Dictionary<string, string>();
private Action _script;
public IDictionary<string, string> Params
{
get { return _parameters; }
}
public ClientScript AddParam(string key, string value)
{
Params.Add(key, value);
return this;
}
public ClientScript Ready(Action script)
{
_script = script;
return this;
}
public Action Value
{
get
{
return _script;
}
}
}
Here is my extension method:
public static ScriptManager ScriptManager(this HtmlHelper helper)
{
return new ScriptManager(helper);
}
And then you only need to add this at the end of your master page:
<% Html.ScriptManager().Render(); %>

I love Language Integrated Query (LINQ) and have used it for the last couple of years. With LINQ2NHibernate, LINQ2Lucene, LINQ2Amazon and even LINQ2Twitter emerging, it is very clear that LINQ is here to stay.
Enter jLinq from Hugoware…
$.getJSON("/Person/All", function(data) {
var results = jLinq.from(data.users)
.startsWith("first", "a")
.orEndsWith("y")
.orderBy("admin", "age")
.select();
});
You can view the basics of jLinq from this screencast.
We used jLinq to solve a very complex join operation on our JSON object and found it to be a very simple task…
var results = jLinq.from(data.users)
.join(data.locations, "location", "locationId", "id")
.equals("location.state", "texas")
.orderBy("location.city")
.select(function(r) {
return {
fullname:r.first + " " + r.last,
city:r.location.city,
state:r.location.state
};
});
jLinq is also very extensible and creating your own extension methods couldnt be easier…
jLinq.extend({
name:"startsWithLetterP",
type:"query",
count:0,
method:function(q) {
return q.helper.match(q.value, /^p/);
}});
//use the new method
var results = jLinq.from(data.users)
.startsWithLetterP("first")
.select();
So why wait another minute, head off to experiment here right now!
Wouldn’t it be nifty to receive a JSON result object from AJAX and it automagically updates your form fields? Those who said “No” leave now!
I had to find a way to iterate through my JSON properties and, if I named the fields the same as the JSON properties, update them all in one go.
Lets use a simple “Person” model and have a look at the ASP.NET MVC action:
public ActionResult GetPerson(int id)
{
var person = Context.GetPerson(id);
return Json(new
{
success = true,
model = new
{
person.Id,
person.Name,
person.Surname,
person.Age,
}
});
}
As you can see this returns a DTO (data transfer object) as JSON result and is ready to be consumed via AJAX…
$.getJSON("/Person/1", function(data) {
if (data.success) {
$.updateForm(data.model);
} else {
alert("An error occurred");
}
});
All we then need is the magical “$.updateForm” method that contains all the voodoo:
jQuery.updateForm = function(model) {
// we iterate through all the properties
for (var property in model) {
// evaluate the expression to get the value
var propertyValue = eval("model." + property);
// and update the field
$("#" + property).val(propertyValue);
}
};
Currenty this will work fine for textboxes and textareas as this was all the functionality I needed for now. If anyone extends this for “select”, “checkbox”, “radiobutton” etc… please share with us so I can update this post.
“Kaboom!” is one of those things you stumble upon and intrigues you from the start. It probably needs more work (not to mention documentation), but certainly gives you a peak at what I believe could be the next big thing in the ASP.NET MVC world. Having just finished a CMS system where I used a very rich JQuery client, I found this a very probable next step. Already using “jquery.forms” to submit all my forms via ajax POST and using “$.getJSON” for all my GET actions, my views started to get pretty lean. I thought, what if I had a JQuery ViewModel that would handle my UI commands and handle all my communication with my controller? A quick search lead me to “Kaboom!”.
The first thing I worried about was testability. “QUnit” (http://docs.jquery.com/QUnit) seems to be a very powerful testing framework that would probably give me more coverage than what I had before! UnitTesting – Check
So lets look at one of the samples in the codeplex download, specifically the Asp.Net MVC sample…
We start with a “Person” Model:
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
And a “PersonController” class which has a Search action:
public class PersonsController : Controller
{
public JsonResult Search(string searchString)
{
List persons = new List();
persons.Add(new Person { FirstName = "Kozin", LastName = "Osot" });
persons.Add(new Person { FirstName = "Setesyci", LastName = "Rynaugh" });
persons.Add(new Person { FirstName = "Atheck", LastName = "Garash" });
return Json(persons
.Where(p => p.FirstName.ToLower().Contains(searchString.ToLower()) ||
p.LastName.ToLower().Contains(searchString.ToLower())).ToList());
}
}
Then we create a “SearchViewModel” js file and put this in our “ViewModels” folder like so:
var SearchViewModel = {
Initialize: function(args, callback) {
Kaboom.register("Search", SearchViewModel.Search);
SearchViewModel.SearchResults = new Array();
callback();
},
Ready: function() {
SearchViewModel.Search();
},
SearchString: '',
SearchResults: null,
Search: function() {
$.getJSON('/Person/Search',// You can set this as a hidden field on your View with 'Url.Action(..' and simply do $('#searchSource').val()
{ searchString: SearchViewModel.SearchString },
SearchViewModel.PopulateSearchResults);
},
PopulateSearchResults: function(data) {
SearchViewModel.SearchResults = data;
Kaboom.notify(SearchViewModel, "SearchResults");
}
}
And our “Search” view would hook up to our view model like so…
<head runat="server">
<title>Search</title>
<% string version = DateTime.Now.Ticks.ToString(); %>
<script type="text/javascript" src="../../ViewModels/Persons/SearchViewModel.js?id=<%= version %>"></script>
<script type="text/javascript" src="Scripts/jquery-1.3.2.js?id=<%= DateTime.Now.Ticks.ToString() %>"></script>
<script type="text/javascript" src="Scripts/json2.js?id=<%= DateTime.Now.Ticks.ToString() %>"></script>
<script type="text/javascript" src="Scripts/kaboom.js?id=<%= DateTime.Now.Ticks.ToString() %>"></script>
</head>
<body>
Quick start shows how to communicate with an aspMVC controller.....<br />
<input type="hidden" id="viewmodel" viewmodel="SearchViewModel" debug="0" />
<div id="Debug"></div>
Search:<br />
<input type="text" bindto="SearchString" mode="TwoWay" /><br />
<input type="button" value="Search" command="Search" />
<br />
<table bindto="SearchResults">
<thead>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Options</th>
</tr>
</thead>
<tbody>
<tr>
<td>$FirstName</td>
<td>$LastName</td>
<td><a href="#" onclick="alert('$FirstName')">Delete</a></td>
</tr>
</tbody>
</table>
</body>
You can bind all your view actions to commands.
<input type="button" command="Save" value="Bound to Save command via jQuery(element).click()" /><br /><br /> <input type="button" command="Save" trigger="dblclick" value="Bound to Save command via jQuery(element).dblclick()" /><br /><br /> <input type="button" command="Save" trigger="blur" value="Bound to Save command via jQuery(element).blur()" /><br /><br /> <input type="button" command="Save" trigger="customaction" value="Bound to Save command via jQuery(element).customaction()" /><br /><br />
… and it has support for other controls and more complex bindings.
<select bindto="Person.Salutation"
datatextfield="Name"
datavaluefield="Id"
datasourceid="Salutations"
onbind="ProgrammaticallyBindIt" >
<option value="0">[select]</option>
</select>
There are loads of other examples, including binding to Tables, Divs, Spans, Checkboxes etc. You can download the framework here.