How to create an ActionController to work both at runtime and with ajax

I have an AddressBook controller that will return a list of "folders" (mostly groups / locations). This can be invoked through an AJAX request or on the MVC page itself during rendering.

How to create a function that will work well for both scenarios? Here is my current Controller action, which I seem to be struggling with using MVC on my page

public ActionResult GetFolderList(int? parent) { List<String> folderList = new List<String>(); folderList.Add("East Midlands"); folderList.Add("West Midlands"); folderList.Add("South West"); folderList.Add("North East"); folderList.Add("North West"); return Json(folderList); } 

Inside the page (atm does not work)

 @{ var controller = new My.Controllers.AddressBookController(); var what = controller.GetFolderList(0); foreach(var p in what){ //i want to get the list items here } } 
+8
json c # asp.net-mvc razor asp.net-mvc-4
source share
4 answers

Just execute a function that returns a List , and then call it both on page loading and in the AJAX action method.

Something like:

 public List<string> GetFolderList() { List<String> folderList = new List<String>(); folderList.Add("East Midlands"); folderList.Add("West Midlands"); folderList.Add("South West"); folderList.Add("North East"); folderList.Add("North West"); return folderList; } 

Then, when loading the page, you can stick to this in your model:

 public ActionResult Index() { var model = new YourViewModel(); //whatever type the model is model.FolderList = GetFolderList(); //have a List<string> called FolderList on your model return View(model); //send model to your view } 

Then, in your opinion, you can:

 @model YourViewModel @{ foreach(var item in Model.FolderList){ //do whatever you want } } 

Then, if your ajax request was something like this:

 $.ajax({ url: '@Url.Action("GetFolders", "ControllerName")', type: 'POST', datatype: 'json', success: function (result) { for (var i = 0; i < result.length; i++) { //do whatever with result[i] } } }); 

Your GetFolders action GetFolders will look like this:

 public ActionResult GetFolders() { return Json(GetFolderList()); } 
+9
source share

This will work:

 public ActionResult GetFolderList(int? parent) { List<String> folderList = new List<String>(); folderList.Add("East Midlands"); folderList.Add("West Midlands"); folderList.Add("South West"); folderList.Add("North East"); folderList.Add("North West"); if(Request.IsAjaxRequest()) { return Json(folderList); } return View("someView", folderList ); } 
+5
source share

First of all, you should never do something like this in your view:

 var controller = new My.Controllers.AddressBookController(); var what = controller.GetFolderList(0); 

This creates a tight connection between your view and the controller, which greatly violates the principles of MVC. Now to answer your questions.

As stated in mattytomo, you'll want to use a strongly typed view and get your list from the view model. Something like below will work for a simple case. If this view becomes more complex, you will need the actual object of the view model:

 @model List<string> @{ foreach (var p in Model) { p; } } 

Now you can use one controller method for AJAX or a regular request along with Request.IsAjaxRequest, as Maris pointed out. Assuming your view is called a "FolderList", the controller action will look like this:

  public ActionResult GetFolderList(int? parent) { List<String> folderList = new List<String>(); folderList.Add("East Midlands"); folderList.Add("West Midlands"); folderList.Add("South West"); folderList.Add("North East"); folderList.Add("North West"); if (Request.IsAjaxRequest()) { return Json(folderList); } return View("FolderList", folderList); } 

Now, when you call this method through AJAX, it returns the JSON representation of the List list, otherwise it will return the FolderList view.

+2
source share

I once wrote an ActionFilter that does just that, it overrides ActionResult with JsonResult , as soon as the Accept http header contains json (and can limit itself only to Ajax requests, passing onAjaxOnly with true ):

 public class ReturnJsonIfAcceptedAttribute : ActionFilterAttribute { private bool _onAjaxOnly; private bool _allowJsonOnGet; public ReturnJsonIfAcceptedAttribute(bool onAjaxOnly = true, bool allowJsonOnGet = false) { _onAjaxOnly = onAjaxOnly; _allowJsonOnGet = allowJsonOnGet; } public override void OnResultExecuting(ResultExecutingContext filterContext) { var request = filterContext.HttpContext.Request; if (!_allowJsonOnGet && request.HttpMethod.ToUpper() == "GET") return; var isAjax = !_onAjaxOnly || request.IsAjaxRequest(); if (isAjax && request.AcceptTypes.Contains("json", StringComparer.OrdinalIgnoreCase)) { var viewResult = filterContext.Result as ViewResult; if (viewResult == null) return; var jsonResult = new JsonResult(); jsonResult.Data = viewResult.Model; filterContext.Result = jsonResult; } } 

Then you save your Action as it is, you just add a new ReturnJsonIfAccepted Attribute :

 [ReturnJsonIfAccepted] public ActionResult Index() { var model = new Model(); // whatever return View(model); } 
0
source share

All Articles