Routing with multiple parameters in ASP.NET MVC - asp.net-mvc

I'm new to ASP.NET and I'm struggling to understand how routing works. In my project I've managed to create routing for; login, logout, create new user and delete user.
I've created cards containing dates and a stretched-link with the purpose to act as a booking-table (click on a card to book the said time).
View code:
When I click on the link I want to pass forward the user as a string and the time as DateTime(or string). If I replace the url.action argument "TimeSlot.ToString()" with null my routing "works", but of course with the exception that only the user is passed forward to my controller.
#model MyProject.Models.BookingSchedule
...
<div class="row" style="padding-top: 50px; border:solid">
#{ foreach (var TimeSlot in Model.GetAllAvailableTimes())
{
<div class="col-sm-1" style="padding:10px">
<div class="card text-md-center">
<a class="stretched-link" href="#Url.Action("BookTime","Booking",new { user = Model.UserName }, TimeSlot.ToString())">#TimeSlot.ToString()</a>
</div>
</div>
}
}
</div>
Controller:
So far I've just created a mockup code for my controller, its only purpose is to reload my view and redisplay available times together with the time I've booked. For now, I just want to see if my routing passes all parameters to my controller (which it doesn't):
public ActionResult BookTime(string user, string Time)
{
return View("BookingPage", bookingSchedule(user));
}
Routing:
routes.MapRoute(
"BookTime",
"Booking/{Action}/{user}/{time}",
new { controller = "Booking", action = "BookTime", user = UrlParameter.Optional, time = UrlParameter.Optional }
);
When I run my code I get the following error:
How do I create a routing with two or more parameters and what are the key aspects that I need to keep in mind?
I've tried to get knowledge from the Microsoft-docs but as I've not yet managed to overcome this issue I'm hoping someone here could explain it.
https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/routing?view=aspnetcore-6.0#attribute-routing
If I set my href to "href="#Url.Action("BookTime","Booking",new { user = Model.UserName }, TimeSlot.ToString())" then I get the error message above.
If I set href to href="#Url.Action("BookTime","Booking",new { user = Model.UserName }, null)" it will route correctly but of course it doesn't pass my parameters (see photo below):
(in the picture/code above I changed the name of the controller to BookingPage as is the name of my viewpage. I did this to rule out any routing issues)

just add an attribute route
[Route("~/Booking/BookTime/{user?}/{time?}",
public ActionResult BookTime(string user, string Time)
{
return View("BookingPage", bookingSchedule(user));
}
and fix your link
href= ( "Book Time",
"BookTime",
"Booking",
new { user = Model.UserName, time = TimeSlot.ToString() },
null )

Related

Can't get new controller or view to work in ASP.Net MVC

I'm making an project for a online car dealership, for a final exam in ASP.NET.
I created a new project, but when i click to edit an item in the site, it just opens the path to the View for editing, with the correct ID of item: https://localhost:44335/Cars/EditCar/1 but the displayed page is just black with nothing in it, without getting any errors.
This is the code in the controller:
[HttpPost]
public IActionResult EditCar(Car carToEdit)
{
this.carService.EditCar(carToEdit);
return RedirectToAction("Index");
}
This is in the services:
public void EditCar(Car carToEdit)
{
var editedCar = this.GetById(carToEdit.Id);
editedCar.Picture = carToEdit.Picture;
editedCar.Brand = carToEdit.Brand;
editedCar.Model = carToEdit.Model;
editedCar.Engine = carToEdit.Engine;
editedCar.HorsePower = carToEdit.HorsePower;
editedCar.ManufactureDate = carToEdit.ManufactureDate;
editedCar.TotalMileage = carToEdit.TotalMileage;
editedCar.Features = carToEdit.Features;
editedCar.SalePrice = carToEdit.SalePrice;
}
And this is the code applying to the item i want to edit:
<a class="text-white float-right" asp-controller="Cars" asp-action="EditCar" asp-route-id="#car.Id"><i class="bi bi-pencil-square"></i></a>
I tried to modify the controller return paths, double checked everything from an old project with the same functions to make sure i haven't messed anything, but i can't get it to work. In the project, i have a seperate controller for the different pages in the site, so i made a new project with the very basic of what my exam project contains, but instead kept everything in the HomeController only, and it worked. So i think this might be something wrong with my controller.
Remove an ancor tag from your code. It always creates a GET request. You have to use a sumbit method in you view since you need to POST the whole car model, not just a car Id
#model Car
....
#using (Html.BeginForm("EditCar", "Cars", FormMethod.Post))
{
#Html.HiddenFor(m => m.Id)
..... your view html
<input type="submit" value="Submit Data" />
}

Passing a dropdown selected value to a contorller

I'm playing around in an MVC application and am trying to figure out something that seems pretty straight forward.
I have a Index.cshtml file in my Views/Home/ folder that is pretty simple (below)
Index view
...
<div>
Search
#Html.DropDownList("selection", MyProject.Util.Lists.GetMyList(), "Select One")
#Html.ActionLink("Search", "Index", "Search", new { st = xxx }, null)
</div>
...
I also have a Search controller that needs to take a "st" value and looks like this
public class SearchController : Controller
{
// GET: Search
public ActionResult Index(string st)
{
ApplicationDbContext db = new ApplicationDbContext();
List<Report> filteredReports = db.Reports.Where(r => r.Tag == st).ToList();
return View(filteredReports);
}
}
What I'm not sure of how do is grab what ever value is selected from the drop down and add that to my Html.ActionLink where I just have 'xxx' now.
Clicking on the link usuallly does a new GET request to the href attribute value of the link. It will not send any data from your form.
You need to use javascript and hijack the click event on the link and append the selected option value from the SELECT element as querystring to that and send it.
So give an id to the link which you can use later to wireup the click event
#Html.ActionLink("Search", "Index", "Search", null, new {#id="search"})
And the javascript
$(function(){
$("#search").click(function(e){
e.preventDefault();
window.location.href=$(this).attr("href")+"?st="+$("#selection").val();
});
});
Another option is to use the form submit. Wrap your select element inside a form and have a submt button which sends the form to the action method. This method does not need javascript. But your select input element name should match with your parameter name.
#using(Html.BeginForm("Index","Search",FormMethod.Get))
{
<div>
Search
#Html.DropDownList("st", MyProject.Util.Lists.GetMyList(), "Select One")
<input type="submit" value="Search" />
</div>
}
If you do not prefer to have button, but need the link element, you can use javascript to submit the form (like we did in the first approach). With this approach you do not need to manually append the querystring.
You have to call a AJAX POST call to your controller which store selected value, and then when your action link event fire get stored value from there.
AJAX:
$.ajax(
{
url:your url,
data:{"st":dropdown selected value},
type:"post"
});
Controller:
public ActionResult Index()
{
string st=TempData["st"].ToString();
ApplicationDbContext db = new ApplicationDbContext();
List<Report> filteredReports = db.Reports.Where(r => r.Tag == st).ToList();
return View(filteredReports);
}
public void SetSt(string st)
{
TempData["st"] = st;
}

In MVC3, how can I create a route like mydomain.com/chigago and hit that route from a GET form?

I have an application that shows locations on a map. I have created a route so that I can have nice hackable URLs, like http://www.mydomain.com/paris. This works fine just typing in the URL, but I have a search form on the home page that sends a GET request. When the form is submitted, the URL displayed in the location bar is in the format http://www.mydomain.com/Dashboard?location=paris. Normally this wouldn't matter too much as it's hitting the correct action, but I have a backbone.js application running the show and it's particular about the URL structure.
It may be impossible to do what I need without javascript or a redirect, because the location isn't known when the form ACTION attribute is populated - but can anyone confirm?
Here are my routes.
public static void RegisterRoutes( RouteCollection routes )
{
routes.IgnoreRoute( "{resource}.axd/{*pathInfo}" );
routes.MapRoute(
String.Empty,
"{location}",
new {
controller = "Dashboard",
action = "Index",
id = ""
}
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new
{
controller = "Home",
action = "Index",
id = UrlParameter.Optional
} // Parameter defaults
);
}
Here is the controller.
public class DashboardController : Controller
{
[HttpGet]
public ViewResult Index(string location)
{
return View(new AccItemSearch { Location = location });
}
}
Here is the form.
#using (Html.BeginForm("Index", "Dashboard", FormMethod.Get)) {
<h2>Where are you going?</h2>
<input type="text" placeholder="Area, town or postcode" id="location" name="location"/>
<button>Search!</button>
</div>
}
To clarify: the problem I want help with is how to have the user submit the form then land on a page with the URL: http://www.mydomain.com/searchterm and thus match the route, rather than on a page that with the URL http://www.mydomain.com/Dashboard
You will not be able to change the action attribute of the form during HTML generation (i.e. server side) as you simply don't know what it should point to. So if you need the URL to end up being the exact search term the easiest bet is probably to change the action attribute to it with JavaScript before the form is submitted, and have a controller that catches all urls that follow the www.domain.com/searchterm pattern.
You can't really redirect to a specific action because then that would become the URL returned to the browser, and I doubt you want one action per search term.
HTML:
<form method="post" id="myform">
<input type="text" id="searchterm" />
<input type="submit" value="Search" />
</form>
jQuery:
$(function () {
$("#myform").submit(function () {
var searchVal = $("#searchterm").val();
$(this).attr("action", searchVal);
});
});
Route:
routes.MapRoute(
"",
"{searchterm}",
new { controller = "Home", action = "Search" }
);
Note that this has to be put before the default route(s).
Action:
public ActionResult Search(string searchterm)
{
//do stuff
}
Now if a visitor enters the term "Alaska" and submits the search form, they will end up on domain.com/Alaska.
That form should be a POST to submit the form data.
The search should be a submit button
<input type="submit" name="Search" value="Search" />
Otherwise it looks good, unless you have a conflicting route. Tested your route in isolation and it seems fine. It's just that location is not being sent.

#Html.ActionLink not Rendering as Expected

I have this in my Global.asax.cs:
routes.MapRoute(
"User",
"User/{username}/{action}",
new { controller = "User", action = "Index", username = "*" }
);
Then on my _Layout.cshtml I have this code:
<ul id="menu">
#if (!String.IsNullOrEmpty(Context.User.Identity.Name))
{
<li>#Html.ActionLink("Home", "Home", new { controller = "User" }, new { username = Context.User.Identity.Name })</li>
}
</ul>
</div>
</div>
The thing is, it will render the link properly the first time it swings through here. (Link will be /User/rob/Home where "rob" is a username. If I navigate elsewhere on the page and then click back on my link, the link is rendered as /User/*/Home. When I step through the code, Context.User.Identity.Name is correct every time.
Am I missing something really basic here? I'm not sure what to search for.
That's exactly what you should expect given that route. You don't specify username in the route values dictionary but in the HTML attributes, so it takes the default from the route, *. You should be using the signature that allows you to specify both the controller and the action as strings with additional route values in the dictionary.
#if (!String.IsNullOrEmpty(Context.User.Identity.Name))
{
<li>#Html.ActionLink("Home", "Home", "User" new { username = Context.User.Identity.Name }, null )</li>
}

Actionresult doesnt get called by routelink. Formcollection the culprit?

I am fairly new to MVC. I am trying to set up a search page that searches a database and returns results. The search box is within a Html.BeginForm in my View, and looks like this:
<% using (Html.BeginForm())
{ %>
<%= Html.TextBox("searchBox", null, new { #id = "searchBox" })%>
<div id="searchButtonsDiv">
<input type="submit" value="Search" />
</div>
<% } %>
//Results are returned in a ul and orgainized
//Pagination below
<% if (Model.HasPreviousPage)
{ %>
<%= Html.RouteLink("Previous", "SearchResults", new { page = (Model.PageIndex - 1) })%>
<% } %>
<% if (Model.HasNextPage)
{ %>
<%= Html.RouteLink("Next", "SearchResults", new { formCollection = "", page = (Model.PageIndex + 1) })%>
<% } %>
I am using a FormCollection to pass to my controller that looks like this:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Index(FormCollection formCollection, int? page)
{
var searchString = formCollection["searchBox"];
var results = resultsRepository.GetResults();
var paginatedResults = new PaginatedList<Driver>(results, page ?? 0, pageSize);
return View(paginatedResults);
}
So far so good. When I type a word and press the submit button, Index gets called and the database returns accordingly. The ul gets populated with the results, and when there are more than pageSize results (10 in my case), the Next link shows up.
When I click "Next", the default page just loads. No pagination or anything like that. I'm pretty sure it has to do with the fact that my Index ActionResult has a FormCollection as a paramater. I thought I read somewhere that only strings/ints can be handled? Here is the MapRoute:
routes.MapRoute(
"SearchResults",
"Drivers/Index/{formCollection}/{page}",
new { controller = "Drivers", action = "Index", formCollection = "", page = "" }
);
Am I completely missing something or is there a way to handle this? I know I could just use jquery/ajax to send the string contained in the search listbox, but I don't want to do that because later I plan on adding checkbox's as means of filtering searches, etc.
I tried several different ways of setting the formCollection's value, including creating a new FormCollection that adds the searchBox, and just passing strings, etc.
The FormCollection argument in the action isn't the problem. That will always work.
It absolutely does not belong in your route, however! Just get rid of that and you'll probably solve the problem. Form elements don't go in the URI, and only stuff in the URI should be in the route.
It's not how I'd write that action signature, however. I'd suggest:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Index(string searchBox, int? page)
{
var results = resultsRepository.GetResults();
var paginatedResults = new PaginatedList<Driver>(results, page ?? 0, pageSize);
return View(paginatedResults);
}
Finally: You shouldn't return a View from a POST in this case. This will cause weird behavior for the user; e.g., when they press refresh their browser will warn them about re-submitting the form.
You should either:
Use a GET, not a POST for search results.
Redirect instead of returning a view.
I'd pick the first, personally.

Resources