Ajax with the ASP.NET MVC Framework

This post presents a few basic Ajax features (similar to partial rendering and behaviors in terms of concepts) running on top of the ASP.NET MVC framework... some early ideas, experimentation and app-building results.

Hopefully everyone had a good few days off. Before the holiday break, I did some app-building on top of the ASP.NET MVC framework. Actually rather than building some sort of fancy app, instead I was prototyping some features on top of the framework bits slated for an initial release. I've shared out the sample code, sample app and tests - yes, sorry for another tease :-)... but stay tuned... and you'll soon have actual bits to play with as well. Until then, you can download the sample code and browse it locally, and follow along the rest of the post. In particular there are two projects within the solution: TaskList (the web app) and AjaxMVC (a class library with Ajax extensions).

One of the prototypes is around bringing some basic Ajax functionality - basically to get post-back-less partial rendering and some behavior-like extensions to associate with DOM elements - sort of like ASP.NET Ajax but in a manner that fits with the pattern around how controllers and views are written. I should say that eventually Ajax functionality will exist out-of-the-box, so you can think of this as an early experiment, and by no means complete. In the spirit of experimentation, feedback and suggestions are welcome.

A Super-simple Task List Application
I know, the task list application has been beaten to death, but it’s a simple enough that it allows focusing on the Ajax features. Below is the screen-shot of the application that you get when browsing /TaskList on the site.

TaskList Screenshot

For a great intro to MVC, to get a sense of what is involved, the application structure etc, check out Scott's intro post if you haven't already done so. I won't repeat that material here, but like Scott's product catalog application, the TaskList application has a controller to handle the incoming requests, a set of classes making up the model representing a collection of task items, and a set of views used to render the user interface.

I'll point out some basic aspects of the application as it exists before I start adding some Ajax functionality.

The Model
Lets start with the model, which is primarily made up of two types:

public class Task {

    public int ID { get; }
    
    public bool IsComplete { get; set; }
    
    public string Name { get; set; }
}

public interface ITaskDB {

    Task AddTask(string name);
    
    void CompleteTask(Task task);
    
    Task GetTask(int taskID);
    
    ICollection<Task> GetTasks();
    
    void RemoveTask(Task task);
}
The implementation of this isn't that interesting. For my sample purposes, I actually used an in-memory collection in my concrete implementation of ITaskDB rather than an actual database, since that isn't the focus here. I chose the interface approach as it allows me to plug in a test implementation when writing tests, as we'll see later.

The Controller
I have a single controller, called TaskListController which has a few actions it exposes. Here are two of them:

public class TaskListController : Controller {

    private ITaskDB _taskDB;

    public TaskListController() : this(GlobalTaskDB) {
    }
    public TaskListController(ITaskDB taskDB) {
        _taskDB = taskDB;
    }

    // Example URL: /TaskList or /TaskList/List
    [ControllerAction]
    public void List() {
        Dictionary<string, object> viewData = new Dictionary<string, object>();
        viewData["Tasks"] = _taskDB.GetTasks();
        
        RenderView("List", viewData);
    }
    
    // Example URL: /TaskList/Add
    [ControllerAction]
    public void Add(string name) {
        Task newTask = null;
        if (String.IsNullOrEmpty(name) == false) {
            newTask = _taskDB.AddTask(name);
        }
        
        if (newTask != null) {
            RedirectToAction("List");
        }
        else {
            Dictionary<string, object> viewData = new Dictionary<string, object>();
            viewData["Tasks"] = _taskDB.GetTasks();
            viewData["ShowAddTaskError"] = true;
            
            RenderView("List", viewData);
        }
    }
    
    // Other actions: DeleteTask, CompleteTask
}

The View
Next, I've got my UI implemented in List.aspx within the /Views/TaskList folder of my application. Here is some of the interesting markup:

<div id="taskList">
  <% foreach (Task task in Tasks) { %>
    <div>
      <div id="taskItem<%= task.ID %>" class="taskPanel">
        <form method="post" action='<% Url.Action("CompleteTask") %>'>
          <input type="hidden" name="taskID" value="<%= task.ID %>" />
          <input type="submit" name="completeTask" value="Done!" />
          <input type="submit" name="deleteTask" value="Delete" />
          <span><%= Html.Encode(task.Name) %></span>
        </form>
      </div>
    </div>
  <% } %>
</div>

<form method="post" action='<%= Url.Action("Add") %>'>
   <input type="text" name="name" />
   <input type="submit" name="addTask" value="Add Task" />
