Showing posts with label ajax. Show all posts
Showing posts with label ajax. Show all posts

Thursday, 19 July 2012

Using SignalR and MVC to create a Facebook style news feed

A thought occurred to me whilst driving home a few days ago, where I considered that there was a buzz around node.js and servers pushing content directly to clients. The connecting thought was that I had no idea how this could be accomplished using the .NET framework.

I decided that I'd be really interested to find out if it would be possible to use .NET to produce a Facebook-style news feed that automatically refreshes as soon as something is pushed to the list. Podio does a really good job of this too. As soon as a user posts an update on a story, the change is immediately pushed to all clients complete with a snazzy "knock-knock" sound effect.

There are a few ways of accomplishing this using standard AJAX. The first is long-polling, which maintains an open connection waiting for the server to send something when it's ready. The second way of doing this is using the Javascript setInterval function to poll the server and see if anything has changed every x number of seconds.

However, I wanted a Websockets-style solution that would manage connections better. I did a bit of reading around the subject, and arrived at SignalR.

The clever bit about SignalR is that it provides all the support for this sort of connection for you. It starts by trying a WebSockets connection, and if your browser (or the webserver for that matter) doesn't support it, it picks a different method of transport until it finds something that fits. For Chrome and IIS 8 it would be WebSockets, and for any Internet Explorer version, it uses Forever Frame. You can read more about that on this StackOverflow answer from one of the authors.

My plan was this. I would produce a simple MVC application with a standard create form with two fields. Name and update type (either news or event). When the MVC submission was complete, the update would appear in all connected browsers, almost like a one-way multicast chat program, but over HTTP instead of a standard socket.

I then wanted a bit of JQuery in place that would push the list down and fade my new update in at the top of the list, because the list is ordered by publish date.

I created a blank MVC project, and created a controller class with skeleton actions for index and create. My view contained references to JQuery, JQuery UI, the SignalR Javascript includes, and the standard rendering of the existing list.

To prevent any confusion of the point of the project, this example doesn't connect to a database. I create a list of existing news and event updates in global.asax.cs, and store them in the HttpContext.Application object.

I then needed to create a Hub class that my client would connect to in order to receive updates. If you want the client to fire methods on the server-side, you can create them in this hub class. In my case however, I'm just pushing content from the server to the client, and this is done later on in my controller class.

My controller class takes the name and type of update, and adds it to the list of updates in the Application state as any MVC application would do. The difference however, is just before returning the view, I call a new function called SendMessage(Update). It looks like this:


The idea of a lot of SignalR examples is to push client events to the server, which then distributes it to all clients. I didn't want client-side code muddying the waters of submission of simple data, because in the future, data may change in a different way.

For example, we may want to create a delegate method server-side that performs some functionality and then pushes updates to the clients, rather than waiting for client input. My example is broadcasting a server-side event to all connected clients. This is all happening in BroadcastUpdate using .NET 4.0 dynamic variables to call client functionality.

The last step was to implement some Javascript that would listen on a connection for a call from the server to a client-side method. When this response is sent over, we get a serialized JSON object containing our update. We then do a bit of nifty JQuery to make it fade into appearance at the top of the list.

The Javascript looks like this:


It's really best to get the source code and see the demo in action to really understand how it all fits together, so I've uploaded the project to a repository on GitHub so that you can get it running yourselves.

I understood much more about how to use SignalR in this video about SignalR.

Tuesday, 28 June 2011

A .NET tab list control for content managed sites

Overview

Most websites these days seem to contain tabbed content; it's a way for more information to be displayed on a page whilst still technically being above the fold, and it's historically been a very good way of organising and pooling content together. However, having a tabbed layout on your website can create a few problems too, such as:
  • How search spiders crawl your content
  • How you URL-rewrite your content so that you can drop users onto a specific page with a specific tab pre-selected for them to look at.
Possible solutions

How tabs are handled from an ASP.NET technical perspective depends largely on the content that you're displaying.

In some cases, tabbed content is minimal in size, and you can get away with creating a very fast user interface by pulling all of the content for each tab in on the preload, and then showing and hiding the containers holding the content.

In this case, if you have five tabs, you could create five hidden divs and show or hide them depending on which tab has been clicked. This approach also has the benefit of allowing search spiders to crawl all of your tabbed information in one hit, as it's held in one page.

