Hi; i am a Asp.net software developer. i try to learn asp.net mvc. But i face to face strange thing. My contoller method name must be the same as view name or reverse. this is strange! Look please my _Layout:
<nav>
<ul id="menu">
<li>#Html.ActionLink("Home", "Index", "Home")</li>
<li>#Html.ActionLink("About", "About", "Home")</li>
<li>#Html.ActionLink("Article", "GetAll", "Article")</li>
</ul>
</nav>
article view page need GetAll method also need GetAll.cshtml. My desire: my view page name must independent of controller class'method name. My Controller:
My solution :
i think that Asp.net mvc is strange. i dislike controller' action name name must the same as view page name? how to make it? i think that View name must independent form any name
You are correct that by default your view name must be the same as your action name. However, this is easy to change. You can just called this overload of the View method in the controller and pass in whatever view name you want:
return View("SomeViewName",articles);
It doesn't have to be the same as the name of your method. By Default MVC3 will look for a View with the same name but you can create a View with ANY name and tell MVC to return that View:
return View("MyView",articles);
I have 2 comments:
GetAll() in MVC would typically be called Index (as in articles index)
You could name your Method something and return a view with a different name,
public ActionMethod GetAll()
{
return View("Index");
}
Related
I have navbar elements in my _Layout.cshtml which depend on the controller being called. On a search page there will be no navigation but in order to keep the style of the site consistent the navbar itself will remain. I'm not sure what is the most accepted and idiomatic way of performing this work.
_Layout.cshtml
(etc)
<nav>
<div class="container">
<ul>
#if (TempData.ContainsKey(KeyChain.ItemKeyTempDataKey))**
{
var itemKey = TempData[KeyChain.ItemKeyTempDataKey] as ItemKey;
<li>#Html.ActionLink("Overview", "Index", "Overview", itemKey, new { })</li>
<li>#Html.ActionLink("Purchasing", "Index", "InvoiceMovementHistory", itemKey, new { })</li>
<li>#Html.ActionLink("Profit Trends", "Index", "SalesMovement", itemKey, new { })</li>
<li>#Html.ActionLink("Coupons", "Index", "Coupon", itemKey, new { })</li>
<li>#Html.ActionLink("Deals", "Index", "WebDeal", itemKey, new { })</li>
<li>#Html.ActionLink("Update Log", "Index", "UpdateLog", itemKey, new { })</li>
}
</ul>
</div>
</nav>
(etc)
ItemKey.cs
public class ItemKey
{
public string Upc { get; set; }
public string VendorItemCode { get; set; }
public int Vendor { get; set; }
}
UpdateLogViewModel.cs
public class UpdateLogViewModel
{
public IEnumerable<UpdateLogEntryViewModel> UpdateLogEntries { get; set; }
}
UpdateLogController.cs
public ActionResult Index(ItemKey itemKey)
{
TempData[KeyChain.ItemKeyTempDataKey] = itemKey;
//etc uses itemkey to get data in order to generate updateLogViewModel
return updateLogViewModel();
}
Things I thought of
Using TempData (as above) to display the navbar elements if the itemkey is populated. TempData, however, is kind of on its way out and feels hacky.
Add a rendersection to the navbar, put the navbar elements in a renderaction and populating them in the section on every view that uses it (which is essentially every view EXCEPT the search view). This just violates DRY on overdrive, but seems to me to be the idiomatic thing.
Derive a secondary sublayout that is an "itemlayout", which would be typed to itemkey and drops the tempdata check. At least provides compile-time checking as long as developers use the itemlayout for item subscreens. But, call me crazy, that's worse because now all of my derived view's viewmodels have to depend on the type from the itemlayout viewmodel. However, this has the advantage of making the dependency clear: if you're going to use this layout, you must derive from this viewmodel that contains an itemkey property. This seems like the most idiomatic way, but I hate the idea of a typed layout.
Move the navbar on to every view page. I will almost certainly not do this, but it should be mentioned that the possibility exists.
So, is there another way I could perform this action idiomatically in MVC, or is one of the options I've listed above the preferred method?
TempData is a bad way to send data around in an ASP.NET MVC application. It's a holdover from the Viewstate days. It's a bad idea.
Instead of TempData, you can make your Navbar a RenderAction, and pass it information from each page it appears on (from the view). You can also use an HtmlHelper (outlined below) to render the links. There's no sense in having all this cooped up in the Layout.cshtml, since it'll have code that doesn't apply to it.
Effectively what you're trying to do is show the active page in a different style.
There are quite a few ways of doing that.
Highlighting current page ASP.NET MVC
Highlighting current page in navigation ASP.NET MVC
And K. Scott Allen has a blog post about the various methods he uses.
All of this tricks have one thing in common: They all suggest using an HTMLHelper that simply looks at the current page.
The most natural and canonical way to do this in MVC is by overriding partials. Create a specific controller called SearchController.
Then, create a partial called _Navigation.cshtml in your "Views\Shared" folder, like this:
<nav>
<div class="container">
<ul>
<li>...</li>
<li>...</li>
<li>...</li>
</ul>
</div>
</nav>
And then, in "Views\Search" create another partial called _Navigation.cshtml like this:
<nav>
<div class="container">
<p>Nothing to see here.</p>
</div>
</nav>
Then, in your layout, when you do this:
#Html.Partial("_Navigation")
The precedence of the view resolver will pick up the latter on the search page and the former everywhere else.
Edit: Based on what I can gather from your comments and updates, you have a controller action that receives some values in the query string and you want to persist those in your action links. The answer is easy.
Assuming the URL /UpdateLog?Upc=xyz&VendorItemCode=abc&Vendor=1 hits your UpdateLog.Index action, then in your view, your links just need to be, e.g.:
#Html.ActionLink("Purchasing", "Index", "InvoiceMovementHistory")
If InvoiceMovementHistory.Index also accepts those parameters, the MVC framework will automatically map the current route parameters to the target route when it is generating the link. No need for you to manage the values at all.
If they're not query string parameters but URL segments, the same applies.
This stateless passing of context from request to request via GET parameters is the ultimate "idiomatic" way of doing this sort of thing in MVC and on the web in general.
Use this in conjunction with the view overriding I described above and you have a very easy way of doing this and switching the nav off for specific pages. That said, given the emerging clarity I would forego the partials and just check a ViewBag.DisableNav property in your layout:
#if (!ViewBag.DisableNav)
{
<nav>
<div class="container">
<ul>
<li>#Html.ActionLink("Purchasing", "Index",
"InvoiceMovementHistory")</li>
<li>...</li>
<li>...</li>
</ul>
</div>
</nav>
}
I am starting with MVC5 and created first project from MVC5 Getting Started.
Now trying with Partial Rendering and added a method in MoviesController as below
[ChildActionOnly]
public ActionResult PriceRange()
{
var maxprice = db.Movies.Max(m => m.Price);
var minprice = db.Movies.Min(m => m.Price);
ViewBag.MaxPrice = maxprice;
ViewBag.MinPrice = minprice;
return PartialView();
}
It sets Min and Max price from Movies collection into ViewBag that are later displayed at view. I am trying to render it on different views.
First i tried to render it at Views/Movies/Index.cshtml as below
#{Html.RenderAction("PriceRange");}
It works well there and results displayed correctly because it is using MoviesController, the same class where method PriceRange defined.
Then i tried to render it at Views/Hello/Index.cshtml (this view is using HelloWorldController) with following code (first passing Action name then Controller name)
#{Html.RenderAction("PriceRange", "MoviesController");}
Here it is giving run-time error
The controller for path '/HelloWorld/Index' was not found or does not implement IController.
Here is complete code from Views/Hello/Index.cshtml
#{
ViewBag.Title = "Movie List";
}
<h2>My Movie List</h2>
<p>Hello from our view template</p>
#{Html.RenderAction("PriceRange", "MoviesController");}
I found few examples through Google, they are calling RenderAction helper the same way, first passing Action name then Controller name.
I couldn't understand what the wrong i am doing here.
Can someone point out?
It might be that you're adding the "Controller" postfix to the controller name which isn't required.
Try:
#{Html.RenderAction("PriceRange", "Movies");}
The controller name needs to be "Movies" and not "MoviesController". Because now I believe it is looking for a controller called "MoviesControllerController".
Being new to ASP MVC, I met the following problem.
I have a list of "repeating" controls on my page, which are presented by the following Razor code:
#model BankBLL.Interfaces.ISecureFolder
...(some irrelevant code here)
<header><h3 >Commitee list</h3></header>
#foreach (var commitee in Model.Commitees)
{
<a href="#Url.Action("CommiteePage", "SecureFolder", commitee)">
<div class="commiteeButtonImageContainer">#commitee.Name</div>
<img src="~/Images/CommiteeButtonImage.png"/>
</a>
}
Model.Commitees here is a List of ICommitee objects, that means that I am trying to "bind" each Url.Action to a corresponding ICommitee commitee object.
However, when it comes to my controller action:
public ActionResult CommiteePage(ICommitee commitee)
{
return View("CommiteePage", commitee);
}
looks like I am making it a wrong way, because application returns "Cannot create an instance of an interface." error, that means that application is unable to retreive required commitee object when the action link is clicked.
Is there a way to bind each row "item datacontext" (ICommitee object in this case) to correspoding Url.Action?
Unfortunately could not post it earlier due to reputation regulations.
Finally resolved this issue due to good explanation at:
HTML.ActionLink method
When you try to pass an argument from Url.Action or Html.ActionLink - you have to specify explicitly the final "null" argument responsible for html arguments.
In my case the following code works correctly:
slightly changed controller action (now receives just name instead of commitee object itself)
public ActionResult CommiteePage(string commiteeName)
{
return View("CommiteePage", SecureFolder.Commitees.First(o=>o.Name == commiteeName));
}
and changed syntax for html calling this action:
#foreach (var commitee in Model.Commitees)
{
<a href="#Url.Action("CommiteePage", "SecureFolder", new { commiteeName=commitee.Name }, null)">
<div class="commiteeButtonImageContainer">#commitee.Name</div>
<img src="~/Images/CommiteeButtonImage.png"/>
</a>
}
Now view correctly passes the name of selected commitee to controller so that I can redirect to corresponding commitee view.
Thank you all for helping to resolve this issue!
The main problem is that the default model binder cannot create an instance of an interface. Try to be more specific, i.e. public ActionResult CommiteePage(ImplementedCommiteeType commitee). You can also create a CommiteeViewModel: ICommitee class in which you can transport your structures (in Controllers and Views only).
Or you can create your own model binder which knows what to implement. This is slightly more complicated.
I'm using MVC3 and Spark.
I need to add a class to a LI if a certain ViewBag element is set to X.
<li id="menu-home" class="active?{ViewBag.Active=='home'}" >${Html.ActionLink("Home", "Index", "Site")}</li>
Like the above. This doesnt work, however wondering if there is a way to approach this?
Here are the steps I took and it worked for me:
Create a new ASP.NET MVC 3 project using the default template and the Razor view engine
Install the Spark.Web.Mvc3 NuGet package.
Change the Index action of HomeController to look like this:
public ActionResult Index()
{
ViewBag.Active = "home";
return View();
}
Rename ~/Views/Home/Index.cshtml to ~/Views/Home/Index.spark and make it look like this:
<li id="menu-home" class="active?{ViewBag.Active == 'home'}">
${Html.ActionLink("Home", "Index", "Site")}
</li>
Run the project
The generated HTML is:
<li id="menu-home" class="active">
Home
</li>
Remark: Everytime I see someone using ViewBag instead of strongly typed views with view models I feel in the obligation to point this as a bad practice.
How can I get the actual "Main-Controller" in a RenderAction?
Example:
MyRoute:
{controller}/{action}
My url my be:
pages/someaction
tours/someaction
...
In my Site.Master I make a RenderAction:
<% Html.RenderAction("Index", "BreadCrumb"); %>
My BreadCrumbController Action looks like this:
public ActionResult Index(string controller)
{
}
The strings controller contains "BreadCrumb" (which is comprehensible because actually I am in BreadCrumbController).
What's the best way to get the "real" controller (e.g. pages or tours).
Parent view/controller context
If you use MVC 2 RC (don't know about previous releases) you can get to parent controller via view's context, where you will find a property called:
ViewContext ParentActionViewContext;
which is parent view's context and also has a reference to its controller that initiated view rendering...
Routing
It seems to me (from your question) that you have requests with an arbitrary number of route segments... In this case you have two options:
Define your route with a greedy parameter where actions in this case will catch all actions in your request URL
{controller}/{*actions}
Create a custom Route class that will handle your custom route requirements and populate RouteData as needed.
the second one requires a bit more work and routing knowledge but it will help you gain some more knowledge about Asp.net MVC routing. I've done it in the past and it was a valuable lesson. And also an elegant way of handling my custom route requirements.
Could you pass it as a parameter to the controller?
--Site.master--
<% Html.RenderAction("Index", "BreadCrumb"
new { controller = ViewData["controller"] }); %>
--BreadCrumbController.cs--
public ActionResult Index(string controller)
{
}
--ToursController.cs--
public ActionResult SomeAction(...)
{
// ....
ViewData["controller"] = "Tours"
// You could parse the Controller type name from:
// this.ControllerContext.Controller.GetType().Name
// ....
}
What do you mean with "real" controller? Your action points to one controller.
Do you mean the previous controller? So: the controller that was used to render your view where your link was created that points to your breadcrumbcontroller?
Unless you add the name of that controller to the link as a parameter, there is no way to get to that.