</form>

The Unit Tests
Finally, I've got my set of associated test cases within a separate Test project. Here are a couple of the tests associated with the Add action of my controller.

public void TestAddEmptyName() {
    TestTaskDB taskDB = new TestTaskDB();
    taskDB.AddTask("Test Task 1");
    taskDB.AddTask("Test Task 2");
    
    TestTaskListController controller = new TestTaskListController(taskDB);
    
    controller.Add(null);
    
    Assert.AreEqual("List", controller.RenderedView);
    Assert.AreEqual(true, controller.GetRenderedViewData("ShowAddTaskError"));
    Assert.AreEqual(2, taskDB.Count);
}

[TestMethod]
public void TestAddValidName() {
    TestTaskDB taskDB = new TestTaskDB();
    taskDB.AddTask("Test Task 1");
    taskDB.AddTask("Test Task 2");
    
    TestTaskListController controller = new TestTaskListController(taskDB);
    
    controller.Add("New Task");
    
    Assert.AreEqual("List", controller.RedirectedView);
    Assert.AreEqual(3, taskDB.Count);
}

So that is basically the setup - a simple TaskList application, ready to be ajaxified. Here is a set of things I'd like to do:

  1. Add items to the list without a full post-back, and add the newly added task item to the end of the list.
  2. Complete and delete tasks, also without full post-backs.
  3. Add a watermark to the textbox

I'd like to minimize any differences between the Ajax and non-Ajax versions. Of course, I'll be making the changes in the application while using the prototype I am putting together, which introduces some new classes in the System.Web.Mvc namespace such as Ajax extension methods etc.

Adding Ajax Support in the Controller
The first thing I'll do is derive TaskListController from AjaxController instead of just Controller.

AjaxController is a class I added, and it introduces a new property IsAjaxRequest, which I can use in my action methods to do things like render different views. It also introduces members such as RenderPartial, which can be used to render a portion of the user interface defined within a partial view as opposed to the full page. So here is my updated controller and the updated Add method with the changes and additions in bold.

public class TaskListController : AjaxController {

    public void Add(string name) {
        Task newTask = null;
        if (String.IsNullOrEmpty(name) == false) {
            newTask = _taskDB.AddTask(name);
        }
        
        if (IsAjaxRequest) {
            if (newTask != null) {
                RenderPartial("TaskView", newTask);
            }
        }
        else {
            if (newTask != null) {
                RedirectToAction("List");
            }
            else {
                Dictionary<string, object> viewData = new Dictionary<string, object>();
                viewData["Tasks"] = _taskDB.GetTasks();
                viewData["ShowAddTaskError"] = true;
                
                RenderView("List", viewData);
            }
        }
    }
}

My TaskView partial is defined as TaskView.ascx within the same /Views/TaskList folder and is as follows:

<div id="taskItem<%= Task.ID %>" class="taskPanel">
  <form method="post" action='<%= Url.Action("CompleteTask") %>'>
    <input type="hidden" name="taskID" value="<%= Task.ID %>" />
    <input type="submit" name="completeTask" value="Done!" />
    <input type="submit" name="deleteTask" value="Delete" />
    <span><%= Html.Encode(task.Name) %></span>
  </form>
</div>

If this looks familiar, it is because it was essentially re-factored out of the existing List.aspx. The view data for this partial is a single Task instance. Now that I've got this partial defined, I can use it from the main List.aspx view page as well. This is accomplished using the RenderPartial extension method I provide. Once I've done this the task list portion of my List.aspx gets reduced to:

<div id="taskList">
  <% foreach (Task task in Tasks) { %>
    <div>
      <% this.RenderPartial("TaskView", task); %>
    </div>
  <% } %>
</div>

Next I need to cause the view to issue XMLHttp requests rather than do a regular form submit. Again, I've provided a few extension methods: RenderBeginForm which renders a regular form tag, RenderBeginAjaxForm which renders an Ajax-enabled form that I will be interested in this scenario, and RenderEndForm. Using these, the form tag representing the UI to add tasks becomes (with changes represented in bold):

<% RenderBeginAjaxForm(Url.Action("Add"),
                       new { Update="taskList, UpdateType="appendBottom",
                             Highlight="True",
                             Starting="startAddTask", Completed="endAddTask" }); %>
   <input type="text" name="name" />
   <input type="submit" name="addTask" value="Add Task" />
<% RenderEndForm(); %>

