Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
385 views
in Technique[技术] by (71.8m points)

jquery - Return Partial View and JSON from ASP.NET MVC Action

I'm introducing KnockoutJS into an existing app. My plan is to modify/utilize the existing partial views that we've already created and bind them to the JS view models with Knockout's declarative attributes. When I make an AJAX call to an action, ideally I'd like the action to return both the HTML of the partial view and JSON object. Then I can fill a div with the HTML, convert the JSON to a Knockout object and bind it to the HTML. But I can't figure out how to return both from the action.

I need the full view model because I'll be updating it and eventually sending it back to the server.

I thought about having the action return the partial view (already bound to the model), and within the partial view, include javascript to convert the .Net model into a Knockout object. But I feel that scattering the JS around like that is messy and unmaintainable. I'd rather have everything close to the original ajax call.

I guess another alternative is to make two action calls. One for the JSON, and another for the partial view. But there has to be a slicker way.

Any ideas on how best to do this?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I'm sure there are a variety of ways to do this. I manually render the view from the controller, and then pass the rendered view back as part of my JSON response.

This preserves the responsibilities of each entity. Views are still located using the view engine and they can be reused. The controller knows little or nothing about the view beyond its name and model type.

Manual Rendering

public static class RenderHelper
{
    public static string PartialView( Controller controller, string viewName, object model )
    {
        controller.ViewData.Model = model;

        using( var sw = new StringWriter() )
        {
            var viewResult = ViewEngines.Engines.FindPartialView( controller.ControllerContext, viewName );
            var viewContext = new ViewContext( controller.ControllerContext, viewResult.View, controller.ViewData, controller.TempData, sw );

            viewResult.View.Render( viewContext, sw );
            viewResult.ViewEngine.ReleaseView( controller.ControllerContext, viewResult.View );

            return sw.ToString();
        }
    }
}

In your action method:

object model = null; // whatever you want
var obj = new { 
    someOtherProperty = "hello", 
    view = RenderHelper.PartialView( this, "_PartialName", model ) 
};

return Json( obj );

Note that I'm returning an anonymous type. You can return any (serializable) type you want, as long as it has a string property for the rendered view.

Testing

Testing an action that uses manual rendering requires a slight modification. This is due to rendering the view a bit earlier than it would be rendered in the MVC pipeline.

Manual Rendering

  1. Enter action method
  2. Render view explicitly <-- this will make it difficult to test the calling action
  3. Exit action method

Automatic Rendering

  1. Enter action method
  2. Create a view result
  3. Exit action method
  4. Process view result (thus rendering the view)

In other words, our manual rendering process kicks off a variety of other operations that make it difficult to test (such as interacting with the build manager to compile the view).

Assuming you wish to test the action method and not the actual contents of the view, you can check whether or not the code is executing in a hosted environment.

    public static string PartialView( Controller controller, string viewName, object model )
    {
        // returns false from a VS 2013 unit test, true from IIS
        if( !HostingEnvironment.IsHosted )
        {
            // return whatever you want here
            return string.Empty;
        }

        // continue as usual
     }

Checking HostingEnvironment.IsHosted is inexpensive (under the hood, it is simply a null check).


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...