I find myself needing to return various JSON results to the client from my controller actions. I have created static "helper" methods of the type ContentResult. I want to be able to reuse these helpers in multiple projects, so I've created them in their own class, and someday soon I'll move them to their own assembly.
Here is my question: Some of these return helpers need access to the controller context, so I've create then as extension methods on Controller. I like the way Html Helper methods are extension methods on the HtmlHelper class, but I can't find a suitable controller property similar the the Html property on the View class, so I just extended controller. Below is the code from on return helper. I want to know if there is a better way to do this.
public static ContentResult AJAXRedirect(this Controller cont, string action,
string controller, object routeValues)
{
string url = cont.Url.Action(action, controller, routeValues);
string json = "{{\"redirect\": \"{0}\" }}";
json = string.Format(json, url);
return new ContentResult
{
Content = json,
ContentType = "text/x-json",
ContentEncoding = Encoding.UTF8
};
}
You can use this code:
string url = cont.Url.Action(action, controller, routeValues);
return Json(new { redirect : url });
Extending Controller seems to be good way. Content(), View(), Json(), Empty() methods are also Controller extensions. You are adding your own.
EDIT
Sorry, I was actually wrong about extension methods. They are not extension methods. So it would be better if you used your own base Controller class with additional methods. Since you'll be using these methods inside of your classes, inheritance is the best solution. you can still share this class between projects.
Related
I'm using MVC3 razor, and I'm trying to pass an object to a partial view, and it's not working.
This works fine without sending the object model to the partial view:
Html.RenderAction("Index", "ViewName");
Trying this doesn't sent the model object, i'm getting nulls instead (the object has data, and the view expects it):'
Html.RenderAction("Index", "ViewName", objectModel);
Is this even possible using RenderAction?
Thanks!
Edit: I found the error, there was an error with the controller's action that didn't pick up the sent object. Thanks for all your help!
You can actually pass an object to a controller method using Action. This can be done on any avaialble view, for instance I have one in a shared library that gets built to project bin folders that reference my shared project (properties - Copy if newer on the view file, in Visual Studio). It is done like so:
Controller:
public class GroovyController : Controller
{
public ActionResult MyTestView(MyModel m)
{
var viewPath = #"~\bin\CommonViews\MyTestView";
return View(viewPath, m);
}
}
MVC page (using Razor syntax):
#Html.Action("MyTestView", "Groovy", new { m = Model })
or using RenderAction method:
#{ Html.RenderAction("MyTestAction", "MyTestController", new { area = "area", m = Model }); }
Note: in the #Html.Action(), the Model object must be of type MyModel and that 3rd parameter must be set to the controller variable name, of which mine is MyModel m. The m is what you must assign to, so I do m = Model.
say you want to pass foo as model, make it first
public class Foo {
public string Name { get; set; }
public int Age { get; set; }
}
now make an ActionResult
public ActionResult FooBar(Foo _foo){
return PartialView(_foo);
}
call it
#Html.RenderAction("FooBar", "Controller", new { Name = "John", Age=20 });
Usually if I have a model already available it makes more sense to use Html.Partial than trying to render an action.
#Html.Partial("Foo", Model.FooModel)
Where Foo.cshtml is a view file (perhaps in your Shared folder) strongly typed with with #model FooProject.Models.FooModel or whatever your model is called. This can be as complex a model as you need it to be. Model is your page's main model into which you must set FooModel - or just omit this parameter if the Foo view uses the same model as the parent page.
RenderAction is generally better when you have just simple parameters, because you're just simulating a request to a regular action which has routing/query string parameters - and then dumping that response into your page. It works well if you need to put something in a Layout that isn't available in your page's model such as an element in a side bar.
In my javascript code I have the following:
function SelectTopics() {
$.ajax({
url: "/Administration/Loc/SelectTopics",
data: { DataSourceID: $('#DataSourceID').val(),
LocationID: $('#LocationID').val()
},
success: function (data) {
$('#TopicID').html(data);
$('#SubTopicID').html("<option value='00'>All Sub Topics</option>");
}
});
}
In my controller code I have:
public string SelectTopics(string dataSourceID, string locationID)
{
return Select.Topics(
dataSourceID,
locationID);
}
In the Select class I have the following:
public static string Topics(string dataSourceID, string locationID)
{
...
return text;
}
The controller is just passing through a request and my controller code is getting bigger with all these requests. Is there another more conventional way of getting data into a view from a data source without going through the controller each time?
MVC definitely requires a Controller class to handle all requests. You cannot simply expose the Select class without it inheriting Controller. There might be a way, though, to minimize the amount of "pass-through redundancy" in your controller.
Simple Reflection
If your methods take the same parameters, you could use reflection and clever routing to get the results you're looking for.
For example, the url /Administration/Loc/Select/Topics?dataSourceId=x&locationId=y could be routed with /Administration/{Controller}/{Action}/{Method} to:
public ActionResult Select(string method, string dataSourceID, string locationID) {
var methodInfo = typeof(Select).GetMethod(method);
return methodInfo.Invoke(dataSourceID, locationID);
}
MVC Routing
If the methods take different parameters, then it would be pretty tedious to use reflection to map inputs to the parameters. A huge portion of the MVC framework is in the Routing. It does a lot of reflection to do the mapping from the request to the Action method, and it would be pretty tedious to recreate this functionality.
It might be possible to use the MVC routing engine to pass the requests to the controller. This might be a great solution, but it definitely takes a lot more research than I have time for, sorry. The good news, though, is that MVC is open source, so you could definitely see how they do it!
I want to write an HtmlHelper to render an ActionLink with pre-set values, eg.
<%=Html.PageLink("Page 1", "page-slug");%>
where PageLink is a function that calls ActionLink with a known Action and Controller, eg. "Index" and "Page".
Since HtmlHelper and UrlHelper do not exist inside a Controller or class, how do I get the relative URL to an action from inside a class?
Update: Given the additional three years of accrued experience I have now, here's my advice: just use Html.ActionLink("My Link", new { controller = "Page", slug = "page-slug" }) or better yet,
<a href="#Url.Action("ViewPage",
new {
controller = "Page",
slug = "my-page-slug" })">My Link</a>
Your extension method may be cute and short, but it adds another untested point-of-failure and a new learning requirement for hires without adding any real value whatsoever. Think of it as designing a complex system. Why add another moving part, unless it adds reliability (no), readability (little, once you read more docs), speed (none) or concurrency (none).
Not sure I actually understood your question clearly, but, let me try.
To create a HtmlHelper extension like you described, try something like:
using System;
using System.Web.Mvc;
using System.Web.Mvc.Html;
namespace Something {
public static class PageLinkHelper
{
public static string PageLink(
this HtmlHelper helper,
string linkText, string actionName,
string controllerName, object routeValues,
object htmlAttributes)
{
return helper.ActionLink(
linkText, actionName, controllerName,
routeValues, htmlAttributes);
}
}
}
As for your question on getting a URL from a class, depends on what kind of class you'll implement it. For example, if you want to get the current controller and action from a HtmlHelper extension, you can use:
string currentControllerName = (string)helper.ViewContext
.RouteData.Values["controller"];
string currentActionName = (string)helper.ViewContext
.RouteData.Values["action"];
If you want to get it from a controller, you can use properties/methods from the base class (Controller) to build the URL. For example:
var url = new UrlHelper(this.ControllerContext.RequestContext);
url.Action(an_action_name, route_values);
If I create a PartialView for a box that holds a header, image and content what is the best way to store the content without using a database?
Example: TurboTax
I doubt the content for the boxes in the sidebar are stored in a database but to make reusable code it would be beneficial to create the structure in a PartialView and populate the content areas. I can create a PartialView and pass a Model from the parent Controller to the PartialView but then I would be stuck copying and pasting that same content if I wanted to use the same box on another page.
For fixed content you might want to think about using XML+XSLT or even HTML snippets in the file system and simply rendering them. An HtmlHelper method might make more sense for this than a partial view - Html.RenderXml() or Html.Include(). The only real difference between these and partial views is that the view engine isn't invoked since there aren't any substitutions. I do this sort of thing with my privacy policy and terms and conditions. I'd certainly consider keeping these cached.
If these really are templates and you are just substituting content, then I think the partial view works well and I would consider putting the data in a database, again, maybe using caching if I found that performance suffered. You could use this in combination with the former -- say keep your images/xml in the file system and a pointer to them in the database so you know which ones to pick in the partial.
Passing data to partial view that is used in many places can be done in many ways:
Create base model class for all your models. In base class define PartialModel property which will be holding model for partial view (there may be many of them if use have many partial views). Now you can populate the PartialModel property in controller action, but to make code more reusable you can create your own Action Filter which will insert the partial view data just after the action method is executed (but before the model is passed to the view)
public class PartialViewModelAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
BaseViewModel model;
if (filterContext.Controller.ViewData.Model == null)
{
model = new BaseViewModel();
filterContext.Controller.ViewData.Model = model;
}
else
{
model = filterContext.Controller.ViewData.Model as BaseViewModel;
}
model.PartialModel = new PartialModel(...) // Partial model initialization
base.OnActionExecuted(filterContext);
}
}
Then you can use it like:
[PartialViewModel]
public ActionResult Index()
{
//...
}
Another option: you can create BaseController class for all your controllers and create PartialModel on base controller initialization. Then PartialModel can be stored in ViewData[] dictionary. Because using ViewData dictionary in views is bad, create extension method on HtmlHelper like:
public static PartialModel GetPartialModel(this HtmlHelper helper)
{
return helper.viewContext.ViewData["PartialModel"] as PartialModel
}
So you could obtaint the model this way:
<% Html.RenderPartial("MyPartial", Html.GetPartialModel()); %>
i have different different pages which i want to call from one controller action
here is what i've done
public class TemplatesController : Controller
{
public ActionResult Select(int id)
{
return View("Temp"+(id));
}
}
i have different view pages like
Temp1, Temp2, Temp3,..etc...
the id is fetched properly
but i think there is a problem in concatenation
i want final result to be
return view("Temp1");
in another case it would be
return view("Temp2");
so that these pages can be called without creating controllers for each of the pages.
pls help.!
return View("Temp"+id.ToString());
The parameter is a String, so you can build the string up however you want.
string RetView = "Temp"+id.ToString();
return View(RetView);
so that these pages can be called
without creating controllers for each
of the pages.
Although i'm not sure if this is good practice, I suppose it depends on how many views you have.