As you can see the contents of the form did not change. Just the declaration of the form itself. Basically RenderBeginAjaxForm takes in the URL representing the action to invoke when the form is submitted, and then some Ajax-specific parameters as follows:

  • Update: The id of the DOM element to update with the results. In this case, it’s the container of the task items.
  • UpdateType: This can be "none", "replace", "replaceContent", "insertTop", or "appendBottom" - in our case we choose the latter, which causes the newly rendered task to show up at the bottom of our list.
  • Highlight: This is optional, and when set, it causes the newly added item to be highlighted for a short duration with a subtle yellow fade effect.
  • Starting, and Completed: These are essentially events. You can handle them and write little bits of Javascript to do things like disable buttons, show progress indicators, add additional bits to the outgoing request, or pre-process the incoming response etc.

Here is the Javascript, written in TaskList.js, which I've placed in the /Views/Scripts folder of my application.

function startAddTask() {
    $('addTaskGroup').disabled = true;
    return true;
}
function endAddTask() {
    $('addTaskGroup').disabled = false;
    return true;
}

In my startAddTask method, I can also perform validation, and return false, if the form is invalid to prevent the request from being issued. The sample code does show some rudimentary form of validation. However, an actual validation system would be a whole blog post in itself, which I'll get to in the future.

My final step is to actually include the TaskList script as well as the script framework that provides the core bits of functionality. I can go into my master page for the application which is in the /Views/Layouts folder and add some rendering directives e to initialize the Ajax functionality, register scripts, and render out scripts at the bottom of the generated HTML. This is accomplished by calling the extension methods I've added to the Ajax object.

<% Ajax.Initialize(); %>
<% Ajax.RegisterScript("~/Views/Scripts/TaskList.js"); %>

<!-- UI goes here -->

<% Ajax.RenderScripts(); %>

Actually another step I need to do is add a test case, so I'll add a test case that tests the Ajax variant of my Add controller action.

I can do the same thing for handling the completion and deletion of the tasks. Completion of tasks causes the UI for that task to be re-rendered and the new HTML replaces the existing HTML. Deletion of tasks is a bit more interesting - rather than updating HTML, the old rendering is simply removed from the DOM, and the HighlightLeave effect (a red fade effect) can be used to make it visually interesting. Either way, you can check out the mechanics by browsing through the source code (specifically TaskList.js and TaskView.ascx, as well as the associated controller actions).

Adding Some More Ajax
I can do something very similar for the <form> contained within the TaskView.ascx representing each task item to convert it from a regular submit-based form to an Ajax-enabled form, so that item completion and deletion can occur without page refreshes. The sample code demonstrates this, so I won't repeat it all here.

Instead, what I do want to show is adding script behaviors and associating it with the HTML being rendered for creating additional Ajax-based interactivity within your application's UI. Specifically as I indicated above, I want to add a watermark to the textbox, that provides an in-place hint to the user about what is the expected input. The watermark appears as long as there is no user input, and it goes away when the user focuses into the textbox.

For the purposes of this post, I won't go too deep into the actual script itself. Again the script is included in the sample. The watermark behavior is implemented as a Behavior for those of you familiar with the ASP.NET Ajax model of implementing script behaviors. Like all behaviors it is associated with a DOM element, the textbox in our scenario, and it subscribes to events raised by the element to do its job.

In traditional web forms pages, I would then have an Ajax-enabled server control such as a WatermarkExtender I could associate with a TextBox server control. Here instead I have a rendering method implemented as an extension method that allows me to do the equivalent of creating and initializing an instance of the script behavior. Using that, here is my updated view:

<% RenderBeginAjaxForm(Url.Action("Add"),
                       new { Update="taskList, UpdateType="appendBottom",
                             Highlight="True",
                             Starting="startAddTask", Completed="endAddTask" }); %>
   <input type="text" name="name" id="nameTextBox" />
   <% Ajax.Watermark("nameTextBox",
                  new { watermarkText="[What do you need to do?]",
                        watermarkCssClass="watermark"}); %>

   <input type="submit" name="addTask" value="Add Task" />
<% RenderEndForm(); %>

The extension method implementation is really simple. It pretty much just calls into my Ajax framework. Here is what my WatermarkBehavior class looks like:

public static class WatermarkBehavior {

    public static void Watermark(this AjaxHelper ajaxHelper, string id, object watermarkOptions) {
        ajaxHelper.RegisterScript("~/Views/Scripts/Watermark.js");
        ajaxHelper.RegisterScriptBehavior(id, "Ajax.Watermark", watermarkOptions);
    }
}

