There seem to be several ways to integrate jQuery dialogs with ASP.NET MVC. Has a specific approach emerged as the commonly-accepted best-practice way to do so?
As an example: I have a list page where clicking "edit" for any of the listed items opens a form in a jQuery dialog, populated with the item's details. User edits details and clicks "save". If the save succeeds on the server-side, the dialog is closed and the list is re-built with fresh data. If the save fails on the server-side, the dialog remains open and displays the error message(s) to the user.
No-JSON approach: each "edit" link is an HREF to an "edit" controller action. That controller action builds a view that is identical to the "list" view, plus it includes a partial action to build the edit form, populate it, and define the javascript to pop it open as a jquery dialog. The "save" is a form-post; if it succeeds, it returns a redirect action back to the list page. If it fails, it rebuilds the entire page (including the form that pops up in a dialog) displaying the error messages as well.
All-JSON approach: the list page renders an empty edit form (hidden), ready to be popped open into a dialog. The "edit" link calls local javascript which does an ajax request to get the full object (I define a controller which returns the full object as a JsonResult). On success, it fills the edit form with the object's data and opens the dialog. The "save" link calls local javascript which bundles the form data into a json object, and calls a post operation with that json object as payload (I define a controller which expects that object, attempts the save, and returns a JsonResult indicating success/failure+errorMessages). Success callback from the ajax request evaluates the returned object, and either shows the error messages in the still-open jquery dialog, or closes the dialog and reloads the "list" page to get fresh data in the list.
[Edit] Ajax-HTML approach: just saw this SO discussion which describes another approach. The "edit" calls local javascript which does an ajax post to get the FULL HTML of the dialog (I would write a controller which returns a partial view: the fully-populated form). It renders the returned HTML into a jquery dialog, and also "re-wires" the form submission to do an ajax-post of the form's contents (I would write an httpPost controller, same as in #2 above). The success callback evaluates the response and populates error messages or closes the dialog.
Some other cool approach I haven't thought of?
Option 1 seems to be more in keeping with "pure" ASP.NET MVC. However, it seems to feature big HTTP payloads (since we're shipping the full page back to the browser on every request), and slower server-side performance (since we're re-building the list on every request).
Option 2 seems to be more consistent with more modern Ajax-based web applications (smaller HTTP payloads, more granular operations). However, it seems like many of the controllers will be JSON controllers, and I'll be writing a lot of client-side code to marshal data from JSON objects to/from form fields, display error messages, etc. It also seems like I'd be missing out on a lot of cool MVC features like EditorFor() and ValidationMessageFor(). It just "feels" like I'm working around the MVC system instead of with it.
[Edit: added option 3] Option 3 seems to be a bit of a hybrid between 1 and 2. I use the "pure" MVC approach to build and populate the form, and return a fully-formed HTML FORM tag. Returning HTML to an ajax request feels weird since it's so verbose, but I can get over it. The post operation is nice, compact JSON which "feels" better over ajax. However, it's unfortunate that the payload object is a FormCollection rather than a real viewmodel object. It seems like I can make use of some of the MVC conveniences (EditorFor()) but not others (ValidationMessageFor()).
I'm looking for the "right" way to do this, not just the fastest way to hack it together. Yeah, yeah, I know there's no universally "right" way. But I'm sure there's some notably wrong ways to do it, and I want to avoid them.
I'm pretty experienced in ASP.NET/C#, but I'm pretty new to MVC. Thanks in advance for your help!
[Edit] - outstanding responses - I wish I could award multiple answers/bounties, as I found several responses extremely useful. But since I can't, I'm marking the top-voted response as the answer. Thanks again to all respondents!
My team and I have a lot of experience writing AJAX enabled MVC apps, and we have used all 3 approaches.
However, my favorite is definitely the AJAX-HTML approach -- Use a PartialView to render the contents of the dialog, which could include server-side validation messages and any other logic.
The biggest benefit of this approach is the separation of concerns - your Views are always responsible for rendering your HTML, and your JavaScript doesn't have to contain any text, markup, or "templates" needed to render the JSON.
Another big advantage is that all the great MVC features are available for rendering the HTML: strongly-typed views, HtmlHelper, DisplayFor and EditorFor templates, DataAnnotations, etc. This makes it easier to be consistent and lends well to refactoring.
Just remember, there's no requirement to stick to a single approach. When your AJAX call only needs something simple, such as a status update like "Success", it's fine to just use a string or JSON to convey those messages. Use PartialViews when HTML is needed, and use simpler methods when communication is needed.
Your 2nd method, the All-JSON approach, seems to be increasingly prevalent with MVC and MVVM client side libraries like Knockout
In this you could actually have all of the data in JSON (including the list) and edit list items (similar to their list item editor demo, just with a dialog rendering isntead of inline, and bind the data to readonly spans in your cells) and then serialize the entirety of the set back to the server on save. Or you could do it with piece-meal saves after each popup edit.
JSFiddle: http://jsfiddle.net/paultyng/weLtH/17/
The JS could be cleaned up a bit, I didn't include a save button, but you should get the idea. The edit dialog could be a single template bound to one edit as well, instead of doing per row, this was just the simplest way to do it with Knockout.
I think the best way would be to render the list normally. Hook up the edit links to go to a separate page (follow me here) like you would normally do.
With JS handle clicking of the link, and do a get to it's href. In the edit action do a check for Request.IsAjaxRequest() and if it is, return a partial view if it isn't, return the full view. Or render the normal edit view without the master page (pass in null to the master page parameter in the View() call or call return Partial()). Take the contents of the result and put it into a dialog.
Also use JS to handle submitting the form and getting the result from the request. If it wasn't successful insert the contents of the view into the dialog to show that there were errors. Otherwise close it and move on.
The benefit to this approach is it's very unobtrusive and still allows functionality for those who don't have JS.
Ok, so your options are pretty much torching the concept of progressive enhancement here. 2 & 3 aren't going to work if your client doesn't support java script. Obviously thats ok if you don't care, but I think I'd try to engineer things so that they degrade gracefully and you are asking for best practise here.
So the way I would construct it, starts with your option 1. You have edit buttons that trigger another action which loads an edit page, and this page is designed with all the validators etc as per normal mvc. That's your base functionality so works with no js.
So then the next question is how do we progressively enhance this to have a nice popup instead of a new page?
Well first step is to create a handler to open your dialog attached to the edit links on click (make sure to e.PreventDefault). Now to save too much coding effort I would be looking to reuse the edit page. This is going to require a bit of refactoring though as you don't want to include the layout for the ajax requests. You can do this a few ways, but I think the cleanest is to have the edit area of the edit view as a partial view which the main edit view uses to render its model.
Then, in your edit action, you can check if you have an ajax request, if so then return a PartialView(mypartialeditview) or a View(editview) if not.
In terms of then submitting the results back to the server if you want an easy life just treat it like a form. You can use the micorsoft unobstrive ajax here and it will be very simple. You use Ajax.BeginForm in your partial view. n.b.This will degrade to a normal form if ajax not available. Have the AjaxOptions for this beginform set to update the div in the dialogue so if it responds with its html you don't have to do anything more, it implies a validation error.
[small aside as you asked above about this: In terms of the HttpPost handler, the default model model binder is amazingly smart, it can bind form fields to properties on a complex class object parameter. This also work with json so you don't have to end up with lots of action methods to support different scenarios.]
So if your update is unsucessful the post handler will return the partial view again, bound to the model so you'll get all your validators. If however the update is successful I would suggest that rather than return anything, the action does a redirect back to your main page which you want to reload anyway as you've changed the underlying.
If you don't like doing a full reload of the main page, then it gets more complex as with the approach above you are returning html. You will either have to jquery over that to find a hidden field or class to indicate success/failure or migrate to a pure json approach which returns a jsonresult. This is getting heavier on the maintenance/coding front though. I'd probably do the jquery check and have it wired to the completion handler of the ajax.Beginform.
If you really want to get your head around this stuff, I have found the Steve Sanderson Pro Asp.net MVC book invaluable. The one I initially read was for MVC2, but just in process of reading the MVC3 update. I have mixed feelings on the update, as its been simplified in a few places - easier to follow now but I feel like a few things are missing, also it was rushed as some errors etc as it gets near the end. I guess maybe they panicked now that MVC4 is being talked about and book wasn't out! Still a good book though and it covers all this stuff beautifully.
Hope this helps. Appreciate it covers some of the same ground as an answer above but hope I've drilled in more for you.
Using jQuery.tmpl() plugin1
I use a similar scenario but do it in a different way.
I have a master list where each item is wrapped in a container (be it table TR or DIV). Some general information about the item is displayed in the master list when there's too much data to contain in a simple list. Hence a master list and not details (of course).
Each item container is usually written this way:
<div class="item" data='<%= item.ToJson() %>'> <!-- single quotes! -->
item visible list data
</div>
The main part here is my custom extension method ToJson() that serializes my item data which can easily be used on the client.
Then I have a jQuery template at the end of a list contained inside a script tag. This template is the actual content of the edit dialog with all item variables set as needed.
Whenever user clicks edit on the item, I simply parse item JSON by:
// in edit link click handler
var itemData = $.parseJSON($(this).closest("[data]").attr("data"));
// display dialog
displayDialog($("#editDialog").tmpl(itemData));
My dialog than takes care of the rest via Ajax calls and closing the dialog when calls are successful or user cancels editing.
This keeps my HTML code to minimum (having only one template) and JSON also contains only those properties that are actually needed.
What to do when your model class has other entity references?
This is quite common that entities are related between each other. Suppose you have a Post and Comment entities:
public class Post
{
public int Id { get; set; }
public string Body { get; set; }
public IList<Comment> Comments { get; set; }
}
Of course you're not interested in related entities when converting your items to JSON on the server. That's why you can put an attribute to related properties so they don't get included in the JSON:
public class Post
{
public int Id { get; set; }
public string Body { get; set; }
[ScriptIgnore]
public IList<Comment> Comments { get; set; }
}
This will make JSON serializer ignore the related property we won't edit in our details editor.
ToJson() extension code
The last thing to put here is extension method code so here it goes:
public static class ObjectExtensions
{
/// <summary>
/// Serializes this object instance to JSON string.
/// </summary>
/// <param name="instance">Object instance being extended.</param>
/// <returns>Returns a JSON string that represents current object instance.</returns>
public static string ToJson(this object instance)
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
// register custom class converters
serializer.RegisterConverters(...);
return serializer.Serialize(instance);
}
/// <summary>
/// Serializes this object instance to JSON string.
/// </summary>
/// <param name="instance">Object instance being extended.</param>
/// <param name="recursionDepth">Serialization recursion limit depth.</param>
/// <returns>Returns a JSON string that represents current object instance.</returns>
public static string ToJson(this object instance, int recursionDepth)
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
// register custom class converters
serializer.RegisterConverters(...);
serializer.RecursionLimit = recursionDepth;
return serializer.Serialize(instance);
}
}
What to do when entities are too complex?
Upper approach is useful when your entities aren't too complex. In cases when:
your entities are complex or have long data (long in terms of serialized string)
determining JSON objects for all items would be too time consuming
unknown JSON object at time of master list display
then you can always send a remote Ajax call to server for a single entity and return either
JSON object to use with dialog template
return partial view with editor
Second approach is better, since converting object instance to either HTML or JSON is practically similar task. but in the second case there's no need for client-side template processing.
But don't use remote requests just because you can. Use an approach that's easier and better for the problem at hand.
1: jQuery template API reference
Related
I am sure many people have had this question but I failed to get any results after searching the web and SO or the search keywords were different.
I am working on a new asp.net mvc web app where I get a plain template returned by the index action method on the controller. Later in the document.ready event handler I build the ui dynamically and append the dom elements to the blank template and this just works fine. My issue is I need 2 server calls here,
1) to get the view from the index action method
2) an ajax call inside the document.ready{} to get the data using which I build the ui.
I was wondering if there is any method using which I could pass back the data from the index action method along with the blank template view and use this data to create the ui inside the document.ready event handler. This will save that one additional hit to the server.
The reason for not using partial views is
1) we have some functionality already developed in jquery and
2) in my org people think making the functionality using razor and partial view will not be as flexible, for example building and raising customevent in js is a great feature that helps to keep the functionality loosely coupled from other features. (please correct if we are wrong)
Edit: I thought an example will explain this better,
Say I need to create a list of users, but the entire list and its functionality like checkboxes selection etc are built by a js module. So along with the blank view i want to pass the "users" object which is a class in my models currently.
Kindly suggest.
You have a couple of options:
1) Server-side rendering:
Putting the necessary data into the model would seem to be the obvious thing to do...that's what MVC models are for.
During the building of the HTML your View code runs - so you can access the model values in Razor code, which you can use to build your view and influence the final HTML. So in this scenario you build your view using Razor, rather than constructing it using JS code. You can of course still use JS to change it after the page has loaded, but it would be downloaded into the browser with the HTML already in the desired starting state.
2) Client-side rendering, but with necessary data pre-populated:
If you'd rather stick with your existing client-side rendering code, you could use Razor to inject some ready-made JSON into the JavaScript, so it's effectively hard-coded into the page when it first runs, rather than having to fetch it from the server separately via AJAX.
For example, if you have some object in C# which holds the data, you can serialise it to a JSON string and then use Razor to write that string into your JS in the correct place.
I have a MVC view that has many fields, input, checkboxes, select lists etc.
When the user makes a choice in any field I use jQuery to trigger a change event that picks the values in all fields and make a (ajax) post to a ActionResult method (with all the values) which makes a database query and at last a search result is returned.
The values from the html fields are put back into the fields so that the user may continue narrowing down his search.
I find it very error-prone and cumbersome to use JQuery for this. I have taken a look at Angular and React, but these seems bloated if I only need it for above task.
I'm open towards frameworks, nuget packages, programming languages, as long as it fits well with above explanation.
Thanks
This doesn't quite feel right as an approach- if you are posting to .NET with AJAX, why not post to a WEBAPI controller, and get back a Json results set - deal with everything in JavaScript (jQuery). However, I'd still keep the form element wrapper and use that to post to the ActionResult MVC controller method to return a View for no-JS scenarios.
I am creating a ASP.NET Web Form application where I am trying to use some nice jQuery functionality and flashyness. The current part of the application I have consists of two jQuery UI tabs; a search tab and a results tab. When the user performs a search from the search page, the results tab will be selected and the results will be displayed in this tab. I need to get the results into a gridview. Now this is where the issue starts to come in:
The easiest way to get the search results is to allow the search click to perform the postback where I can then format the datasource with the parameters from the input fields and let the datagrid take care of itself and data bind and show the results. The thing is, this really doesn't look that great (due to the whole post back and such) as well as starts to cause some issues with using javascript/jQuery to take care of tab switching and all that portion because the postback reinitializes everything from the jQuery UI (i.e. the jQuery UI tabs). So in short, the postback allows for easy binding of the input for the search and getting the results, but makes the page and its behavior all wonky.
I was wondering if there is a standard way to do this type of mixing jQuery/javascript/AJAX all together within web formto get the functionality of things like the gridview and such. I am wondering if there are some good tutorials, or even just a direction on solving this issue.
I hope all this made sense, and thank you all for your help.
I don't think this is a standard, but here is the pattern I use:
First of all, I use Page Methods for ASP.Net to get hooked back up to the server. In this case it would be something like this:
PageMethods.Search(searchValue, onSearchComplete);
That calls a static page method in the page, like this:
public static void Search(string searchValue)
...
Inside that procedure, I create an instance of a user control which contains the gridview, and invoke a method on that control, passing the searchValue:
var searchControl = (SearchControl)new SearchControl().LoadControl("/controls/SearchControl.ascx");
searchControl.Search();
var stringBuilder = new StringBuilder();
using (var textWriter = new StringWriter(stringBuilder))
{
var htmlWriter = new HtmlTextWriter(textWriter);
searchControl.RenderControl(htmlWriter);
return stringBuilder.ToString();
}
This is all going to end up as the result argument to the handler you specified in the initial call (onSearchComplete) in this example. You can do whatever you want with the markup, including slapping it into a div, or alerting it for debugging.
What is the difference between the controller result named ViewResult and PartialViewResult? More importantly, when is the PartialViewResult used?
PartialViewResult is used to render a partialview (fx. just a user control). This is pretty nifty for AJAX stuff, i.e.
<script type="text/javascript">
$.get(
"/MyController/MyAction",
null,
function (data) { $("#target").html(data) }
);
</script>
and action
public ActionResult MyAction()
{
return PartialView("SomeView");
}
where SomeView is a MVC User Control, e.g.:
<div>
<%= DateTime.Now.ToString() %>
</div>
http://msmvps.com/blogs/luisabreu/archive/2008/09/16/the-mvc-platform-action-result-views.aspx
In practice, you’ll use the
PartialViewResult for outputing a
small part of a view. That’s why you
don’t have the master page options
when dealing with them. On the other
hand, you’ll use the ViewResult for
getting a “complete” view. As you
might expect, the Controller class
exposes several methods that will let
you reduce the ammount of typing
needed for instanting these types of
action results.
Generally speaking, ViewResult is for rendering a page with optional master, and PartialViewResult is used for user controls (likely responding to an AJAX request).
none of the existing answers actually answer the question "What is the difference".
The differences are as follows:
1) the locations where the view engine will attempt to find the view:
for ViewResult, it's in ViewLocationFormats and MasterLocationFormats
for PartialViewResult, it's in PartialViewLocationFormats
2) ViewResult has the additional property MasterName
that is all.
There are several cases where you would want to break down your view into several small components. One use case that I am working with right now, is I have a multi-lingual site that I would like to reload content using AJAX principles.
Normally what I would do in the case of a non-multi lingual site is to create another ActionResult to return the ViewModel that is changing with the new parameters. I like to use a custom ActionResult that I have called JsonpResult. The problem resides in the fact that I have labels not in my database but in Resource files. So what I would need to do is to somehow hydrate my Resource file data into the ViewModel.
Once the data comes down the pipe, my AJAX callback handles the wiring up of the ViewModel response back to the HTML page using Javascript (I use jQuery).
This definitely works, however it becomes a question of maintainability. I now need to not only maintain my original ASP.NET view, but I also need to maintain a set of scripts that handle AJAXian behavior. If you need to have your site SEO, then you really need to make sure that both the Server Side and Client Side behavior are both working the same.
This is where Partial Views come into play for me. What I do is "pull out" the logical data sections where the bulk of the reload occurs. The nice thing about PartialView is that you can pass your ViewData and Model along to the PartialView. If your PartialView is strongly typed against your ViewModel you can get Intellisense to help with the wiring of the PartialView.
Now all I need to do with my AJAX call is to write the response back to a single DIV rather than handling data points individually. What it does mean is that there would be more content coming down the pipe. However, the trade off is easier to read and maintain code.
The one of the main differences is PartialViewResult doesn't use _ViewStart.cshtml. The code from _ViewStart.cshtml file executes at the start of rendering before any code in the view.
I am working on an ASP.NET MVC application that contains a header and menu on each page. The menu and header are dynamic. In other words, the menu items and header information are determined at runtime.
My initial thought is to build a base Controller from which all other controllers derive. In the base controller, I will obtain the menu and header data and insert the required information into the ViewData. Finally, I will use a ViewUserControl to display the header and menu through a master page template.
So, I'm trying to determine the best practice for building such functionality. Also, if this is the recommended approach, which method should I override (I'm guessing Execute) when obtaining the data for insertion into the ViewData.
I'm sure this is a common scenario, so any advice/best-practices would be appreciated! Thanks in advance!
EDIT:
I did find the following resources after posting this (of course), but any additional anecdotes would be awesome!
http://www.singingeels.com/Blogs/Nullable/2008/08/14/How_to_Handle_Side_Content_in_ASPNET_MVC.aspx
How do you use usercontrols in asp.net mvc that display an "island" of data?
Depends on where your information is coming from. We have standard view data that we use to generate some of the information we have on screen that we create in just this fashion. It works well and is easily maintained. We override the View method to implement strongly typed view names and use this information to retrieve some of the data that the master page requires as well.
You could write a helper extension to render the header/menu
That way you could have it show in different places in the view should you need to, but only one place for maintenance.
public static HtmlString MainMenu(this HtmlHelper helper)
Use a base controller class to implement generell filter methods. The controller class implements some filter interfaces IActionFilter, IAuthorizationFilter, IExceptionFilter and IResultFilter which are usefull to implement some common behavior for all controllers.
If the menu data is the same on all pages but different for each unique user.
Generate the menudata in an OnAuthorization or Initialize method of your controller base class. First will be called on authorization. Initialize will be called before every action method. You have access to ViewData Context. Generate the menudata there.
Put the view content for menu and header into the master page and access generated ViewData there.
I tackled a similar design challenge a couple months ago - implementing a breadcrumb feature that changes as user navigates from page to page.
I overrided the OnActionExecuting method to gather the breadcrumbs and store them in ViewData (I use the name of the action as the breadCrumb of the view). Then I updated the Master page to include a user control that takes the ViewData and renders the breadcrumbs.
One thing to be aware is that if you were using the default ASP.NET MVC error handling attribute [HandleError] and your error page is using the same Master page that attempts to read the ViewData, you will soon find out that you can't access ViewData from your error page and it will raise an exception. Depending on whether you need the ViewData for failure scenarios, the viable solution is to use a separate Master page or do this: How do I pass ViewData to a HandleError View?
I'll answer your question with another question. Will the base controller have to determine what type it really is in order to generate the proper menu data? If so, then you're defeating the purpose of polymorphism and the code to generate the data should go in each controller, perhaps in OnActionExecuting if the menu is the same for all actions. Pushing it back down into a parent class seems likely to end up with some switch statement in the parent class doing what each derived controller really ought to take care of.