2014. január 29., szerda

Returning more views in an ASP.NET MVC action

Demo and download: pulzonic.com/multipartial
Nuget package: www.nuget.org/packages/Multipartial

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:


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:

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

or, you can use it in an Ajax form:

@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:
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:

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:


public override void ExecuteResult(ControllerContext context)
        {
            List data = new List();
            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:

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. :)

6 megjegyzés:

  1. Play at 1XBET, oneXBET Casino, Sportbet, Positi-Deposit
    Online 1XBET 1XBET Casino, Sportbet, Positi-Deposit, SBOBET, leovegas XBet, OVO, Pulsa.

    VálaszTörlés
  2. Gambling in California: Legal And Illegal - Dr.MCD
    Is Gambling 통영 출장샵 Legal In California? 진주 출장안마 Yes, legal Gambling is legal 아산 출장샵 in California. California's gambling laws are 전주 출장샵 in 남원 출장안마 full force. Here's what is

    VálaszTörlés
  3. MGM National Harbor Casino, Atlantic City - Dr.MCD
    MGM National Harbor Casino, Atlantic City, NJ. MGM National Harbor 광명 출장안마 Casino, Atlantic 인천광역 출장샵 City, 전라북도 출장마사지 NJ. MGM National Harbor Casino, Atlantic City, NJ. MGM National Harbor 문경 출장마사지 Casino 원주 출장샵

    VálaszTörlés
  4. Casino Finder (Google Play) Reviews & Demos - Go
    Check Casino Finder good jordan 18 white royal blue (Google air jordan 18 retro super Play). A look at some of the best gambling sites 피망 포커 현금화 in the air jordan 18 retro toro mens sneakers outlet world. They air jordan 18 retro varsity red cheap offer a full game library,

    VálaszTörlés
  5. For about two years he had a steady 토토사이트 life, dwelling off public help, playing infrequently, and half in} the occasional lottery ticket. Then Kane known as to inform him a couple of bug he'd found in video poker. Nestor drove to the airport that evening and camped there till the following out there flight to Las Vegas.

    VálaszTörlés
  6. However, it is not clear how reliable these strengthening results are, and in the event that they} generalize to adolescents. However, it isn't clear how this relationship could generalize to adolescent 토토사이트 populations. Adolescents, as a bunch, are notably threat of|susceptible to|vulnerable to} developing downside playing.

    VálaszTörlés