Of course in reality, I might have more to my implementation but what this shows is the core framework providing the functionality of collecting registered scripts, rendering them out into the page, and then instantiating the behavior objects, hooking them up with their associated DOM elements, and passing in the option values used by the developer in their view implementation to customize the specific instances.

Conclusion
This shows in brief a first stab at getting the core aspects of the Ajax functionality that exists in ASP.NET pages - partial rendering, behaviors and extender controls - and bringing those concepts to MVC views. I am sure there is a lot more to be done to round out these two scenarios, as well as address other Ajax features (validation, periodic refreshes, simplify invoking controller methods via script proxies ala web services etc. etc.). Check out the prototype and sample app as they exist right now, and go ahead and share your thoughts and ideas.


[ Tags: | | | ]
Posted on Sunday, 11/25/2007 @ 11:08 PM | #ASP.NET


Comments

57 comments have been posted.

Roman Nikitin

Posted on 11/26/2007 @ 12:18 AM
Hello, Nikhil!
Great post!
Asp.Net MVC become more pretty :)
And i cant wait, when CTP will be released!

Another question.
What Ajax prototype you used in sample?
Does there original Asp.Net Ajax Extension or something specified ?

Thanks, Roman.

RichB

Posted on 11/26/2007 @ 12:50 AM
Why did you use a foreach loop in the view and not a repeater?

Ponnu

Posted on 11/26/2007 @ 12:52 AM
Hi Nikhil,

Great Stuff. Any idea when the first ASP.Net MVC CTP will be out?

Thanks
Ponnu

Mike

Posted on 11/26/2007 @ 1:42 AM
Great post, very detailed!

Do you have any idea if the concepts you present here will be the ones we will be working with? Or will Microsoft release a more declarative framework for ASP.NET MVC + Ajax?

I like the idea of rendering partial views based on user controls, seems like a natural fit. I don't like the way the AjaxController is not itself aware that a request is an Ajax request. I shouldn't have to write to many if/else block is most scenario's.

Thanks!

Remy Blaettler

Posted on 11/26/2007 @ 7:00 AM
I agree with Mike, wasn't the idea of the MVC that the controller is independent of the GUI? This seems like we all mixed it together again. It's not like I have a better solution, but just wondering...

Deepak Chawla

Posted on 11/26/2007 @ 7:57 AM
Hi Nikhil,
A few months ago I started working on Web Client Software factory and Service factory. I joined the two in my solution so that I had the features of both.
So in order to implement what you have outlined, I do it via the Guidance Toolkit Receipies
In addition getting the data via best practices also gets implemented.

And for just ajax calls (Created via service factory) in the implementation factory I just add the attribute of [ScriptService] on class and [Webmethod] and [ScriptMethod(UseHttpGet = false, ResponseFormat = ResponseFormat.Json)] on the method. Thus beautifully integrating the service factory with pure ajax calls.

Arne Claassen

Posted on 11/26/2007 @ 8:07 AM
Nikhil,

Thanks for the example. I know this is all very much pre-release prototyping, but I do have a question about your ASCX. It seems you used the extension just to fit with existing usage of user controls, i.e. there isn't a native support in ASP.NET MVC for componentized views yet, is there?

Do you see that in the future it would continue to be used with a syntax similator your RenderPartial? or will we get a more control like syntax to invoke view components as if they were tags (can't give an example, since html is stripped, I hope I'm expressing this clearly enough).

Nikhil Kothari

Posted on 11/26/2007 @ 8:46 AM
Roman, Ponnu - The CTP will be out soon ... I promise, while I can't post planned dates right now, I'll say within weeks now.

RichB - You should be able to use a Repeater control - however personally I thought the foreach was simple enough, plus I don't have to write a Page_Load handler to set the DataSource and call DataBind(). Additionally, since I am not using the control tree capabilities of the page framework, I chose the foreach, and minimized the number of controls being created.

Mike, Remy - you have a few choices - you can write special actions that are for the ajax scenario only - that avoids some ifs (but presumably you want to then check if its an ajax prequest - that perhaps can be done with a flag on the ControllerAction attribute in the future). You can conditionalize the rendering of your view itself, by using Ajax.IsAjaxRequest in the view as opposed to IsAjaxRequest within the controller. I didn't show this, but I added that extension method as well. So you have at least three choices. I chose to use the same action methods, so that if javascript is disabled, the page works as before, when I didn't add ajax functionality to it. However, I am not saying that is always the right choice for all app scenarios. Hope that makes sense.