In other cases, sites will need several tabs, each containing a LOT of content. In cases like these, it's better to call this information when it's required, ie when a user has clicked on the tab they want to see. In cases like these, it's better to pull the content in using AJAX (be it via JQuery, or via .NET update panels). I'm choosing update panels.

Pre-selecting your tab

A good idea to begin with, is that when you enter a URL such as

/this-page?tab=your-tab-name-here

Your tab should be automatically selected and loaded in. This allows users a method of getting straight into a particular part of your content, and the tabs aren't an obstruction. You can do this by checking the URL you came in on in the page_load event, and popping your active tab in the update panel.

if (!Page.IsPostBack)
{
hidValue.Value
= (0).ToString();
if (Request.Params["Tab"] != null)
{
TabName
= Request.Params["Tab"].ToString();
//ContentPageId is a global integer to hold the ID of my tab
hidValue.Value = ContentPageId.ToString();
}
}
else
{
//on postback set content type to hidden field value
ContentPageId = Int16.Parse(hidValue.Value);
}
Repeater1.DataBind();


We call the databind method for the Repeater1 object because that's what's binding our tabs to our update panel. The repeater will grab the ContentPageId, determine what tab you want to show, and set that as the active tab accordingly.

Binding our clickable tabs

Each tab within the repeater has a href with a link of /?tab=your-tab-name-here, and an onclick attribute that fires a javascript function that updates a hidden input value which will hold the ID that you can use to reference your content. This attribute then returns false so as not to get the href to fire.

So your javascript function might look something like this:

function SetHidValue(value) {
document.getElementById(
"hiddenInput").value = value;
document.getElementById(
"SubmitButton").click();
}


Each anchor tag in the repeater will look something like this:

<a href="/?tab=your-tab-here" onclick="javascript:setHidValue(14)">Tab title</a>


Where the argument passed into setHidValue() is the dynamically populated ID of the tab you're after. The methodology of using tab IDs is required in my case because I'm using a content management solution (It could be EPiServer or Umbraco for example), which will require a page ID to grab the relevant page object when I know which tab has been asked for.

Our click() event in the javascript function fires a submit button that'll trigger a postback within the scope of the update panel. Your page_load should then check to see if you've got a hidden input value, and if so, you know which tab to show. If you don't have one, the second choice should be your tab= querystring parameter.

What this means, is if you don't have javascript (or you're a search spider and you want to crawl individual tabs without the help of javascript), you can use the querystring URLs instead.

This post is going to end up being quite complicated, so if you have any suggestions or questions, please pop them in a comment, and I'll try to address them.

Thursday, 6 September 2007

The XMLHttpRequest object

This is AJAX related, and likely to cause problems if you're doing what I'm doing.

I use the object to write out some HTML into a div of a page.

The HTML I'm writing is from an ASP file, and so the syntax for the (pre-declared object) looks like this:

url = "searchDBaseAjax.asp?" + savedUrl;
url=url+"&sid="+ Math.random();

xmlHttp.onreadystatechange=caravanSearchStateChanged;
xmlHttp.open("GET",url,true);
xmlHttp.send(null);


Now if youre searchDBaseAjax.asp file contains some javascript that you want to write to that div, and you want it to happen straight away, you'll have problems.

Say you put a hidden input field in your ASP file.

Say that you then want to test this within the function that you write your ASP file from (just below the code mentioned above).

if (document.getElementById('hideHeaders').value == '1') {
document.getElementById('resultsHeader').className = ' none';
}
else {
document.getElementById('resultsHeader').className = 'floatleft paddingtop15';
}

Because this is an asynchronous call, it's likely that the processing of the AJAX method will not be completed by the time that your javascript underneath executes, and therefore your input field will not be tested properly.

One solution was to make this method synchronous. (Change the true in the open method to false). Trouble is, that doesn't work.

So to fix it, I used the xmlHttp.onreadystatechange attribute to specify the method caravanSearchStateChanged which is mentioned in the first code-snippet.

That method now looks like this:

function caravanSearchStateChanged()
{
if (xmlHttp.readyState==4)
{
document.getElementById("ajaxSearchResults").innerHTML=xmlHttp.responseText;

if (document.getElementById('hideHeaders').value == '1') {
document.getElementById('resultsHeader').className = ' none';
}
else {
document.getElementById('resultsHeader').className = 'floatleft paddingtop15';
}
}
}

So basically, the ready state for our open method is 4 and I've checked that this is the case before I fire off my new javascript that tests the input field.

Days of messing about, this took.

For more information, check out the MSDN entry.