render Url.Action in model - asp.net-mvc

how can i do such thing in model or controller?
<%= Url.Action("Home"); %>

You could use the Url property in the controller:
public ActionResult Index()
{
string url = Url.Action("Home");
// Do something with the url
return View();
}
As far as the model is concerned you should avoid using such methods there. The model is passed to the view and the view has access to helper methods which can do the job.

Related

ASP.NET MVC Clean way to inject partial view from action

I have an app with many widgets and their content depends on the user requesting specific route. Simply put: if widget action is requested, its content must be rendered, otherwise it's empty. Consider routes/actions like this:
~/MyApp/Index -> without model; app HTML, without any widgets
~/MyApp/Foo/{id} -> uses FooModel; if ModelState is valid, returns
Index HTML with injected partial view of Foo's widget to div#foo;
otherwise redirects to Index.
~/MyApp/Bar/{id} -> same as Foo, but different model and widget
My foo action :
public ActionResult Foo(string id) {
if (ModelState.IsValid) {
var response = FooService.GetData(id);
// Inject Foo widget to Index
}
return RedirectToAction("Index");
}
I know that it is possible to use ViewBag or other means to send variables and using the condition to decide whether to render partial view or not. But... there should be a better way to do this, right?
I use MVC's Html.RenderActionResult when I want to build shared views with non-trivial binding logic (calling the database, composing complex objects, etc). The binding logic for each widget is contained in a PartialViewResult method, which is called from the *.cshtml file using Html.RenderAction().
ContentController:
public ActionResult Index(int id)
{
var indexViewModel = new IndexViewModel
{
Id = id,
Title = "My Title",
SubHeader = "Wow its 2016"
};
return View(indexViewModel);
}
public PartialViewResult PopularContent(int id)
{
var popularContentViewModel = new List<PopularContentViewModel>();
// query by id to get popular content items
return PartialView("_PopularContent", popularContentViewModel);
}
public PartialViewResult Widget2(int id)
{
return PartialView("_Widget2Partial");
}
Index.cshtml:
#model StackOverflow.RenderAction.ViewModels.IndexViewModel
<h1>#Model.Title</h1>
<h2>#Model.SubHeader</h2>
--RenderAction will call out to the specified route.
--Note the use of the Id parameter from the viewmodel.
#{Html.RenderAction("PopularContent", "Content", new {Model.Id});}
ASP.NET MVC Attribute Routing could a be a nice solution for this:
In your controller:
public class WidgetController : Controller
{
[Route("myapp/foowidget", Name = "FooWidget")]
public ActionResult FooWidget()
{
//create any model and return any view or partial or redirect
}
[Route("myapp/boowidget/{id:int}", Name = "BooWidget")]
public ActionResult BooWidget(int id)
{
//create any model and return any view or partial or redirect
}
}
And then in a View, you can call the Route by name:
#Url.RouteUrl("FooWidget")
or
#Url.RouteUrl("BooWidget")
or
#Html.RenderPartial("FooWidget")
#Url.RouteUrl("BooWidget") will render or concatenate the id that is in current url, if url is /myapp/something/id, because of your Route attribute definition: "myapp/boowidget/{id:int}". In fact #Url.RouteUrl("BooWidget") might extract the id from any current url of the format /controllerName/action/id, though you will have to test for sure.
And notice how you can have a separation of concerns with your WidgetController and your url Routes are not dependent on that controller's name in any way. That is a nice feature of Attribute Routing, you can declare custom routes as well as organize your controllers and break from nameing convention dependency of a controllerName being part of the url controllerName/action a user sees in their browser.
In regards to Html.RenderPartial, I am not sure if RenderPartial "connects" or will be able to route to your RouteName like "FooWidget". If it does great.
If not your solution is this:
public class WidgetController : Controller
{
public ActionResult FooWidget()
{
//model, you choose, return a partial
}
public ActionResult RedirectUser()
{
//do a redirect
}
public ActionResult BooWidget()
{
//any model, any partial
}
public ActionResult BooWidget(int id)
{
//any model, any partial
}
}
Each method in your controller is single purpose, has a distinct signature and does one thing, no conditions to pass in and no decisions required.