Arne - you can declare an ascx declaratively like you've always done. Additionally if your user control derives from ViewUserControl you will be able to declaratively set the ViewDataKey property to have it be initialized with some view data. However, I think you're alluding to much more - my next prototype I am looking into is around using components in the MVC context... so stay tuned.

Fredrik Normén

Posted on 11/26/2007 @ 9:42 AM
Good work!

But I want to see "Server Controls" to make the HTML more cleanly from in-line code blocks. For example the last part of your code where you make a call to the RenderBingAjaxForm and Ajax.WaterMark makes the code messy, and can in a large form with a lot of AJAX Extensions and inline-code block make the code less readable (can be hard see and easy locate the HTML element between code-blocks, they will sort of be hidden between server-side code).

Keep up the good work!

Nikhil Kothari

Posted on 11/26/2007 @ 10:23 AM
Fredrik - I agree with you - honestly, I am still thinking about this. I was thinking not only in terms of server controls, but other markup patterns (thinking bigger about what really is the right approach, if one were to build it).

I think it would be interesting to see some server controls that retain the power of the model, yet, are true to xhtml/css and semantic markup. There are some interesting things to watch out for - server controls tend to not be (or don't want to be) restricted to just rendering and exposing properties... it is all the additional things a server control wants to do leads to the full set of requirements being handled by the page framework, which some may not like. I tend to see it as a case where there needs to be choice - of course the pitfall here is tending toward a model where there are two implementations of everything.

Just some thoughts really at this point.

Fredrik Normén

Posted on 11/26/2007 @ 10:38 AM
Nikhil:

I agree with you. For fun I created a "server control" for the Html.ActionLink, so instead of typing:

Html.ActionLink("Home", "Home");

I could add a control like this:

<mvc:ActionLink ID="ActionLink3" Action="Home" runat="server" Text="Home"/>

In this case I wanted to have another model because I didn't wanted to create a Server Control by inheriting the Control base class etc. The only thing I wanted to do is some simple rendering.

Steve Gentile

Posted on 11/26/2007 @ 2:41 PM
Fredrik, have you worked with ViewComponents from Monorail?

http://www.castleproject.org/monorail/documentation/trunk/usersguide/viewcomponents.html

The simplicity is there - inherit from ViewComponent and override Render.

Thanks for the writeup Nikhil

Nikhil Kothari

Posted on 11/26/2007 @ 7:33 PM
From the brief look at view components (thanks for sharing the link)...

There are perhaps two aspects to think through here:
1. Encapsulation - I almost wonder if we should have a minimal ViewComponent class that doesn't derive from Control if you don't want the minimal set of concepts but achieves encapsulation etc.
2. Declarative syntax - I have some thoughts (or maybe some crazy ideas here) that would enable these lightweight classes to be used declaratively within aspx markup... need to verify some things. Will do a future post if the ideas pan out.

Shawn Oster

Posted on 11/26/2007 @ 10:12 PM
Reading through the comments it's interesting to see the different ways people approach web development. Seems some really want the concept of server controls while others aren't that bothered by it. Since I have to do a lot of work still in classic ASP, PHP, Rails and ASP.NET I much prefer things like a foreach instead of a repeater and try to avoid server controls yet I know a lot of developers whose only programming experience is in .NET and I'm curious how they're going to transition to the cleaner yet more "thoughtful" MVC pattern.

I'd love to see some MVC examples using some of the existing AJAX frameworks out there like Prototype or jQuery. This has been a huge source of frustration with server controls, the inability to use the server control's ID attribute as a valid selector when attaching JavaScript behaviours or CSS styles. Sure you can do it with a little sleight of hand but it really feels like you're having to break the system a little to do it.

Fredrik Normén

Posted on 11/27/2007 @ 12:36 AM
The helper functionality like creating a Link, RenderBeginForm etc is the part I want to have in a declarative way to not make the code messy with a lot of server-side code blocks, only to use a markup similar to XML/HTML to make the page easier to read and locate elements etc. Using things like for each in code is fine for me, it's only one single line. I don't like to bind data in the Page_Load event etc.

Nikhil:

Here are my thoughts about your 2 thoughts/questions

1) I prefer a minimal ViewComponent class that doesn't derive from the Control, but give it a new name so developers don't mess it up with controls. Another solution would be to use the Control class but by default hide its members; all expect the rendering part as default.

