I'm trying to get the master page changed for all my aspx pages. For some reason I'm unable to detect when this function is called for a ascx page instead. Any help in correting this would be appreciated.
protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
var action = filterContext.Result as ViewResult;
if (action != null && action.MasterName != "" && Request.IsAjaxRequest())
{
action.MasterName = "Ajax";
}
base.OnActionExecuted(filterContext);
}
If you're still keen on changing the master page based on the fact whether your request is ajax or not - I just accidentally stumbled on exactly the thing you were looking for:
http://devlicio.us/blogs/sergio_pereira/archive/2008/12/05/reusing-views-in-asp-net-mvc-for-ajax-updates.aspx
Basically, instead of overriging the OnActionExecuting method in the BaseController - override the View method! You get exactly the thing you want, with a method that seems specifically designed for it :)
protected override ViewResult View(string viewName, string masterName, object model)
{
return base.View(viewName, Request.IsAjaxRequest() ? "Empty" : masterName, model);
}
So you're saying that MasterPage is empty when you're executing actions to .ascx "pages"?
.ascx's aren't pages, they're UserControls / PartialViews. And as such they don't have master pages. They can be dropped in a mage, or a master page.. But if your request is returning an .ascx, it won't have a master page.. )
UPD:
This is most likely because of the way MVC works - all the 3 parts (M-V-C) are completely independant. Which means that when your code executes inside the controller, we know nothing at all about the view. And the View is the one that selects the master page, not the controller.
Tbh if you're trying to change the way the app looks (change the master page) inside the controller - you're most likely doing something wrong. It was designed with separation of context in the first place, and you're trying to go around it :)
UPD2:
So you're saying that you want to return the full page + master page for regular requests, and just the page without the master (well, clean at least) for ajax requests? You're still trying the wrong approach.
Here's what I've been doing instead:
if (!Request.IsAjaxRequest())
return View(model);
else
return PartialView("PartialName", model);
Exactly the same situation. If I'm loading the url in a browser - it returns the full page, master and all.. If I'm loading it later on, using an ajax call - just load the partial view. Simple and easy. And still adheres to the MVC methodology :)
Also, if you're absolutely keen on preselecting the master name.. just do it like this:
return View("ViewName", "MasterName", model);
Related
I'm trying to load my partial view with some data from database, but I'm getting following issue when I run the application:
Child actions are not allowed to perform redirect actions.
I don't know why this is happening because I'm pretty new with MVC technology.
Here is my PartialViewResult method in a controller:
public PartialViewResult UnReadEmails()
{
if (User.Id != null)
{
List<Emails> resultList = EmailController.GetUnreadEmailsByUserId(User.Id);
return PartialView("~/Views/Emails/_UnReadEmails.cshtml", resultList);
}
return PartialView("Error, not found!");
}
And here is my partialview itself, it is called _UnReadEmails (as you can see I'm displaying here info about sender and email body), PartialView is retrieving list of Emails that I'm sending to from my Controller
#model IEnumerable<Emails>
foreach (var item in Model)
{
<li>
<a>
<span>
<span>#item.EmailSender</span>
<span class="email">
#item.Body;
</span>
</a>
</li>
}
After I tried to load my partial view on this way:
#Html.Action("UnreadEmails", "Message")
I started to receive following issue that I mentioned in my Title,
I already tried few things to solve this like changing #Html.Action("UnreadEmails", "Message") to #Url.Action("UnreadEmails", "Message") etc etc but that didn't solve my issue.
EDIT: It allways breaks on this line (on view) :
#Html.Action("UnreadEmails", "Message")
It never goes into code behind..
After Chris suggestion I added [AllowAnonymous] on the top of the method:
[AllowAnonymous]
public PartialViewResult UnReadEmails()
{
if (User.Id != null)
{
List<Emails> resultList = EmailController.GetUnreadEmailsByUserId(User.Id);
return PartialView("~/Views/Emails/_UnReadEmails.cshtml", resultList);
}
return PartialView("Error, not found!");
}
EDIT EDIT EDIT:
Interesting fact is that whatever I wrote in my Controller's method and even if I comment all code, it will still break on a View, that means it will never came into a Controller's method. I put breakpoing there at the begining of the UnReadEmails method and it was never hitted, it allways breaks on a View!
The error is pretty explicit. Child actions cannot issue redirects. This is because, while child actions look like regular actions, they are in fact rendered in a separate process and cannot modify the actual response (which a redirect would require).
In your actual child action code, you're not returning a redirect, so that means you must have an action filter being applied to the child action that is issuing the redirect. In particular, you should avoid using things like Authorize or RequireHttps on child actions, as those work by using redirects, which again, a child action cannot do. If the child action is in a controller that is decorated with Authorize, it should be marked with AllowAnonymous.
Usually it happens when child Action has errors, because exception gets thrown and MVC trying redirect user to error page. In your case it is trying to find view named "Error, not found!", which probably you don't have. Try run Action as itself first, and check if logic works. Then put your view _UnreadEmails.cshtml in Manage Controller Views folder, and modify the code:
public PartialViewResult UnReadEmails()
{
if (User.Id != null)
{
List<Emails> resultList = EmailController.GetUnreadEmailsByUserId(User.Id);
return PartialView("_UnReadEmails", resultList);
}
return PartialView("_UnReadEmails" new List<Emails>());
}
Sometimes empty List means that nothing was found. OR:
public ActionResult UnReadEmails()
{
if (User.Id != null)
{
List<Emails> resultList = EmailController.GetUnreadEmailsByUserId(User.Id);
return PartialView("_UnReadEmails", resultList);
}
return Content("Error, not found!");
}
Hi you may try by changing this line #Html.Action("UnreadEmails", "Message") to like this #{Html.Action("Category","Home");}
if the above doesnt work there are three other ways to render the partial view in mvc. those are
Html.RenderPartial
Html.RenderAction
Html.Partial
you make use any one of the above and get your solution.
Good example with comparision can be found here:http://www.dotnettricks.com/learn/mvc/renderpartial-vs-renderaction-vs-partial-vs-action-in-mvc-razor
Hope it was helpful,kindly let me know your thoughts or feedbacks
Thanks
Karthik
At the end I decided to skip my PartialViewResult method from a controller and do it on another way, because I've allready lost 2 days trying to find an answer and it wasn't there, but something else seems to be working, and it was this:
{#Html.RenderPartial("~/Views/Emails/_UnReadEmails.cshtml",EmailController.GetUnreadEmailsByUserId(User.Id))}
What I did there was next, I called directly GetUnreadEmaisByUserId method from my BussinesLogic part, and everything worked like charm.
And what we might do here to make this solution better is next:
In case we dont have a "main model" we could store that in a ViewBag, calling this from a MessageController/Email controller method
ViewBag.lstUnreadEmails = EmailController.GetUnreadEmailsByUserId(User.Id);
And in the View use this
{#Html.RenderPartial("~/Views/Emails/_UnReadEmails.cshtml",ViewBag.lstUnreadEmails}
One of the View methods on the Controller class (in System.Web.Mvc) allows you pass a model to a different view. However, it does not update the url to the name of the new view because this method works more like Server.Tranfer than Response.Redirect. The point is that this behaviour can be confusing to anyone picking-up MVC. So, after the View method has been called, I would like the url path to be rewritten to reflect the name of the new view. I have tried the following, which does not work, on a class that implements the Controller class:
public ViewResult ViewAsRedirect(string viewName, object model)
{
var baseView = base.View(viewName, model);
ControllerContext.HttpContext.RewritePath(baseView.ViewName);
return baseView;
}
What's the correct code to implement what I have described?
EDIT
Any RedirectToAction method does not allow you to send you model to another action. There is a dirty workaround where you store it in TempData before you exit one action and retrieve it at the beginning of another action. I don't want to use this pattern. This is why I am using View(...) instead.
You simply cannot "update the URL" (i.e. redirect) and return content.
If you want the new URL to show the same content as you anticipated, then you'll need to (temporarily) store the results and include an identifier for the resource that you wish to display on the redirected URL.
There you can pull the resource in from the controller for the redirected URL again, and display it on the appropriate view.
So if you POST your object model to /Foo/Create, you can for example store model in a database, which yields an ID: 42. Then you can redirect to /Foo/View/42, and display it.
If you can explain what you are actually trying to do, a more concrete answer can be given.
RedirectToAction is very intuitive in my opinion... you should use one of the redirecting methods of MVC controller: Redirect(url), RedirectToAction(acion), RedirectToAction(acion, controller), and so on.
example
public class HomeController : Controller
{
[HttpGet]
public ActionResult Index()
{
return View();
}
public ActionResult Redirect()
{
return this.RedirectToAction("Index");
}
}
EDIT
If your action needs to collect tons of data to pass to the view, you could redirect very early, by detecting the condition of redirection, and loading all the data inside the other action, based on simple route data such as database IDs, or simple strigs or numbers.
== OR ==
You can render javascript code to change the URL in the client side, when the page loads: Modify the URL without reloading the page
This works only in very recent browsers (current date nov/2013).
The image of a login page (clickable image):
Is it seems (though the debug viewer), the ViewEngines are both (razor+webforms, im using the first) there, in the ViewEngines collection, and them both can find the views (manually) and display them, also tried manually.
So why is that they cannot do it on their own? This is simply an action method, nothing more:
public ViewResult Login()
{
return View();
}
Major Update
I have overridden the OnResultExecuting method:
protected override void OnResultExecuting(ResultExecutingContext filterContext)
{
if (filterContext.Result is ViewResult)
{
if (((ViewResult)filterContext.Result).ViewEngineCollection.Count == 0)
{
((ViewResult)filterContext.Result).ViewEngineCollection.Add(new RazorViewEngine());
}
}
base.OnResultExecuting(filterContext);
}
And it turned out that every time and every view I do now need to add new IViewEngine to a ViewResult. Now why is that so? Even if the ViewEngines.Engines collection is not empty?
Minor update.
The ViewEngineCollection stopped being empty at some point of idling.
Do you have a master page defined?
If you have a _ViewStart.chstml file in the root of your solution then it is possible the engine is trying to load a master page to combine with your view.
To skip the master page tell your view to not use a master page. Add the following to the View
#{
Layout = null;
}
or make sure that the master page referenced in the _viewStart.cshtml file exists.
You may not have a correct ViewEngines configured (or none).
Try adding to view engines manually to identify if there is a configuration error somewhere.
Add something like this to Application_Start() method of Global.asax.
protected void Application_Start()
{
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new RazorViewEngine());
// Other code
}
This should initialise the Razor view engine, and if as a result mvc starts searching locations, you have somehow turned off razor in the configuration.
The ViewEngineCollection stopped being empty at some point of idling.
This only might refer to some ASP.NET caching of old assemblies which was not for some reason updated by the new ones. This happens from time to time, and I have to clear the cache in order for some new code to work.
I'm writing a page in MVC3, and there are a few places where I want to request a page in ajax and from the url bar too.
If the request is a full pagerequest I want the Action to render "Example.cshtml" which is a full view.
But is the request is an ajax request I would only want to render the "_Example.cshtml" which is a partial view.
My code is
if (Request.IsAjaxRequest())
{
return PartialView("_Example");
}
return View();
but since the MVC3 is all about conventions and that everything is reconfigurable, I would like to able to write
if (Request.IsAjaxRequest())
{
return PartialView();
}
return View();
and still load the "_Example.cshtml" if it's a partial view.
I name "_Something.cshtml" all my partial views anyway so wouldn't it be cleaner if I could just call PartialView(); ?
Please tell me that this is possible. And tell me how.
EDIT:
I still want to be able to make the partialviews different from views, so switching the masterpage would be enough for only a few cases.
I would like to do something like overloading the default path to look for partialviews, like:
PartialViewLocationFormats = new[] {
"~/Views/{1}/_{0}.ascx",
};
but this affect PartialView("Example") to use _Example.cshtml too, which is undesirable.
EDIT:
An other thing I tried is to overload the controller's PartialView() and PartialView(object model) methods, but they cannot be overridden neither can I find a proper way to find which action were they called from.
Have you seen this? It may help with what you are looking for.
I wrote a pretty substantial answer on this one:
Simple ASP.NET MVC CRUD views opening/closing in JavaScript UI dialog
Basically you adjust the view engine to open full "master themed" views for regular requests and then return the view, but with an empty master, when the request is done via ajax.
You could create an ActionFilterAttribute:
public class AjaxResultActionFilter: ActionFilterAttribute
{
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
var result = filterContext.Result as ViewResult;
result.ViewName = "_" + result.ViewName;
filterContext.Result = result;
}
else
{
base.OnResultExecuting(filterContext);
}
}
}
This will append a "_" infront of the ViewName that MVC tries to resolve if the IsAjaxRequest returns true.
All you need to do now is to decorate either your actions or the controller with this attribute:
[AjaxResultActionFilter]
public ActionResult Index()
{
return View("Index");
}
Now, I wrote this on the fly, so in this version it only works when you actually pass the ViewName into the return View() statement.
With some debugging and more time though, I'm sure this could be fixed :)
Hope this helps!
Is there a clean way to manage my Asp.Net Mvc Web site to both work correctly if javascript is enabled/disabled. Because, for now, I have to do hack like that to make both work. I think that doesn't make code that is easy maintainable and reusable...
if (Request.IsAjaxRequest())
{
return PartialView("SignUpForm", user);
}
else
{
return View("SignUp", user);
}
In this answer I've outlined a modal window technique that works cleanly without javascript; no code changes if you wanted to disable all the modal and javascript functionality.
Simple ASP.NET MVC CRUD views opening/closing in JavaScript UI dialog
The bits that I think are most important for you is custom ViewEngine:
public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
{
//you might have to customize this bit
if (controllerContext.HttpContext.Request.IsAjaxRequest())
return base.FindView(controllerContext, viewName, "Modal", useCache);
return base.FindView(controllerContext, viewName, "Site", useCache);
}
This code turns off the javascript and the surrounding template by loading a separate MasterPage if a request is from ajax or not. By switching the masterpage inside your own custom ViewEngine you avoid the if( Ajax ) code in all of your controllers and keeps thing clean.