Method to render partial view

I got a partialView that I would only like to render if the user is logged in.
Im thinking something like this:
View:
#Html.ActionLink("Text", "Method", "Controller")
<section id="events">
#Html.Partial("_CreateNewPost")
</section>
Controller:
[Authorize]
public ActionResult Method()
{
Code that renders the PartialView
return View();
}
This way, I would asume that a user that is not logged in will be sent to the login-page.
Thanks!
Edit:
So im wondering if its possible to create code in the method that renders the partial view.
The way it is now, the partial view gets rendered as soon as the page loads.
Sure it's possible. On your controller:
[Authorize]
[ChildActionOnly]
public ActionResult MyPartial()
{
//Do stuff...
return PartialView("_partialViewName");
}
and then in your view:
#Html.Action("MyPartial", "ControllerName")
This is useful in cases where you want to return different partial view, depending on some condition, or if you want to pass some data, like a viewmodel, to the View. The ChildActionOnly specifies that this view is only accessible when it's called from another view, so you can't just type /controller/MyPartial in the address bar.
You can use a child action:
[Authorize]
public ActionResult Method()
{
return PartialView("_CreateNewPost");
}
Then call it in your view:
#if(Request.IsAuthenticated)
{
Html.Action("Method","SomeController")
}
#Html.Partial("ViewName")
#{ Html.RenderPartial("ViewName"); }

Can you put a PartialView into a ViewData object?

I'm trying to mimic the webforms multiview functionality and the only way i can think of is to put a PartialView into a ViewData object? Something like the following:
View code:
<%= ViewData["PartialViewPlaceholder"] %>
Controller code:
if(//condition){
ViewData["PartialViewPlaceholder"] = partialView1;
} else {
ViewData["PartialViewPlaceholder"] = partialView2;
}
How would you go about this?
ViewData is meant to contain actual data, not views themselves, which contain markup and rendering code. Would it not be possible for you to do this:
public ActionResult MyActionMethod()
{
var model = new MyModel();
model.UsePartialView1 = false; // Tell the view not to use Partial View 1
return View("MyView", model);
}
And in the View MyView:
<% if (Model.UsePartialView1)
Html.RenderPartial("PartialView1", Model);
else
Html.RenderPartial("PartialView2", Model); %>
This will render either PartialView1 or PartialView2 using the same Model depending on the condition set by the Controller.
Or, to return a Partial View with a Model directly from your controller, instead of a normal View, you can do this:
public ActionResult MyActionMethod()
{
var model = ...
ViewData["MyViewData"] = ...
return PartialView("PartialView1", model);
}
This will return the Partial View PartialView1 directly to the client. This is mostly useful in AJAX scenarios, since the result will most probably not be an entire HTML page. Partial Views are .ascx files.

.NET MVC instantiate controller inside another controller