2) I still like the way to declare server controls, it’s something people are used to and it will so nicely blend in to HTML. The only problem with that markup is that developers can probably mix it up with regular server controls (but they are sort of a server-control, it will be rendered on the server-side), but I'm interested in your thoughts about a declarative syntax.

Fredrik Normén

Posted on 11/27/2007 @ 1:19 AM
By using a declarative syntax (tags) instead of server-side code blocks can make it easier for designers to maintain the Views. By adding server-side code block can make it messy and will also make it difficult for designers to understand (they need to learn a programming language). I think it will be much easier for a designer to understand markup tags than server-side code, for example:

<c:if test="${customer == null}">

mrplace

Posted on 11/28/2007 @ 7:07 AM
when can we use the mvc framework?

Raf

Posted on 12/7/2007 @ 1:18 PM
I can see that you guys are really trying hard to imitate ruby on rails. Even the names like 'partials' are there. I would advise to take a look at some different approaches like creating a set of regular expressions that are bound to static view methods in a config file. Then, the name of the method is not directly related to the url and the controller name. Apart from that, I think there should be a way to easily integrate various types of applications under one project. For example all the apps that are under /basket/ are one app and the apps that are under /home reside in completely different directory with f.e. different language. This way you would be able to create a set of apps and use them all over the place. As for the SQL: all the tables would start with basket_*. Just a thought. I hope you will have migrations and automatic sql generator for you models. Just run an app and I have my sql server filled with tables and procedures for the models in C#.

Kevin Frey

Posted on 12/12/2007 @ 4:55 PM
You mention that TaskView.ascx has been factored out of List.aspx. Does that mean List.aspx has changed also? Can you please clarify?

Nikhil Kothari

Posted on 12/13/2007 @ 3:21 AM
Kevin - Yes, List.aspx changed in the sense that TaskView.ascx was factored out of it. The extracted portion of List.aspx was then replaced with a call to RenderPartial to call into TaskView.ascx.

If it helps, think refactoring for c#/vb, where you do something like extract a method. Same thing, but for a view instead.

MDB

Posted on 12/17/2007 @ 5:31 AM
Does anyone know of any way to use the ASP.NET Ajax Client Library with MVC without going back to using <form runat="server"> ???

Nikhil Kothari

Posted on 12/21/2007 @ 10:36 AM
MDB - You'll need to use the script files directly via direct script references, rather than by using a ScriptManager control. The partial rendering stuff however depends on the whole web forms framework...

Mark Woodlief

Posted on 12/27/2007 @ 8:42 AM
I just tried to implement this model, the MVC model is great, but the Ajax model used in this example doesnt seem to sit well with the AjaxControls.. Seems like it is a huge mess if you try to implement something like a combo and based on the choice in the combo a grid refreshses.. in order to do that with the ajax controls and extensions is a no brainer with update panels and asynch triggers.. Are you telling me I would have to abandon those and basically rewrite in javascript what microsoft has put so much work into?

Nikhil Kothari

Posted on 12/28/2007 @ 2:51 AM
Mark - most controls that exist today are designed to work with the web forms server control framework whereas, in the MVC world the focus is on splitting out controller logic from view rendering - there are strengths of both models, and the investments in both models will carry forward. Keep in mind that different apps might have different needs, and one model might be more suited to some apps. Also, over time, the MVC model will likely offer additional building blocks in terms of productivity as well... however at this moment things are in quite early stage.

Mark Woodlief

Posted on 12/28/2007 @ 9:12 AM
I appreciate the response.. thanks.. Im currently considering two models, I've been using the Web Client Software Factory for an application I've been working on, but have a few new developers on staff that are and will run into a serious learning curve using p&p stuff... so figured MVC would be a bit of an easier approach as it removes the complexity of presenter etc.. thoughts? Tough choice now days, with all the good models, toolkits, etc out there.. thanks again!!!

Bob

Posted on 12/28/2007 @ 7:13 PM
Hey Nikhil, I am having one problem, and I am not sure if it is a bug, or if I am just doing something wrong. I have a form that when submitted successfully does a RedirectToAction(new {Controller = "Home"}). My RenderBeginAjaxForm() UpdateType="replace". What happens is that when the RedirectToAction() is hit in my Controller, the new page is injected into the current page instead of redirecting.
How do I redirect to another page (controller/action)?
Thanks for the great post!

