I have two controllers and two models (completely separate). I then have one view where I use an url.action to create a new record using the first controller and model. I then have another url.action to create a record for the other controller and model. My issue is that submitting one will submit the other. Is it bad practice to use a url.action to display the create view - because it works well when I only use one url.action. Or is it bad practice to have multiple. How else could this goal be achieved without having to use another view?
the view is pretty straight forward.
....
<div>
#*#Html.Action("Create", "MPost", new { TaskId = Model.TaskId, TaskTitle = Model.Title })*#
</div>
<div>
#Html.Action("Vote", "Vote", new { TaskId = Model.TaskId})
</div>
There is nothing wrong with using multiple Html.Action() calls within your Views, however you need to consider if that's the best option for you to use.
Determining What You Want To Do
Html.Action() is going to call the controller action when the View is initially rendered and it will perform whatever actions are in the View and then it may or may not return another View / Partial View.
You mentioned the following in your post :
My issue is that submitting one will submit the other. Is it bad
practice to use a url.action to display the create view - because it
works well when I only use one url.action.
This sounds like you actually want these to function as links and when they are clicked that you want to call them. If that is the case, you may be better off either creating a link that points to the appropriate action via the Url.Action() or Html.ActionLink() helpers :
<a href='Url.Action("Create", "MPost", new { TaskId = Model.TaskId, TaskTitle = Model.Title })'>Create New Post!</a>
These will perform basic GET requests to your Controller Action as you might expect. If you need to actually POST values, then you should consider using a <form> and submitting it :
<form action='#Url.Action("Vote", "Vote", new { TaskId = Model.TaskId})'>
<input type='submit' value='Cast Your Vote!' />
</form>
Generally, if you have two separate URLs that you might be posting values to, you should consider placing them each within their own <form> to avoid any confusion or possibly posting to the wrong area. Using links will work just fine as well, but keep in mind that they will redirect the user to the View that is returned from the Controller Action that you are hitting.
Related
I have a pretty big ASP.NET MVC site with 100 controllers and thousands of actions. Previously the header image that was defined on the Site.Master page was hardcoded and I want to make it dynamic.
To do so, I added this line to my Site.Master file:
<%= Html.Action("GetHeaderTitle", "Home")%>
which just returns some HTML for the header title such as:
<span style='font-size:15px;'>My Header Title</span>
The issue is that <title> also had this same hard coded value. I could obviously create another HTML.Action to have it show the dynamic valid in the title, but now I am going back to the server twice for essentially the same information (not the exact same HTML as I don't want the span information, but the same logic on the server to get the data).
Is there a way to have an Html.Action return multiple snippets of HTML that I can updates in different places on my master page?
I think you're looking at it wrong - if retrieving of the title is a long operation then just cache the results and write different actions anyway.
// Controller
public string GetTitle()
{
var title = (string)ControllerContext.HttpContext.Items["CachedTitle"];
if (string.IsNullOrEmpty(title))
{
title = "some lengthy retrieval";
ControllerContext.HttpContext.Items["CachedTitle"] = title;
}
return title;
}
public ActionResult GetTitleForTitle()
{
return Content(GetTitle());
}
public ActionResult GetHeaderTitle()
{
return Content("<span>"+ GetTitle() + "<span>");
}
Alternatively, you can cache it directly on the view, which is kind of evil (the simpler view the better):
<%
ViewBag.CachedTitle = Html.Action("GetHeaderTitle", "Home");
%>
...
<%= ViewBag.CachedTitle %>
...
<%= ViewBag.CachedTitle %>
Are you using <asp:ContentPlaceHolder ID="TitleContent" runat="server" /> ? in your site.master? Or have you considered using it? This would allow you to set the title from within a view based on your model.
Maybe you should also consider to introduce ViewModels, which allows you to combine view related data into a ViewModel and return it from a controller. This would allow you to batch queries and save round trips. And use a data repository which gets injected into your controller class (if you are not already doing). Sorry I'm guessing here because you do not show any controller code.
Or you should take advantage of client side code (JavaScript) and load parts of the UI via ajax? And have the UI update itself (could also be reactive).
Unfortunately I have the feeling that the thing your are facing today is not about the title but more about the hundreds of controllers with thousands of actions.
So researching how to organize controllers and building up an opinion based on your use cases would be my best bet based on what I can see and guess from your question.
Hope that helps...
So far:
I have a page with multiple submits on it, where each submit depends on the previous one.
The same page is rendered each time a submit is clicked.
I found myself writing spaghetti code in the controller method (branching based on the ViewModel), and wanted to factor out the behaviour for each submit into a separate method.
I implemented the solution found here - specifically the solution posted by mkozicki based on the article by Maartin Balliauw.
This worked well for forking to different controller methods. But I encountered two problems:
Returning to the same view each time.
Hard-wiring the action method names in the View.cshtml
Here's the code:
Controller:
public class PlayerStatController : Controller
{
public class PlayerStatViewModel . . . //quite complex ViewModel
// HTTP GET
public ActionResult SelectPlayer()
{
List<string> idx_list = getSeasonIndex();
return View(new PlayerStatViewModel(idx_list));
}
// One of three forked action methods
[HttpPost]
[MultipleButton(Name = "action", Argument = "ChosenSeason")]
public ActionResult ChosenSeason(PlayerStatViewModel viewModel)
{
List<string> team_idx = getTeamNameIndex(viewModel.selected_seasonIndex);
return View("SelectPlayer",new PlayerStatViewModel(new List<string>(), team_idx, new List<string>(), 0));
}
Here an excerpt from the view (SelectPlayer.cshtml)
<form action="/PlayerStat/ChosenSeason" method="post">
<fieldset>
<legend>Select Season</legend>
<div class="editor-field">
#Html.LabelFor(m => m.selected_seasonIndex)
#Html.DropDownListFor(m => m.selected_seasonIndex, Model.seasonIndex_select_list)
#Html.ValidationMessageFor(m => m.selected_seasonIndex)
</div>
<p>
<input type="submit" value="Choose Season" name="action:ChosenSeason" />
</p>
</fieldset>
</form>
Hence:
Is returning from the forked action method with return View("SelectPlayer",new PlayerStatViewModel(...); the best solution to forcing the same view (SelectPlayer.cshtml) to be rendered every time?
Is there a way to avoid hard-coding the action method name in the View (i.e., <form action="/PlayerStat/ChosenSeason" method="post">) I would like to keep using #using (Html.BeginForm()) if possible.
Specifying the view name in the return statement is the best and most practical way to return a view that is named something different than the current action method being executed. I believe this is by design in order to decouple action methods from a single view.
Again, for the view if you want the form to post to an action other than the one specified in the current URL you have to specify it explicitly. Using an empty BeginForm() will cause the form to post to the same URL that was returned on the previous request.
I believe what you have is the best way to tackle the problem and is the way I have my MVC application implemented as well. There is nothing wrong with being explicit, especially when it comes to views and view logic because they are by their very nature explicit. Separating the different submit buttons into different action methods is a solid approach and one that will inherently require you to specify which action to target for each submit button. You can think of this approach as analogous to Web Forms Server Side Event Handlers for button clicks (minus all the nasty page life cycle). This approach is elegant and clean, only the server side code corresponding to the submit is executed.
I don't know if I'm doing this wrong, but here's my problem. I need to pass a View's data to another Controller/Action.
In my HTML Form, I have
#using (Html.BeginForm("Preprocess", "Item", FormMethod.Post))
{
...some html...
...loop for each item in Items collection
<button type="submit" name="itemInfo" value="#Model.someValue">Submit</submit>
}
I receive the Form's data on my Item/Preprocess Action (strongly-typed view). However, I need to pass this to a 'central processor' that process the data depending on a certain flag. How do I pass the values I receive on this controller to another controller? I'm a beginner in MVC and I'm not even sure if this is the right way to do it.
Basically, I have three HTML forms similar from the above form, but with different Controllers and Views. I need them to call a single central master-Controller/Action and of course pass the data from the Form to the master Controller via HTTPPOST. Of course, each Controller has to format its own data to a class the master Controller can accept. What should I put in place of Return View() or RedirectToAction(...)?
Can you guys suggest a way to do this?
Or maybe you guys can suggest another way. It may not follow what I want, but basically my requirement is a central Controller/Action (or some other Centralized code) can receive the data and do operations depending on a value
You can do this:
return RedirectToAction("SomeAction", "SomeController",new { id=someString} );
Not sure I follow exactly what you are trying to do here. But from your description it does sound like your design is wrong.
If you are trying to perform some common set of actions once data is received and processed by your controllers then you should promote your "central controller" to be some kind of service class that can be accessed by all three controllers. The service class could return a ActionResult if necessary:
return new ViewResult { ViewName = "MyForm" };
However if you want to intercept the data and perform some common actions you could write a custom ActionFilter to do whatever your central controller is doing and leave the other controllers clean.
I have a partialview in _Layout.cshtml that I only want to display for certain urls.
My first thought was in my partial I would use a string as the model #model String .
In the actionmethod that is called I would return this
return PartialView("_MyPartial", new string{Request.FilePath});
In the partial I would have an if block wrapping my outer div that would check the model to see if the url it contained was the url that can display the partial.
I don't like this way because I would have to hardcode the url in if block check
#if( Model == "/Test/Home")
{
<div>
Just an example
</div>
}
What would be the best way to do this?
Thanks
You shouldn't need to use hard coded strings, even if you did the validation within your view like you initially intended.
You can use
Request.Url.AbsolutePath
to get your current url and
Url.Action("action", "controller")
to generate the inacceptable locations.
That said, I would keep your logic determining whether to show the partial view within your controller.
if(showPartialView)
return PartialView("_MyPartial");
else
return new EmptyResult();
Deciding actions based on the request is the responsibility of the Controller. Since the controller chooses the view, why not have it choose the partial as well? Figure out what, if any, partial you want in your controller, and pass it to the view on your view model.
I'm stuck! I'm under the impression that the _layout.cshtml file is used for MasterPage-like content. Everything there is rendered on every page. Naturally, I want to write the code for rendering my sidebar menu in that file.
I want to dynamically display a list of Categories from my DB, but I'm having a problem with passing the actual model of categories to Layout.cshtml since it seems no controller actually touches it.
Any suggestions?
Otherwise please tell me how to approach this problem. I've been wracking my brain for the past three days and still no elegant solution.
I need to:
Dynamically fetch a list of Categories from the DB.
Display this list of Categories on every single view. (Hence the use of _layout.cshtml)
Elegantly handle each different categories click.
I'm at my wits end. :P How would you solve this?
_layout.cshtml
#if(isSectionDefined("Categories"))
{
<div id="sidebar">
#RenderSection("Categories", required: false )
</div>
}
index.cshtml
#section Categories {
<ul>
<li>Category One</li>
<li>Category Two</li>
<li>Category Three</li>
</ul>
}
see this : http://weblogs.asp.net/scottgu/archive/2010/12/30/asp-net-mvc-3-layouts-and-sections-with-razor.aspx
Any viewmodel that you pass to your view is automatically available within your master page. If you do not use RenderAction/Action which is the best approach, then you must create the necessary master page data in every action and add it to viewdata - either by having a common base class for your strongly typed viewmodel that contains all master page data or by using the viewdata dictionary.
I would strongly recommend that you go down the html.action approach though. In this way, you have a totally separate controller action for dealing with your list of categories. This action can retrieve the neccesary category data and return the categorylist usercontrol as a partialview and you will not have to worry about polluting all your other actions with this data.
As I see it, ViewData (and its relatives like ViewBag, Model, etc.) is meant for the specific current view. Your _Layout.cshtml is not specific to the current view; and it would be awkward if EVERY controller would have to pass the categories data in addition to whatever else data it needs to pass for the view.
Instead, what I do, is provide a static method in one of my helper classes that retrieves the categories from the DB. I also do some caching there, so that I do not have to hit the DB on every single request. The _Layout.cshtml then simply calls this static method. Simple and elegant.
If you wish, you can bring this out to a partial view, make it a helper method, whatever.
One note of caution though - my custom error view also uses the same _Layout.cshtml, and if the DB goes down, you get an exception trying to display the exception. ASP.NET MVC is smart enough to detect this and abort processing, but you're left with a nondescript default error page. What I did was to place try...catch statements around these dangerous calls, which quietly ignore the exception if the current page is the error view.
I've achieved something similar by having my ViewModels implement an Interface which has members that contain the menu data. In my action method I set that data. Then in my view I check to see if my view-model implements that inteface, pull the menu data out and render the menu (in a partial view actually)