Is it possible for an ASP.NET MVC controller to create a new instance of a different controller and effectively delegate resonsibility to that?
Let's say for example that I have two controllers in the /Controllers/ directory:
public class HomeController : Controller
{
public ActionResult Index()
{
var otherController = new OtherController();
return otherController.ShowNumberOfThings(100);
}
}
public class OtherController : Controller
{
public ActionResult ShowNumberOfThings(int index)
{
return View(index);
}
}
...and a View called Views/Other/ShowNumberOfThings.aspx:
<%# Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="ViewPage<int>" %>
Number of things: <%= Model.ToString() %>
When I hit the url:
http://localhost/Home/Index
I want to be presented with a page that reads:
"Number of things: 100"
I would like to be able to persist temporary data between controller redirections without being forced to use the session object (TempData[""] uses the session object for cross-controller redirections). My real world case has a complex object which needs passing (not just an int) so using a URL/Cookie is out of the question, and session state is a no-no.
In WebForms at least we could use Server.Transfer and maintain any state in the HttpContext.Items collection. In MVC the only option I can see is to call the controller method directly passing in required arguments.
At the moment it's having trouble trying to resolve the view folder as the "context" is still running under the HomeController.
I guess where I am going with this is trying to cludge ASP.NET MVC into acting like a FrontContoller.
Any ideas?
EDIT
In the end we had to serialise everything into a session and use that. A shame, but I have heard that MVC2 will support serialising objects into a ViewState.
If you want to be presented with "Number of things: 100" when you hit the Index action why not directly render the corresponding view:
public class HomeController : Controller
{
public ActionResult Index()
{
return View("~Views/Other/ShowNumberOfThings.aspx", 100);
}
}
I think it would be preferred to use.
return RedirectToAction("Controller", "Action")
However I'm guessing you want to maintain the Url Home/Index.
If you're looking at the FrontController pattern then you should investigate writing a Custom ControllerFactory which inherits from DefaultControllerFactory then Override the CreateController method.
You can register your factory using the code below.
protected void Application_Start()
{
ControllerBuilder.Current.SetControllerFactory(new MyCustomControllerFactory();
RegisterRoutes(RouteTable.Routes);
}
In the Controller factory you have access to the RequestContext so you can change the RouteData as needed and delegate to the correct controller.
You could of course just set a a Custom route for Home/Index which goes to OtherController.ShowNumberOfThings()
routes.MapRoute("Home", "Home/Index/{id}",
new {controller = "Other", action = "ShowNumberOfThings", id = 100});
a different approach would be the use of partial views
instead of ~Views/Other/ShowNumberOfThings.aspx
you could put your view in ~Views/shared/ShowNumberOfThings.ascx
have both views ~Views/Other/ShowNumberOfThings.aspx and ~Views/Home/Index.aspx implement the partial view
public class HomeController : Controller
{
public ActionResult Index()
{
return View(100);
}
}
public class OtherController : Controller
{
public ActionResult ShowNumberOfThings(int index)
{
return View(index);
}
}
and in both views implement the partial view
<% Html.RenderPartial("~Views/shared/ShowNumberOfThings.ascx", ViewData.Model); %>
you can change the int for any object that will be passed to the model
Another possibility (similar to partial views) is to use Html.RenderAction. This allows for different view model classes and separate controller methods.
<% Html.RenderAction("yourActionName", "yourControllerName", routeValues); %>

model binding in mvc

i'm having a textbox inside a form.
[View]
<%=html.textbox("name") %>
[Controller]
Index(string name)
{
name = "something";
return View();
}
On Form Submit
In this case without sending any ViewData the textbox value is maintained.But the value "something" is not setting up.
But whn i change the Action to
[Controller]
Index()
{
string name="something";
return view();
}
the value is not maintained.
Really wat happening on that parameter.
If you want to set data for html.textbox("name") in the Controller use ViewData["name"] = "something"
Your question is not very clear and your code example is not actually adding anything to ViewData or the view Model - here's a shot at what i think your trying to do...
Assuming you want to re-populate the form and your View is Strongly Typed, You would do something like this:
public ActionResult Index(String name)
{
MyModel model = new MyModel;
model.Name = name;
ViewData.Model = model;
return View();
}
A textbox in your view with the same name would then have the value auto populated from the Model
<%= html.textbox("Name") %>
Posting the form would then post the model object to your controller like this:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Index(MyModel model)
{
// do something with the model
ViewData.Model = model;
return View();
}
and then re-populate the form with the model data.
string name in your Index action in the controller, is mapped to the FormValue, if you change this, MVC understands that it needs to add the value from the FormValueCollection to the textbox, and you have changed that in your Index action. If you declare a variable by yourself this doesn't work because there is no binding to the formvalues.

Resources