Nikhil Kothari

Posted on 12/30/2007 @ 10:28 AM
Bob - basically what is happening is the redirect is being handled internally by xmlhttprequest, and the ajax script on the page is getting the content of the redirected to page.

You will need to send back a custom 302 response (i.e, not an actual 302, but a regular 200 response, whose response includes the fact that you need to redirect, and the redirect location). Then in your script, handle the completed event of the ajax form to see if its this custom response, and instead of letting the update happen, call window.navigate with the new location.

Of course, all of this could be built in into my ajax framework that I have in this post - I will consider adding that. Btw, this is exactly what we do in the partial rendering feature that we have in asp.net ajax.

Bob

Posted on 1/8/2008 @ 12:19 PM
Thanks Nikhil. Any hint as to how you send the custom 302 response back? I would have thought/hoped that doing a Response.Redirect() in the Controller would do the trick, it doesn't appear to be that simple :(

Michael Jenkinson

Posted on 1/15/2008 @ 11:55 PM
Very nice work on making this. :)

But when experimenting with it in FireFox(with FireBug debugger), I get an error when I use the insertTop mode.

The error is on this line
containerElement.insertBefore(updateElement);

And the error message is "Not enough arguments".

Michael Jenkinson

Posted on 1/16/2008 @ 12:08 AM
The problem line that I reported in the last comment, is fixed by replacing it with

containerElement.insertBefore(updateElement, containerElement.firstChild);

Ross Jones

Posted on 1/19/2008 @ 11:07 PM
I am having trouble using the MVC Ajax functionality in a MVC user control. It is complaining about a null reference on the AjaxHelper being passed into "public static void RegisterScript(this AjaxHelper ajaxHelper, string scriptVirtualPath)". Is this maybe due to the order in which the controls are initialized?

Is there a way to get this to work?

Thanks

Ross Jones

Posted on 1/19/2008 @ 11:25 PM
Oops! never mind, using RenderPartial helps =)

Thanks

MCA

Posted on 1/30/2008 @ 3:13 PM
FWIW, I like the idea here, but the implementation is a bit cumbersome.

The view separation is nice, but I think it's better to have fixed methods (Get(),Post(),Put(),Delete()) to which you add your code rather than mapping HTTP Methods to your own functions. This simplifies the dispatch, too.

Someone else on the comment list here (Raf?) mentioned Rails, et al. I think something closer to RestLets would be a better angle.

I implemented a similar pattern for ASP.NET/C# last year and just this week did a version of this TaskList to highlight differences. Check it out here: www.exyus.com/xcs/tasklist/

Joe

Posted on 2/4/2008 @ 5:52 AM
I've been playing with your ajax goodies and have run into an issue I can't quite figure out. This may be just an IE issue, or my own ignorance (never rule that one out) but I can't seem to get ajax to correctly add rows to a table.

I've got a simple <table id="blah"></table>

I'm telling the form/controller to add rows to it using a user control formed like so <tr><td><%=someData%></td></tr>

When I run it, I get a 'Unknown Runtime Error'. I've stepped into the javascript and mucked around a bit to try figure it out - but no luck. I've tried changing the different update options (replace, replaceContent, appendBottom). Nothing helps. I've seen some posts saying it may have something to do with IE validating the innerHTML property.

Oh, it works in firefox - no problem. And the html seems to be well formed.

Thanks.

-Joe

Carl

Posted on 3/8/2008 @ 3:25 AM
Great post, but please include a printer friendly CSS!

Henry

Posted on 3/10/2008 @ 7:47 AM
Hi Nikhil, This is great stuff thanks very much. Have you looked at the latest Preview (2). I'm trying to do something similar to this pattern but in the new release the IView has been removed? (or at least I can't see it anymore). Do you have any pointers for migrating this pattern to the new preview 2?

Thanks

Dan

Posted on 3/17/2008 @ 10:32 AM
Seconded on the request to update this sample for use with the Preview 2 bits.

Thanks!

Emad Ibrahim

Posted on 3/31/2008 @ 6:56 AM
Thanks for this great post.

I have updated the code to work with preview 2 and you can get it from my blog.

Keep up the good work.

PS: I can't include a link to my post because it won't let me post the comment and keeps saying I have "unallowed words"

Angel Escobedo

Posted on 5/21/2008 @ 9:38 AM
Hi, how can i bind like your demo this componets (AjaxDataControlls) cause it have binding with javascript, but i want to know if is possible to bing it from aspx Thanks!

