Blog Posts Published
Busy busy busy
All work and no say makes a blog a dull read.
All work and no say makes a
All work and no say makes a blog a dull read.
All w ork and no say makes a blog a dull read.
All work and no say makes a blog a dull read.
All work and no say makes a blog a dull read.
All work and no say makes a blog a dull read.
All work and no say makes a blog a dull read.
All work and no say makes a blog a dull read.
All work and no say makes a blog a dull read,
All work and no say makes a blog a dull read.
Updates coming soon...
Published: Wednesday, 02 June 2010 08:21 PM by Steve
Comments: 0 Comments
Ext JS 3.0 Cookbook Book Review
I'm not sure if anyone else agrees but I sometimes find it hard to locate genuinely useful and practical Ext JS samples. Don't get me wrong, the Ext JS API documentation is second to none and provides a superb resource, but sometimes I just want to dig right into an application and see how it's been crafted, what best practices I should be following and how I can create DRY code. And do you know what? I find the Ext JS samples to be a little terse and not so well crafted. I understand it is likely that they are deliberately written this way so as to not bury the point of the sample but it is a time consuming process to digest, merge and/or re-factor the elements and snippets that interest you into a coherent and well written application.
What we need is some kind of reference. a reference that uses these sample elements, morsels and snippets as building blocks. a reference that binds and bakes these building blocks into coherent examples and sample applications.
We need a cookbook. an Ext JS Cookbook!
The Ext JS Cookbook by Jorge Ramon is full of step-by-step recipes to bake up some solid Ext JS code. Early pages in the book tackle some Ext JS and OO JavaScript basics though in no way is Jorge trying to provide a primer for developers. In fact he's simply reviewing the more basic ingredients and methods we'll need in order to better understand the rest of the book. I would have preferred these early pages to be less verbose - these early code samples were overly detailed. I don't need to see the the whole HTML document in the examples. just the important bits, please.
After those early pages, the book soon settles into a nice style that is easy to read. Each recipe is laid out in a way that makes it easy to dip into and quickly find the facts. A typical recipe is comprised of the following sections:
- What You're Going to Achieve.
- How To Do It.
- How It Works.
- There's More.
As the book progresses so does the complexity of the examples and recipes. Jorge takes us through the basics of the Ext JS toolkit and widgets providing some great insight and very very practical and useful sample code. Subsequent examples leverage or reference knowledge from previous chapters so we can quickly see how to combine recipes and techniques to create complex Ext JS applications. Importantly we learn how to engineer these techniques using established design patterns to create truly reusable and flexible code encapsulated as components, custom classes or plug-ins.
The last chapter discusses these design patterns in more detail and is an excellent primer for those interested in extending the Ext JS framework and creating plug-ins, modules and components. Additionally we learn how to enhance our application's performance and our user's experience.
All in all this is a great book and a fantastic resource for any Ext JS developer.
Published: Tuesday, 08 December 2009 09:23 PM by Steve
Comments: 1 Comment
Ext JS 3.0 Cookbook
The good people at Packt Publishing have sent me a complimentary copy of their latest Ext JS book - Ext JS 3.0 Cookbook.
A brief look through indicates that it's very well laid out with clear and concise example code. I'd say it's appropriate for those already experienced in RIA development using Ext JS.
I'm looking forward to publishing a much more detailed review of this book so watch this space.
Published: Thursday, 05 November 2009 08:37 PM by Steve
Comments: 0 Comments
Full Frontal JavaScript Conference, Brighton 20th November 2009
Today was an interesting day.
Bad Times
This morning I learnt that my MacBook Pro disk drive had died a very comprehensive death the very second reboot I performed after installing Snow Leopard. The drive is under warranty but hey, it's a pain. I've popped in an old 60gb drive. not really enough room to dual boot the blighter so I whacked on Windows 7 Pro until my replacement unit shows up. Snow Leopard will have to wait a little while longer.
Good Times
I won a free ticket to the Full Frontal JavaScript Conference being organised by Remy Sharp at Left Logic. Aces! It's going to be a great conference with some very interesting speakers. There's still early bird tickets going so hurry up and get yours.
Published: Tuesday, 01 September 2009 02:15 PM by Steve
Comments: 0 Comments
Using Ext JS Grids with ASP.Net MVC - Basic Example
Following my post "Ext JS JsonStore and Linq to JSON Gotcha" I've had a few people asking for a more complete example of how to use Ext JS Grids and data stores (in particular the JsonStore) with ASP.Net MVC and LINQ to SQL. Since writing that blog post I've learnt a lot more about the ASP.Net MVC framework and the Ext JS framework so I thought it would be helpful to publish a complete working demo. So, here's an example that displays a simple Ext JS Grid populated from the Northwind database Products table. The example shows how to use an Ext JS Grid with a JsonStore that supports server side paging and sorting.
Download this example as a working Visual Studio 2008 MVC (Version 1.0) application.
Requirements
- ASP.Net MVC Version 1.0
- Ext JS version 3.0 (works with version 2.x also) - to keep things simple I'm using standalone Ext JS and ext-all.js library.
- Dynamic LINQ Query Library - to keep things simple I've included the source code in the project under App_Code
- Northwind sample Microsoft SQL Server database
Overview
So take a look at the code. It's very simple and straightforward! Here's what you should see when you run the project.
In brief the Index action method of the Products controller handles a vanilla HTTP GET request for the Products index page (/Products/). The view it returns is an almost empty page that simply exposes an element that we can render the Ext JS GridPanel to.
When the index page loads, the load event creates the Ext JS components needed including the JsonStore. It then renders the grid and loads the data store thus triggering an AJAX request to the same URL (this time as a POST, which is the only method supported by the JsonStore component). A different Index action method in the controller handles the POST and if it's an Ajax request it pulls the products from the database and returns JSON in a format expected by the JsonStore.
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Index(int Skip, int Take, string SortDir, string Sort)
{
if (Request.IsAjaxRequest())
{
var products = productsRepository.GetAllProducts();
var results = (new
{
totalRecords = products.Count(),
products = products.OrderBy(Sort + " " + SortDir).Skip(Skip).Take(Take)
});
return Json(results);
}
else
{
return RedirectToAction("Index");
}
}
Some Points of Interest
Where I define the JsonStore component in the ext-lib.js file you will notice I have re-defined the default paging parameter names to match my own code behind. Also notice I am redefining some JsonReader parameters (totalProperty and root) to match the JSON data returned by the controller action method. These steps aren't strictly necessary for this example but I have included it for reference. Alternatively you could use the default Ext JS parameter names and ensure your c# code matches.
Northwind.ProductStore = function(config) {
Northwind.ProductStore.superclass.constructor.call(this, Ext.apply({
paramNames: {
"start": "Skip",
"limit": "Take",
"sort": "Sort",
"dir": "SortDir"
},
totalProperty: 'totalRecords',
root: 'products',
remoteSort: true,
autoLoad: false,
id: "ProductID",
method: "POST"
}, config));
};
The config parameter of the ProductStore constructor above is used to allow us to pass in additional, or override the existing, configuration parameters of the component.
Also in ext-lib.js file you will notice a column renderer method. This is a helper function called during grid rendering to format the data in certain columns. When the columns are specified in the ProductColumnModel you will see I have configured the Stock column to use the stockRenderer method to render the column data.
The stockRenderer method shows how you can reference the data row currently being rendered by the grid and manipulate the HTML rendered in the grid cell. Here I simply add a style to any products that have a stock level less than the reorder level and that are also discontinued.
The column renderer for the Price column uses a built in formatter to render the data as a currency.
The Discontinued column uses a special Ext JS column type to render the boolean value.
Summary
Well, I hope you have found this interesting and helpful. I'm hoping to expand on some of the concepts here and provide examples of some more advanced capabilities and features I have implemented using the ASP.Net MVC and Ext JS frameworks.
Published: Tuesday, 21 July 2009 04:48 PM by Steve
Comments: 8 Comments
Recent Projects Completed by Iqode
Things have been pretty busy here at Iqode hence the lack of updates to this site. Here's a quick run down of some of the major projects I've been involved in. You can find out a little more by checking out my portfolio.
Nickelodeon
Iqode's work at Nickelodeon was extended a few more months after the Christmas rush to help roll out further updates to both the Nick Jr and Nick sites. Updates included micro-sites for St. Valentine's day, Easter and schools half-term. All big events in the world of children's broadcasting! Also some brand new programmes were launched for spring which also needed supporting micro-sites:
Nick Jr
Nick
If that wasn't enough Nickelodeon are promoting a global celebration of ten years of SpongeBob SquarePants this year. Iqode was responsible for building the UK SpongeBob SquarePants portal featuring games, clips, bio's and downloads. That was a lot of fun.
Please be aware Iqode didn't produce any of the artwork for these sites. However, Iqode worked closely with Nickelodeon designers to develop nice clean interfaces created with love using the finest inorganic ingredients sprinkled with XHTML, CSS and underpinned with some solid, tasty ASP.Net code.
St Andrew's Healthcare
St Andrew's is the UK's largest not-for-profit mental health care charity that specialises in mental health care, learning disabilities and acquired brain injuries.
A consortium of three companies built NENE - St Andrew's core nursing resource allocation application.
Devour Design Limited designed the front-end and managed the project.
Simego Limited developed the Microsoft SQL Server database and wrote some excellent data import tools to provide seamless integration with St Andrew's human resource databases.
Iqode Limited developed the web application heavily utilizing some cool technologies and frameworks: AJAX, Ext JS, jQuery and ASP.Net MVC.
This mission critical application tracks clinical and non-clinical staffing requirements across the entire organisation and allows users to assign temporary or permanent members of staff to those vacancies.
Study Group
Study Group helps over 40,000 international students every year to achieve academic success. The company spans five continents and has major learning centres in Australia and New Zealand, the USA and the United Kingdom.
Study Group have developed a web-based application, called Partners, that allows their global network of agents to enrol students, book courses, accommodation and airport transfers.
Before the Partners soft launch Iqode was asked to improve the user experience by refining the user interface and improving workflows and processes. Additionally I improved cross-browser compatibility and, where necessary, development new or missing functionality.
The project was great fun and gave me the opportunity to work on an application that was beautifully architected and designed by some very talented developers. I know I learnt a lot from this project and I hope I was able to share some of my own expertise with the team in return.
The Partners application is only available to authorised agents and there is no public access.
Matchbox Mobile
Matchbox Mobile provide unique mobile software solutions for the wireless industry. Matchbox asked Iqode to produce a working proof of concept web application to support a prototype mobile device application. The application mashes up Windows Live services with location based services to produce and amazing and unique experience for its users. I'm afraid NDA's prevent me from elaborating further so let me bore you with Iqode's role in this project!
From page mock-ups and wire frames provided by the client Iqode produced an ASP.Net MVC application that was brought to life with a smattering of jQuery Ajaxified magic. Behind the scenes the application connected to Windows Live web services for
As soon as this software comes out of private beta I'll publish more information.
Published: Thursday, 18 June 2009 03:21 PM by Steve
Comments: 0 Comments
Telerik RadAjaxmanager and IE Conditional Comments Gotcha
Recently I was working on a project that extensively used Telerik RadControls for ASP.Net AJAX and we were experiencing some Internet Explorer CSS rendering weirdness across versions 6,7 and 8 whenever an AJAX call was made. My immediate thought was the usual IE float clearing bug, a double margin bug or, let's be honest, one of any number of irritating IE CSS bugs . The strange thing was each IE version exhibited slightly different rendering issues which, after some thought, made me think it was something else.
After some rummaging around with the wonderful Firebug I noticed that all of the style sheets within the IE conditional comments were being attached after each AJAX callback. So the style sheet for IE6 was being attached when viewed in IE7 and IE8 and so on. Hmmm.
It seems that the RadAjaxManager doesn't handle IE conditional comments. After an Ajax request the RadAjaxManager attaches all style sheets referenced within IE conditional comments mark-up and the CSS is applied.
Luckily there are some workarounds which we were able to use. In my case I was able to set the EnablePageHeadUpdate property to false so the head content was not updated for each Ajax callback.
Published: Monday, 01 June 2009 07:42 PM by Steve
Comments: 0 Comments
For the last time. Panels Render Mark-up!
If I had a penny for each time I encounter this kind of mark-up in an ASP.Net Web Form...
<asp:Panel runat="server" ID="PanelFooContainer" Visible="true"> <div id="FooContainer"> Foo bar baz </div> </asp:Panel>
...I'd be a few quid better off. I've also seen the following a few times which makes me feel a little unwell.
<table>
<tr>
<th>Foo</th>
</tr>
<asp:Panel runat="server" ID="PanelFooContainer" Visible="true">
<tr>
<td>bar baz</td>
</tr>
</asp:Panel>
</table>
What's wrong with this? Well, this mark-up renders the following HTML to the browser:
<table>
<tr>
<th>Foo</th>
</tr>
<div id="PanelFooContainer">
<tr>
<td>bar baz</td>
</tr>
</div>
</table>
Which, I am sure you will agree, is a pigs ear. Oh, and it's invalid HTML.
I'm sure most developers guilty of this do not intend to render that additional mark-up - they just want a control which they can show or hide easily from the code. But if all you require is a control that you can work with in your code then simply use a PlaceHolder control that doesn't render any superfluous mark-up.
Your mark-up will be valid, you will feel warm and cosy and your code will probably remain exactly the same.
Published: Thursday, 14 May 2009 08:11 PM by Steve
Comments: 0 Comments
Ext JS JsonStore and Linq to JSON Gotcha
Here's a little gotcha that had be scratching my head for a moment. I was testing the Grid component of the excellent Ext JS framework when I ran into a problem loading the JSON result into the grid.
It seemed simple enough - populate the grid from JSON data returned by a controller action method. When I ran the code the grid wasn't populated: the Ext JSONReader was throwing an error "root is undefined". I checked and double checked everything but could not see what the problem was.
Here's the JavaScript to create the JsonStore for the grid:
var ds = new Ext.data.JsonStore({
url: '/Home/GetData',
totalProperty: 'totalRecords',
root: 'requests',
id: "ReqNo",
fields: ['Division', 'Department'],
listeners: {
loadexception: function(proxy, store, response, e) {
alert(e.message);
}
}
});
Here's the controller method that returns the JSON:
public JsonResult GetData(int? start, int? limit)
{
limit = limit ?? 25;
start = start ?? 0;
string dataPath = HttpContext.Server.MapPath("~/Data/TestData.xml");
XDocument xDoc = XDocument.Load(dataPath);
var results = from requests in xDoc.Descendants("requests")
select new
{
totalRecords = requests.Elements("request").Count(),
requests = (from request in requests.Elements("request")
select new NBWeb.Models.Request
{
Division = request.Element("Division").Value,
Department = request.Element("Department").Value
}).Skip((int)start).Take((int)limit)
};
return this.Json(results);
}
And here's an example of the JSON it emits:
[{
"totalRecords":200,
"requests":[
{"Division":"Division 1","Department":"Department 5",},
{"Division":"Division 2","Department":"Department 4",},
{"Division":"Division 3","Department":"Department 5",},
{"Division":"Division 4","Department":"Department 6",},
]
}]
Eventually, upon examining the JSON for the tenth time, it dawned on me - the entire JSON data was nested in a redundant outer array. So the JsonStore root property was not being found - it was looking for:
obj["requests"]
but would need to look for:
obj[0]["requests"]
So it was the controller returning slightly malformed JSON. It's a bug in my code - I need to return the inner result of the Linq select rather than the entire result. So I need to take the FirstOrDefault record from the results, like so:
public JsonResult GetData(int? start, int? limit)
{
limit = limit ?? 25;
start = start ?? 0;
string dataPath = HttpContext.Server.MapPath("~/Data/TestRequests2500.xml");
XDocument xDoc = XDocument.Load(dataPath);
var results = (from requests in xDoc.Descendants("requests")
select new
{
totalRecords = requests.Elements("request").Count(),
requests = (from request in requests.Elements("request")
select new NBWeb.Models.Request
{
Division = request.Element("Division").Value,
Department = request.Element("Department").Value,
}).Skip((int)start).Take((int)limit)
}).FirstOrDefault();
return this.Json(results);
}
And here's the corrected JSON it emits (note the absence of the outer square brackets):
{
"totalRecords":200,
"requests":[
{"Division":"Division 1","Department":"Department 5",},
{"Division":"Division 2","Department":"Department 4",},
{"Division":"Division 3","Department":"Department 5",},
{"Division":"Division 4","Department":"Department 6",},
]
}
Seems like a dumb mistake to make but it took a bit of debugging to find it - hopefully this post will save someone out there some time.
Published: Friday, 24 October 2008 05:22 PM by Steve
Comments: 5 Comments
Latest IQODE Work Goes Live - Nick Jr
I was recently employed by Nickelodeon UK to implement the refresh of the Nickelodeon Jr website (www.nickjr.co.uk) which, I am please to say, is now alive and kicking.
IQODE were required to keep the majority of the existing content "as is" and to apply the new layout, branding and navigation to the whole site. It was vital to retain the search engine positioning Nick Jr had gained over the years and to improve the maintainability of the site.
Published: Thursday, 16 October 2008 04:47 PM by Steve
Comments: 0 Comments
All Your Chip...
Nice stencil graffiti spotted under the arch that links Bond Street to North Street.
This probably only means something to Brighton residents (and those tourists stupid enough to eat their fish supper on the seafront).
Published: Thursday, 16 October 2008 02:28 PM by Steve
Comments: 0 Comments
Integrating a Blogger Atom Feed into an ASP.Net MVC Application
Part One: Configure your Blogger Settings, Consuming Atom feeds with ASP.Net
Part Two: Configure your MVC Application - Routes, Controllers... ACTION!
Part Three: Create a Tag Cloud (Display all current labels with post counts)
Part Four: Create Archive Links
Part Five: Add Blog Search Capabilities
Part Six: Integrating Comments
Part Seven: Disadvantages to this approach and how to improve things
In this series of posts I'm going to describe how to integrate an atom feed into an ASP.Net MVC application based on my experience integrating my Blogger blog into the www.iqode.com site.
In this example I'm going to publish the blog to my server using the Blogger FTP publishing feature. You could simply consume a remote atom feed and display it in your MVC application but I want to create a much tighter integration. FTP Publishing throws up some interesting challenges when integrating into an MVC application and I'm going to elaborate more on this in my next post.
Configure your Blogger Settings
I'll assume you already have a Blogger blog, if not then it only takes a few minutes to sign up to Blogger and make your first post.
OK, now you're signed into Blogger you need to go to your Blogger Settings tab aand follow these instructions on setting up FTP Publishing.
Now go to your Archiving tab and, for now, set Archiving setting to No Archive. Scroll down and make sure Enable Post Pages is set to "Yes". Save your settings and move on to the Site Feed tab and complete the relevant Site Feed values. You also need to set the Blog Posts Feed drop down to "Full" then save your settings.
Now you have your blog set up and you're publishing your blog files and full feeds to your own server via FTP. Now open your MVC application in Visual Studio.
Create Your Model Object
An examination of the Atom feed xml will reveal the following structure:
<feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/'>
<id>tag:blogger.com,1999:blog-8097474</id>
<updated>2008-09-16T16:53:51.743+01:00</updated>
<title type='text'>IQODE Limited</title>
<subtitle type='html'>ATTRACTIVE, INTUITIVE AND ACCESSIBLE WEBSITES, BUILT WITH WEB STANDARDS</subtitle>
<link rel='alternate' type='text/html' href='http://www.iqode.com/Blog/Browse/'/>
<link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8097474/posts/default'/>
<link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://www.iqode.com/Feeds/atom.xml'/>
<author>
<name>Steve</name>
<uri>http://www.blogger.com/profile/15887521996067704936</uri>
<email>noreply@blogger.com</email>
</author>
<generator version='7.00' uri='http://www.blogger.com'>Blogger</generator>
<openSearch:totalResults>2</openSearch:totalResults>
<openSearch:startIndex>1</openSearch:startIndex>
<openSearch:itemsPerPage>25</openSearch:itemsPerPage>
<entry>
<id>tag:blogger.com,1999:blog-8097474.post-2242729009616470352</id>
<published>2008-08-21T18:09:00.002+01:00</published>
<updated>2008-09-15T14:29:52.307+01:00</updated>
<category scheme='http://www.blogger.com/atom/ns#' term='News'/>
<title type='text'>New IQODE Website Goes Live</title>
<content type='html'><h4>"Sometimes you've just got to let it go and get it out there"</h4><p>A comment from the audience during the Q&amp;A session after <a class="external" href="http://skillswap-brighton.org/2008/07/28/skillswap-goes-pretty/">the latest Brighton Skillswap</a> persuaded me to finally publish the latest IQODE website.</p><p>So without further ado I give you... oh, you're already here.</p></content>
<link rel='alternate' type='text/html' href='http://www.iqode.com/Blog/Browse/2008/08/new-iqode-website-goes-live.html' title='New IQODE Website Goes Live'/>
<link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8097474&postID=2242729009616470352&isPopup=true' title='0 Comments'/>
<link rel='replies' type='application/atom+xml' href='http://www.iqode.com/Feeds/atom.xml' title='Post Comments'/>
<link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8097474/posts/default/2242729009616470352'/>
<link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8097474/posts/default/2242729009616470352'/>
<author>
<name>Steve</name>
<uri>http://www.blogger.com/profile/15887521996067704936</uri>
<email>noreply@blogger.com</email>
</author>
</entry>
</feed>
So to model a blog post I'm going to need the following Model Object so right click your Model folder and add a new class and the following class definition.
public class BlogPost
{
public string Title { get; set; }
public string LinkTitle { get; set; }
public DateTime Published { get; set; }
public string Url { get; set; }
public List<string> Tags { get; set; }
public string Content { get; set; }
public string Author { get; set; }
}
Read the Feed Using Linq to XML
Next, Create a method to read in the feed xml, transform it into model objects and return a generic list of BlogPost objects. Linq is an excellent tool to achieve this in a very few lines of code...
protected IEnumerable<BlogPost> GetPosts()
{
var posts = HttpRuntime.Cache["IQODEFeed"] as IEnumerable<BlogPost>;
if (posts == null)
{
string feedPath = Server.MapPath(IQODEWebLib.Config.Current.BlogFeedURL);
XDocument rssFeed = XDocument.Load(feedPath);
XNamespace atomNS = "http://www.w3.org/2005/Atom";
posts = from item in rssFeed.Root.Descendants(atomNS + "entry")
select new BlogPost
{
Title = item.Element(atomNS + "title").Value,
LinkTitle = GetLinkTitle(item.Element(atomNS + "title").Value),
Published = DateTime.Parse(item.Element(atomNS + "published").Value),
Content = item.Element(atomNS + "content").Value,
Author = item.Element(atomNS + "author").Element(atomNS + "name").Value,
Tags = (from category in item.Elements(atomNS + "category")
orderby category.Value
select category.Attribute("term").Value).ToList()
};
// cache and set dependency on the local feed file
HttpRuntime.Cache.Insert("IQODEFeed", posts, new CacheDependency(feedPath),DateTime.MaxValue, System.Web.Caching.Cache.NoSlidingExpiration);
}
return posts;
}
Two things to note here:
- To read the atom feed I need to combine the atom namespace with the local name when constructing the Linq statement.
- I'm caching this list and setting a cache dependency on the actual feed xml file. This way if the feed file changes the cache item is automatically removed and my code reloads the feed xml.
Next create a new controller for your blog index page and drop in the code blow.
public ActionResult Index()
{
var posts = GetPosts();
ViewData.Model = posts.ToList();
return View();
}
Make sure your View page is strongly typed, for example...
public partial class Posts : System.Web.Mvc.ViewPage<List<BlogPost>>
{
}
...and then you can MVC magically reference the Posts Model in your code
<% foreach (var BlogPost in ViewData.Model) {%>
<h3><strong><%=Html.RouteLink(BlogPost.Title, "Blog", new { id = BlogPost.Published.Year, month = BlogPost.Published.Month, title = BlogPost.LinkTitle }, null)%></strong></h3>
<div class="postBody">
<%=BlogPost.Content %>
</div>
<p class="author"><strong>Published:</strong> <%=BlogPost.Published.ToString("dddd, dd MMMM yyyy hh:mm tt")%> by <%= BlogPost.Author %></p>
<p class="tags"><strong>Tags:</strong>
<% foreach (var tag in BlogPost.Tags)
{
Response.Write(string.Format("<span title='{0}'>{1}
</span>", "See all posts tagged " + tag, Html.ActionLink(tag, tag, "Blog/Tags")));
} %>
</p>
<p class="comments"><strong>Comments:</strong> <a href="<%=BlogPost.CommentsInfo.Link%>" class="external-blog"><%=BlogPost.CommentsInfo.Title %></a></p>
<% } %>
You'll notice some extra cruft in the code above which I will be covering in later posts but I'm sure you get the idea. Stay tuned for Part Two where I'll be covering in more details the controller actions, routing and rendering within the application.
Published: Wednesday, 01 October 2008 09:13 AM by Steve
Comments: 0 Comments
CSS to jQuery ASP.Net HTTPHandler
In this post I'm going to explain why and how to automatically turn css into jQuery code that is executed on page load. The solution is written for ASP.Net but in theory it could be ported to any other framework.
It's nice to add various visual enhancements and tweaks to websites I develop and I do that using the jQuery Javascript library. These enhancments, in my mind, are not necessary but nice to haves. However, it's important to ensure that web pages are still accessible and render well when Javascript is disabled. So, according to the principles of Progressive Enhancement, I layer the effects on top of the "vanilla" page using jQuery. A simple example is my About page. Try turning Javascript on and off then reloading. Without Javascript each section is displayed inline. With Javascript turned on some jQuery voodoo shows and hides the sections with some nice fade transition effects .
Where am I going with this? Well I find myself writing a lot of jQuery in order to apply some CSS to various elements in order to switch them from vanilla mode into singing and dancing mode. Which means I need to write script to run as the page loads to override the vanilla CSS rules.
And?
I thought it would be nice to author these styles in a way I'm familiar with - style sheets. So I wrote a simple ASP.Net HTTP Handler to parse certain CSS files (I chose the .jqcss file extension) and spit out jQuery instead.
For example:
.test {
background-color: red;
padding: 10px;
background-position: top left;
margin: 5px;
}
h2:first {
color: red;
}
h2[id="3"] {
color: blue;
}
#blue {
background-color: blue;
color: #fff;
padding: 5px;
}
Becomes:
$(document).ready(function(){
$('.test').css({backgroundColor:'red',padding:'10px',backgroundPosition:'top left',margin:'5px'});
$('h2:first').css({color:'red'});
$('h2[id="3"]').css({color:'blue'});
$('#blue').css({backgroundColor:'blue',color:'#fff',padding:'5px'});
});
To use the HTTP Handler you need to add a reference to your handler in your web.config httpHandlers section and tell it to handle .jqcss files, as follows:
<httpHandlers>
<add verb="*" path="*.jqcss" type="IQODE.WebLib.Handlers.JQCSSHandler" />
</httpHandlers>
Then create your .jqcss file containing the style rules you want to be applied on load. Finally you need to add a link to your .jqcss file from your page:
<script type="text/javascript" src="test.jqcss"></script> I'm not absolutely convinced there's a huge need for this technique. It simplified the develoment of my site, iqode.com, and keeps my pages tidier - I'm not a massive fan of having a load of inline Javascript cluttering up my pages. More importantly, by placing this script in an external file I can also manage how the file gets cached by my application and the browser - in ASP.Net I can set a cache dependancy on the .jqcss file itself and only refresh the cache if the file changes. This means the handler only needs to parse the file occasionally.
Here's the entire source code for the HTTPHandler - I removed the cache control code etc. for brevity. One point of interest is the use of an arbitrary Web Control (a button in this case) - so I don't need to parse all the style rules for each selector I load the entire rules string into the Web Controls CssStyleCollection object. This object parses it for me and I simply iterate of the key collection pulling out each rule/value pair to build the jQuery statement.
using System;
using System.Collections;
using System.Web;
using System.Text.RegularExpressions;
using System.Text;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace IQODE.WebLib.Handlers
{
public class JQCSSHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
// Get the physical path of the file being processed
string File = context.Request.PhysicalPath;
string css = string.Empty;
StringBuilder sb = new StringBuilder();
Button btn = new Button();
// Open the file, read css
using (System.IO.StreamReader reader = new System.IO.StreamReader(File))
{
css = reader.ReadToEnd();
}
css = RemoveWhitespace(css);
string[] cssRules = css.Split('}');
sb.AppendLine("$(document).ready(function(){");
foreach (string cssRule in cssRules)
{
if (!string.IsNullOrEmpty(cssRule))
{
string selector = cssRule.Split('{').GetValue(0).ToString().Trim();
string rules = cssRule.Split('{').GetValue(1).ToString();
CssStyleCollection sc = btn.Style;
sc.Value = rules;
rules = "";
foreach (string key in btn.Style.Keys)
{
rules += ConvertToCamel(key) + ":'" + btn.Style[key].Replace("'", "\"") + "',";
}
sb.AppendLine(" $('" + selector + "').css({" + rules.TrimEnd(',') + "});");
}
}
sb.AppendLine("});");
context.Response.ContentType = "text/css";
context.Response.ContentType = "text/javascript";
context.Response.Write(sb.ToString());
}
protected string ConvertToCamel(string s)
{
try
{
if (s.IndexOf('-') < 0)
{
return s;
}
string[] words = s.Split('-');
string word1 = words[0];
string word2 = words[1];
string lttr = word2.Substring(0, 1).ToUpper();
return word1 + lttr + word2.Remove(0, 1);
}
catch (Exception)
{
throw new Exception("Error Parsing jQcss file - malformed css attribute found in stylesheet: " + s);
}
}
public bool IsReusable
{
get
{
return false;
}
}
public static string RemoveWhitespace(string str)
{
try
{
if (string.IsNullOrEmpty(str))
{
return str;
}
return new Regex(@"[\t\r\n]").Replace(str, string.Empty);
}
catch (Exception)
{
return str;
}
}
}
}
And that's it... I hope someone finds this useful. You could simply put all our jQuery css stuff in a separate script file but I found it easier and quicker to do it this way.
Drop me a line if you have any questions, spot a bug or have suggestions for improvement.
Published: Thursday, 11 September 2008 05:40 PM by Steve
Comments: 2 Comments
New IQODE Website Goes Live
"Sometimes you've just got to let it go and get it out there"
A comment from the audience during the Q&A session after the latest Brighton Skillswap persuaded me to finally publish the latest IQODE website.
So without further ado I give you... oh, you're already here.
Published: Thursday, 21 August 2008 06:09 PM by Steve
Comments: 0 Comments


