Monday, June 14, 2010

jQuery AJAX with ASP.NET Web Forms & Sitefinity



My team and I are working with Sitefinity to create our new portal. Well, since Sitefinity doesn’t currently support MVC, we’re having to use the “good” ole web forms. Sitefinity is a really nice CMS and allows for full customization and we’re still learning how to handle the custom user controls. So…this is how we’re doing it right now.

Download Source

We wanted to allow our users to add quick links to their home screen, so that’s the sample I’m going to use. First, I created two projects, one called UserControlSample.Core (class library) and the other called UserControlSample.UI (a blank Web project).

I started with the domain model, which looks like this:

public class QuickLink
{
public Guid Id { get; set; }
public string DisplayName { get; set; }
public string URL { get; set; }
public string UserId { get; set; }

public string BuildHyperlink()
{
return string.Format("<a href=\"{0}\" title=\"{1}\">{1}</a>", URL, DisplayName);
}
}

Generally, I wouldn’t put the UserId in this class, but for this example it just made it easier.

After the domain model, I added the repository interface, which is this:

    public interface IQuickLinkRepository
{
QuickLink[] GetAllBy(string userId);
void Add(QuickLink quickLink);
void Delete(Guid quickLinkId);
}

You can download the project to see the implementation of the interface. I used L2S and again, you can download the project to see the implementation. Alrighty, so let’s get to the real code that you can use.

I added a user control, which looks like this:

<div id="quicklinks">
<
div>
<
div class="loader" id="loading" style="display: none"></div>
<
asp:Repeater ID="rQuickLinks" runat="server">
<
HeaderTemplate><h3>Quick Links</h3><ul></HeaderTemplate>
<
ItemTemplate><li id="li<%# ((QuickLink)Container.DataItem).Id %>"><img src="/_a/i/ico-delete.png" width="16" height="16" alt="Delete <%# ((QuickLink)Container.DataItem).DisplayName %>" id="<%# ((QuickLink)Container.DataItem).Id %>" /><%#((QuickLink)Container.DataItem).BuildHyperlink() %></li></ItemTemplate>
<
FooterTemplate></ul></FooterTemplate>
</
asp:Repeater>
<
a href="#" id="addQuickLink" title="Add Quick Link">Add Quick Link</a>
</
div>
<
div style="display: none">
<
fieldset>
<
legend>Add Quick Link</legend>
<
p><label for="<%=tbDisplayName.ClientID %>" title="Display Name:">Display Name:</label><br />
<
asp:TextBox ID="tbDisplayName" runat="server" /> <asp:RequiredFieldValidator ValidationGroup="addQuickLink" ControlToValidate="tbDisplayName" Display="Dynamic" ID="rv1" SetFocusOnError="true" runat="server">*</asp:RequiredFieldValidator></p>
<
p><label for="<%=tbURL.ClientID %>" title="Website Address:">Website Address:</label><br />
<
asp:TextBox ID="tbURL" runat="server" /> <asp:RequiredFieldValidator ValidationGroup="addQuickLink" ControlToValidate="tbURL" Display="Dynamic" ID="rv2" SetFocusOnError="true" runat="server">*</asp:RequiredFieldValidator> <asp:RegularExpressionValidator ControlToValidate="tbURL" Display="Dynamic" ID="re1" runat="server" SetFocusOnError="true" ValidationGroup="addQuickLink" ValidationExpression="http(s)?://([\w-]+\.)+[\w-]+(/[\w- ./?%&amp;=]*)?">*</asp:RegularExpressionValidator></p>
</
fieldset>
<
asp:Button ID="bSubmit" runat="server" CausesValidation="true" Text="Add Quick Link" ValidationGroup="addQuickLink" OnClick="AddQuickLink" /> <a href="#" title="Cancel Quick Link Addition" id="cancelAddQuickLink">Cancel</a>
</
div>
</
div>

That looks really messy, but that’s web forms for ya :D

Here’s the interesting part, instead of having a code-behind and inheriting from a partial, we added a class to the Core.UI.Controls called QuickLinksControl. Here’s the code:

 

public class QuickLinksControl : BaseControl
{
private IQuickLinkRepository _repository;

protected void Page_Load(object sender, EventArgs e)
{
_repository = DependencyResolution.
DependencyRegistrar.Resolve<IQuickLinkRepository>();

if (Request.Form["QLID"] != null)
DeleteQuickLink(
new Guid(Request.Form["QLID"]));

if(!Page.IsPostBack)
LoadQuickLinks();
}

private void DeleteQuickLink(Guid quickLinkId)
{
_repository.Delete(quickLinkId);
Response.Write(quickLinkId);
Response.End();
}

protected void AddQuickLink(object sender, EventArgs e)
{
if (!Page.IsValid)
return;

var tbDisplayName = (TextBox)Page.FindControl("tbDisplayName");
var tbURL = (TextBox)Page.FindControl("tbURL");
var button = (Button) sender;
var quickLink = new QuickLink {DisplayName = tbDisplayName.Text, URL = tbURL.Text, UserId = button.CommandArgument};
_repository.Add(quickLink);

tbDisplayName.Text =
"";
tbURL.Text =
"";

LoadQuickLinks();
}

private void LoadQuickLinks()
{
var rQuickLinks = (Repeater) FindControl("rQuickLinks");
rQuickLinks.DataSource = _repository.GetAllBy(Page.User.Identity.Name);
rQuickLinks.DataBind();
}
}

The interesting parts…

  1. DeleteQuickLink() is checked in the Page_Load. We’re doing this here for an AJAX call from the ascx file. We’re probably going to add some type of validation token to the if statement as well. We also might create some type of AJAX call handler instead of throwing them in the page_load, but it works for now.
  2. In the DeleteQuickLink method, we’re calling Response.Write(quickLinkId) and Response.End(). This is used for the AJAX call too to return the guid sent. There’s probably a better way, but so far this is working.
  3. In the AddQuickLink, you’ll notice that we’re having to find the controls because it’s not a partial and doesn’t know anything about the ascx page. This part kinda stinks, but it’s not too bad.

Here are the AJAX calls from the ascx page:

<script language="javascript" type="text/javascript">
$(document).ready(
function() {
$(
"#addQuickLink,#cancelAddQuickLink").click(function() {
$(
"div#quicklinks div").toggle();
});

$(
"#loading").ajaxStart(function() {
$(
this).show().text("Deleting...");
}).ajaxStop(
function() {
$(
this).hide().text("");
});

$(
'#quicklinks ul li img').click(function() {
if (!confirm('Are you sure you want to delete?'))
return;

$.post(window.location.href, { QLID: $(
this).attr('id') }, function(data) {
$(
"#li" + data).remove();
});
});
});
</
script>

I’ve never used the ajaxStart or ajaxStop before, but they’re pretty cool features. I also wanted to note that I started using StructureMap 2.6.1 and I’m liking it. It’s a pretty big difference from the last blog post on StructureMap. Now the ForRequestedType<> in the registry looks like this:

For<IQuickLinkRepository>()
.Use<
QuickLinkRepository>()
.Ctor<
string>("connstring")
.Is(
@"Data Source=.\SQLEXPRESS;AttachDbFilename=""..\LearnerSample.mdf"";Integrated Security=True;User Instance=True");

 

Here’s the finished product:

image

image 

So, any thoughts or ideas on what we’re doing? I’d appreciate any advice or comments. It’s probably easier to understand by downloading the sample code.

Thanks for reading!

Download Source

Shout it

kick it on DotNetKicks.com

blog comments powered by Disqus
Related Posts Plugin for WordPress, Blogger...