Suman Chakrabarti

Posted on 5/27/2008 @ 9:32 AM
Have you tried this with script alone or your Script# framework?

sudhakar

Posted on 6/18/2008 @ 4:56 AM
Can we Combine the MVC framework and Service factory. if yes how??

Thanks in Advance
Sudhakar
-----------------------------
MVC Framework :- divides the web application in to 3 parts Model (Business layer), View (UI) and Controler(to communicate between the other two).
Service Factory :- had four parts - Client, Service interface layer, Businees layer and DataAccess layer. (every communication here will go through Service Adapters and DataContracts.)

Andrew D

Posted on 7/20/2008 @ 11:30 PM
This page seriously kills FF3!

fox_sky

Posted on 9/15/2008 @ 10:22 PM
dwwdwd1111

Gustavo

Posted on 10/12/2008 @ 9:09 PM
I would really like to build this project, but the effort of overcoming the release changes for the version of ASP.NET MVC that this project was built on is a little daunting... I can't even find release notes, forum entries, or anything *web* for earlier versions of MVC to figure out how ViewFactory.CreateView() disappeared or what it became (or if it became anything). Do you have any pointers on building the project on ASP.NET MVC Release 5?

--Gus

Gustavo

Posted on 10/12/2008 @ 9:16 PM
Nevermind. Looks like Emad already covered this, above. The Release 2 version of the project posted on his site contains the versions of all the Assemblies and (hence) namespaces that make the project function. I "pity the fool" that tries to bring this project up from Release 1 to Release 5 without already knowing ASP.NET MVC from the get go.

Prakash

Posted on 10/16/2008 @ 2:40 PM
Hi Nikhil,
I have a very unique issue, not sure whether you've faced it.
I'm using MVC and AJAX for my application. I've done a '.*' in my application configuration in IIS to route all requests to aspnet_isapi. The issue is, I have a PageMethods call in one of the pages (inherited from ViewPage, Page ultimately), and that method doesn't get called. I remove the '.*' setting from the configuration, and it works.

Now I cannot afford to remove the IIS setting. Could you tell me how I can make my PageMethods call work?

Thanks.

Sibi

Posted on 10/22/2008 @ 1:06 AM
Good post

Chris

Posted on 11/12/2008 @ 4:35 AM
I agree, I think the Ajax model is messy. I'm hoping for a true AJAX experience. Like this:

var myObj = new Ext.Grid({
columns: <%=this.getColumns>, // (ex. 'column 1', 'column 2')
backColor: <%=this.backColor>,
});

Will that be possible?

Alex Sleiborg

Posted on 11/25/2008 @ 5:49 AM
What is ViewContext, AjaxHelper and so on? Trying to compile the project.

Jarrett Meyer

Posted on 2/12/2009 @ 10:36 AM
I've built a tiny application, showing how this would be different in the release candidate.

http://jarrettatwork.blogspot.com/2009/02/aspnet-mvc-ajax-brief-introduction.html

Bla

Posted on 3/5/2009 @ 2:40 PM
Feels like stepping back 5 years ago to the good old vb script / asp combination.... looks like it to. I totally hate it!

Keith Newman

Posted on 3/18/2009 @ 2:19 PM
Great post.

Now that ASP.NET MVC 1.0 has been release, Microsoft has published more about MVC on the MSDN Web site. To view the MCV content, go to http://go.microsoft.com/fwlink/?LinkId=145989.

almny

Posted on 5/6/2009 @ 5:10 AM
i want to know if i can use ajax control with mvc asp.net or no ?

Jerry

Posted on 5/21/2009 @ 11:17 AM
Dear God, why? Vanilla ASP.Net == less code X 1,000!

Lipps

Posted on 5/26/2009 @ 12:37 AM
Awesome thanks for posting, there is a lot of logic in your ocntroller though but I'm guessing that was to keep the example straight forward!! Thanks for the post.

Lipps

Posted on 5/26/2009 @ 12:38 AM
@Jerry are you fucking kidding me?? LOL!!!

Why? to keep your job that's why... better get cracking, buy a book and stop being so ignorant! Or at least don't waste our bandwidth with stupid comments like that... cheers!

Lipps

Posted on 5/26/2009 @ 12:39 AM
@all the people having ajax issues, use jquery... this is just an example, he can't write ALL your code and think for you? do your own homework.. yeesh
The discussion on this post has been closed. Please use my contact form to provide comments.