ActionLink is executing properly but not passing id variable.
This is executed first in my main view
<li>#{ Html.RenderAction("ReviewAverage", "Home", Model.TripId); }</li>
which executes this:
[ChildActionOnly]
public ActionResult ReviewAverage(int? id)
{
Trip trip = db.Trips.Find(id);
List<int> values = trip.Reviews.Select(review => review.Rating).ToList();
double average = values.Average();
ViewData["ReviewAverage"] = average;
return PartialView("_ReviewAverage", id);
}
This is the partial view that above method returns and which doesn't for some reason pass the id = Model even though Model variable is definitely set.
The actionLink below is not passing the id to my controller
#model int
<li>#Model</li>
#Html.ActionLink("Reviews (avg: "+ ViewData["ReviewAverage"] +")", "Reviews", "Home", new { id = Model }))
Which should lead to with that id
public ActionResult Reviews(int? id)
{
Trip trip = db.Trips.Find(id);
List<Review> reviews = trip.Reviews;
return View(reviews);
}
When creating a link to a controller action in ASP.NET MVC, using the generic ActionLink method is preferable, because it allows for strongly typed links that are refactoring friendly. Try this:
#Html.ActionLink("Reviews", "Home", new { id = item.Id })
For more information you might have a look at How do I set a click event in C#?. Hope this helps...
The third parameter of the RenderAction overload is the route values. The method expects a dictionary with key and value. Make sure the key matches your action method parameter.
You can pass an annonymous object with id property (which matches your ReviewAverage action method param name.
#{ Html.RenderAction("ReviewAverage", "Home", new { id= Model.TripId} ); }
Now for your action link, you are using the overload incorrectly, The overload you are using expects the last parameter to be an anonymous object need to build the html attributes(ex : css class/ other html attributes).
You can use the correct overload which takes 5 parameters ( 4th one is route values nd 5th one is htmlAttributes)
#Html.ActionLink("Reviews (avg: "+ ViewData["ReviewAverage"] +")",
"Reviews", "Home", new { id = Model },null))
Related
I could use some assistance with this. I have a controller action I've modified to take in a string. When I try to pass the string in the URL its still returning null. All its suppose to do is take in the string, then query based on that string and return the user's name in header tags. Do I need to modify my routes?
Header in View
<div class="nameBlock col-md-push-9">
<h4>Welcome #ViewBag.HeaderName</h4>
</div>
Controller Method
public ActionResult Index(string LanID)
{
var name = db.UserInfoTable
.Where(x => x.Lan_Id == #LanID)
.Select(x => new UserViewModel
{
AppUserName = x.FirstName + " " + x.LastName
}).FirstOrDefault();
ViewBag.HeaderName = name.AppUserName;
return View();
}
Unless you have set up your routing to do something with the LanID parameter, the MVC model binder won't know what to do with it. So you should either change the URL you want to use and tell MVC the name of the parameter by using a query string:
localhost:(portnumber)/DevArea/Dev/Index?LanID={value}
Or add an additional route to your configuration, for example (this is from memory so might need tweaking):
routes.MapRoute(
name: "LanID",
url: "{area}/{controller}/{action}/{LanID}",
defaults: new
{
area = "DevArea",
controller = "Dev",
action = "Index"
}
);
I have a list of products with some title field, e.g.:
ID Title
1 TShirts CK
2 Brand new books
3 Selling whatever I have useless
Ok, I'm calling a Detail method this way:
<a href='#Url.Action("Detail", "Products", new { productId = 3 })'>See detail</a>
[Route("detail/{productId:int}")]
public ViewResult Detail(int productId) {
//...
return View();
}
The generated URL is:
http:example.com/products/detail/3
Well, the idea I want is show an URL like this:
http://example.com/products/detail/3/selling-wathever-i-have-useless
Is there a nice and clean way to do this, based on the given scenario?
I believe this has been termed a URL slug, which will make it 100x easier to search for. I suggest you start: https://stackoverflow.com/a/2921135/507025 for an algorithm to help you slugify your url.
If you have a database with the information, you might want to save the slugified description to it so you can check for duplicates upon the creation of a new item.
Routing will be very similar although you'll be needing to change your routing attribute to:
[Route("detail/{productName:string")]
public ViewResult Detail(string productName)
{
return View();
}
You can then search your DB for the slugified description to return the item searched for OR use the terms given in a search to return multiple results.
There are probably different ways of doing this but now that you know it's called 'slug' you will have an easier time finding info about them.
You can have a route as below and when you define Url.Action you can pass product title to some kind of method which convert the given text to URL friendly text.
Example
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{productId}/{title}",
defaults: new { controller = "Home", action = "Index", productId = UrlParameter.Optional, title = UrlParameter.Optional }
);
<a href='#Url.Action("Index", "Home", new { productId = product.ID, title = ToFriendlyUrl(product.Title) })'>See detail</a>
public ViewResult Detail(int productId, string title)
{
// ...
return View();
}
Thanks!
I have the following sample data from a MySQL table.
My question is in my MVC 4 project how can I get routing to work so that if a user goes to the URL www.mydomain.com/products/apples/ I want the call to actually be the view of www.mydomain.com/products/index/1, how do I achieve this ?
You could create this one route:
routes.MapRoute("Product", "products/{productName}",
new {controller = "products", action="index"});
This route is saying:
When a request comes in with a URL matching the pattern: "products/{productName}" (e.g. http://www.example.com/products/apple), pass that over to the "index" action in the "ProductsController" to handle and pass the segment of the url indicated by the {productName} placeholder, in to that action as the parameter called "productName".
Then your action would be:
[HttpGet]
public ActionResult Index(string productName) {
// Lookup product from DB
// do stuff
var viewModel = ...;
return View(viewModel);
}
So, when a request comes in for products/apple, "apple" gets passed into the Index action as the productName parameter.
Put below code in you RouteConfig.cs before the default route expression.
foreach (var item in _context.products)
{
routes.MapRoute(
item.Url,
"products/" + item.Url,
new { controller = "products", action = "index", #id = item.ProductId }
);
}
In above _context.products is used to retrieve the products from the DB. You can modify this according your models.
I tried to use the debugger but I can't seem to get anywhere. I can't step into Html.RenderAction(), it's in my master page.
I read that it gets the value "automatically" through routing. How does that work?
// "Nav" is the name of the controller housing the "Menu" action
// This is called in Site.Master
<% Html.RenderAction("Menu", "Nav"); %>
// where does "category" come from?
public ViewResult Menu(string category)
{
}
I made this according to a book, but I can't find an explanation in there. The category supposedly comes from the URL automatically into the parameter.
On a related note: Do you advise to download the source code for MVC to work properly, or would that complicate my efforts more than it would help?
The Category parameter is being picked up from the following routing entry
routes.MapRoute(null, "{category}", // Matches ~/Football or ~/AnythingWithNoSlash
new { controller = "Products", action = "List", page = 1 }
);
so if /Football is entered then it is supplied as a parameter to the ViewResult Menu in the
which in turn calls
public ViewResult List(string category, int page = 1)
{
var productsToShow = (category == null)
? productsRepository.Products
: productsRepository.Products.Where(x => x.Category == category);
var viewModel = new ProductsListViewModel {
Products = productsToShow.Skip((page - 1) * PageSize).Take(PageSize).ToList(),
PagingInfo = new PagingInfo {
CurrentPage = page,
ItemsPerPage = PageSize,
TotalItems = productsToShow.Count()
},
CurrentCategory = category
};
return View(viewModel); // Passed to view as ViewData.Model (or simply Model)
}
so later within the view master when the render action is called
<% Html.RenderAction("Menu", "Nav"); %>
it can pick up on the category parameter in the route i.e. {category}
public ViewResult Menu(string category)
{
// Just so we don't have to write this code twice
Func<string, NavLink> makeLink = categoryName => new NavLink
{
Text = categoryName ?? "Home",
RouteValues = new RouteValueDictionary(new {
controller = "Products", action = "List",
category = categoryName, page = 1
}),
IsSelected = (categoryName == category)
};
// Put a Home link at the top
List<NavLink> navLinks = new List<NavLink>();
navLinks.Add(makeLink(null));
// Add a link for each distinct category
var categories = productsRepository.Products.Select(x => x.Category);
foreach (string categoryName in categories.Distinct().OrderBy(x => x))
navLinks.Add(makeLink(categoryName));
return View(navLinks);
}
}
The Html.RenderAction call renders the action with method name "Menu", in the controller "Nav".
The routing file contains patterns for how to populate method parameters (and resolve overloaded actions). The routing file is usually found in ~/Global.asax, in the RegisterRoutes method. This file should contain multiple calls to RouteCollection#MapRoute, which maps a certain URL pattern to a new object with particular variables.
Your routes must include a single string mapping somewhere, which captures anything without a slash into a variable called category. That is then passed to the Menu action method.
Addendum: Try looking here for more information.
I can't seem to retrieve an ID I'm sending in a html.ActionLink in my controller, here is what I'm trying to do
<li>
<%= Html.ActionLink("Modify Villa", "Modify", "Villa", new { #id = "1" })%></li>
public ActionResult Modify(string ID)
{
ViewData["Title"] =ID;
return View();
}
That's what a tutorial I followed recommended, but it's not working, it's also putting ?Length=5 at the end of the URL!
Here is the route I'm using, it's default
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
);
Doesn't look like you are using the correct overload of ActionLink. Try this:-
<%=Html.ActionLink("Modify Villa", "Modify", new {id = "1"})%>
This assumes your view is under the /Views/Villa folder. If not then I suspect you need:-
<%=Html.ActionLink("Modify Villa", "Modify", "Villa", new {id = "1"}, null)%>
In MVC 4 you can link from one view to another controller passing the Id or Primary Key via
#Html.ActionLink("Select", "Create", "StudentApplication", new { id=item.PersonId }, null)
Don't put the # before the id
new { id = "1" }
The framework "translate" it in ?Lenght when there is a mismatch in the parameter/route
On MVC 5 is quite similar
#Html.ActionLink("LinkText", "ActionName", new { id = "id" })
The ID will work with # sign in front also, but we have to add one parameter after that. that is null
look like:
#Html.ActionLink("Label Name", "Name_Of_Page_To_Redirect", "Controller", new {#id="Id_Value"}, null)
If the target action requires a parameter, you can use an anonymous object to pass parameter values:
#Html.ActionLink(“View Movies”, “Index”, “Movies”, new{id=1},null)
Where:
-View Movies-->String LinkText
-Index--> string ActionName
-Movies-->string ControllerName
-new{id=1}-->(object) Is the parameter values that you want to pass
-Null-->object htmlAttributes
*For Example:
This should generate a link like the following:
/movies/index/1