Click here to Skip to main content
15,885,141 members
Articles / Web Development / ASP.NET
Tip/Trick

Returning More Views in an ASP.NET MVC Action

Rate me:
Please Sign up or sign in to vote.
4.77/5 (9 votes)
18 Jan 2014CPOL3 min read 60.7K   975   15   6
This small code enables an MVC application to update many parts of the HTML in one request-response roundtrip.

Introduction

Recently, we have had a web-based project on which a very few of us (developers) worked. One of the most important goals was to create a complex UI like Facebook, but we didn't want to build a complicated, heavy weight client-side code, so we decided to minimize the amount of JavaScript code, and to render the HTML content on the server-side instead. My plan was to slice the page into many, nested, sometimes very small (like a single line in a listbox) partial views, and transfer the final HTML snippets to the client side, using very simple JS .ajax functions, and doing most of the work on the server-side.

Pretty soon, I understood that the built-in ActionResults of the MVC framework aren't suitable for this at all, so I have created an own solution.

The Solution 

I have created a new ActionResult class, which allows an ASP.NET MVC application to update more, unrelated parts of the HTML DOM, in a single request-response roundtrip. The aim of this is to render more partial views on the client side, so it's called MultipartialResult.

For example, you are working on a web-based email reader. When the user selects an email and then clicks on the Delete button, several things should happen at the same time:

  • the current email should be removed from the email listbox, and the next email should be selected
  • the preview pane should be updated by the content of the newly selected email
  • the indicator that shows the number of the emails in the inbox should be updated
  • the browser title (since it also shows the number of the unread emails) should be updated

In my case, the email list is a partial view, so is the preview pane, and the part that contains the number of the emails is a simple text in the DOM.

With the MultipartialResult, the action that handles the Delete click looks like this:

C#
public ActionResult OnDelete(long EmailId)
{
     //... does the deleting
     //... creates the model for the new InboxList partial view (InboxListModel)
     //... creates the model for the PreviewPane partial view (PreviewPaneModel)
     //... calculates the number of the emails (EmailCount)
     //... renders the browser title with the updated unread email number (BrowserTitle)
    
     MultipartialResult result = new MultipartialResult();
     result.AddView("_InboxList","InboxListDiv",InboxListModel);
     result.AddView("_PreviewPane","PreviewDiv",PreviewPaneModel);
     result.AddContent(EmailCount.ToString(),"EmailCountDiv");
     result.AddScript(string.Format("document.title='{0}';",BrowserTitle));
     return result;
}

The AddView function will cause the "InboxListDiv" HTML element to be updated with the _InboxList view.
The AddContent function will cause the content of the "EmailCountDiv" HTML element to be updated with the given string.
The AddScripts function will cause the given JavaScript code to be executed on the client side.

In the event-handler on the client side (which works without page refresh of course), the only thing you have to do is to call the MultipartialUpdate in the OnSuccess JavaScript event:

ASP.NET
@Ajax.ActionLink("Delete", "OnDelete", new { EmailId = Model.CurrentEmail.Id }, new AjaxOptions { OnSuccess = "MultipartialUpdate" }) 

or, you can use it in an Ajax form:

ASP.NET
@using (Ajax.BeginForm("OnDelete", 
 new { EmailId = Model.CurrentEmail.Id }, new AjaxOptions { OnSuccess = "MultipartialUpdate" }))

or, you can use it in a jQuery .post or .ajax function

JavaScript
function deleteClicked(emailId) { 
    $.ajax({
        url: "/inbox/ondelete",
        type: "POST",
        data: { emailId: emailId },
        success: function (result) {
            MultipartialUpdate(result);
        },
    }); 

Background

The concept of the MultiPartial is very simple. It is inherited from the JsonResult class, it renders the specified elements into strings, collects those strings, packs them into a json data, and sends this json to the client side.

On the client side, a small JavaScript function iterates through those strings, and updates the DOM respectively.

When collecting the different kind of results, the MultipartialResult flags them properly by content:

C#
public MultipartialResult AddView(string viewName, string containerId, object model = null)
{ 
        views.Add(new View() { Kind = ViewKind.View, 
        ViewName = viewName, ContainerId = containerId, Model = model });
  return this;
}
public MultipartialResult AddContent(string content, string containerId)
{
  views.Add(new View() { Kind = ViewKind.Content, Content = content, ContainerId = containerId });
  return this;
} 
public MultipartialResult AddScript(string script)
{ 
  views.Add(new View() { Kind = ViewKind.Script, Script = script });
  return this;
}

After the action returns the Result object, the MVC framework calls the ExecuteResult function, which produces a json string by processing the elements one by one:

C#
public override void ExecuteResult(ControllerContext context)
{
    List<object> data = new List<object>();
    foreach (var view in views)
    {
        string html = string.Empty;
        if (view.Kind == ViewKind.View)
        {
            //view result
            html = RenderPartialViewToString(mController, view.ViewName, view.Model);
            data.Add(new { updateTargetId = view.ContainerId, html = html });
        }
        else if (view.Kind == ViewKind.Content)
        {
            //content result
            html = view.Content;
            data.Add(new { updateTargetId = view.ContainerId, html = html });
        }
        else if (view.Kind == ViewKind.Script)
        {
                //script result
            data.Add(new { script = view.Script });
        }
    }
    Data = data;
    base.ExecuteResult(context);
}

Note that rendering the partial view into string is not the subject of this tip.

On the client side, the only thing to do is to iterate through the json, and for all the elements update the DOM, or run the script:

JavaScript
function MultipartialUpdate(views) {
    for (v in views)
        if (views[v].script) {
            eval(views[v].script);
        }
        else {
            $('#' + views[v].updateTargetId).html(views[v].html);
        }
    return false;
}

I hope there is someone out there who can benefit from this. :)

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Hungary Hungary
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionVery helpful Pin
Member 1251436810-May-16 1:58
Member 1251436810-May-16 1:58 
QuestionGreat Work! Pin
milad38615-Apr-15 1:13
milad38615-Apr-15 1:13 
QuestionThanks for sharing! Pin
Dhaval Joshi28-Jan-14 9:49
Dhaval Joshi28-Jan-14 9:49 
GeneralExcellent Article Pin
Ashes In Snow20-Jan-14 22:47
professionalAshes In Snow20-Jan-14 22:47 
QuestionAWESOME Pin
John Salichos19-Jan-14 6:25
John Salichos19-Jan-14 6:25 
AnswerRe: AWESOME Pin
Luxi19-Jan-14 20:40
Luxi19-Jan-14 20:40 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.