I'm trying to implement a basic search page in the web app I'm developing. Right now the page looks like this
When a user enters a last name, the controller gets called to search the backend Microsoft SQL Server database for all accounts with that Last name
Right now the HTML form looks like this
#using (Html.BeginForm("SearchAct", "HomeController", FormMethod.Post))
{
<form>
<div>
Last Name:<br>
<input type="text" id="nameToFind">
<input type="button" id="submitId" value="submit" />
</div>
</form>
}
It's supposed to call this controller
[HttpPost]
public void SearchAct()
{
Console.WriteLine();
}
which will eventually execute the search and then put the results on the page. However, I can't get the controller to be called. I set a break point on the WriteLine so I know its never getting there and I don't know what I'm doing wrong
Add name attribute to your text box. Form collection build based on only name attributes.
Change button type to submit, then it will post your form to controller.
#using (Html.BeginForm("SearchAct", "Home", FormMethod.Post))
{
<div>
Last Name:<br>
<input type="text" id="nameToFind" name="nameToFind">
<input type="submit" id="submitId" value="submit" />
</div>
}
#{
if(ViewBag.SearchKey != null)
{
<span>
Search Key: #ViewBag.SearchKey
</span>
}
}
Instead on Console.WriteLine() use ViewBag to send your required data back to view
Refer below actions
//Get Action for rendering view
public ActionResult SearchAct()
{
return View();
}
[HttpPost]
public ActionResult SearchAct(string nameToFind)
{
ViewBag.SearchKey = nameToFind;
return View();
}
Action parameter name and text box name attribute value must be same otherwise it will null
If your form contains multiple text box then read all input values form FromCollection or Request
[HttpPost]
public ActionResult SearchAct(FormCollection form)
{
ViewBag.SearchKey = form["nameToFind"];
return View();
}
correct your form HomeController should be just Home
#using (Html.BeginForm("SearchAct", "Home", FormMethod.Post))
{
<form>
<div>
Last Name:<br>
<input type="text" id="nameToFind">
<input type="button" id="submitId" value="submit" />
</div>
</form>
}
controller should accept the input parameter for your filter
[HttpPost]
public void SearchAct(string nameToFind)
{
// filter snomething
Console.WriteLine(nameToFind);
}
First: To create a form via using razor syntax:
#using (Html.BeginForm("SearchAct", "HomeController", FormMethod.Post))
{
}
That would generate a form like this:
<form method="post" action="/HomeController/SearchAct">
</form>
So, no need to create a nested form inside that.
#using (Html.BeginForm("SearchAct", "HomeController", FormMethod.Post))
{
<form> // <- here
<div>
Last Name:<br>
<input type="text" id="nameToFind">
<input type="button" id="submitId" value="submit" />
</div>
</form> // <- here
}
Second: HomeController would match the controller what the full name is HomeControllerController.
So, if you want to hit Home controller, remove Controller from HomeController in #using (Html.BeginForm("SearchAct", "HomeController", FormMethod.Post)) {}
Thirth: if you want to catch nameToFind, you can try:
[HttpPost]
public void SearchAct(string nameToFind)
{
Console.WriteLine();
}
Hope this help!
first of all we dont need to two form tag and you dont need to add controller suffix. remve that:
#using (Html.BeginForm("SearchAct", "Home", FormMethod.Post))
{
<div>
Last Name:<br>
<input type="text" id="nameToFind" name="nameToFind">
<input type="button" id="submitId" value="submit" />
</div>
}
Secondly if you need to search you need to add paramater same as the name of input type text :
[HttpPost]
public void SearchAct(string nameToFind)
{
Console.WriteLine();
}
Related
I know the post action method below is anti-pattern, but still I assume to see a new page with Name being set to null. But when I click the submit button, the page is not reloaded and I still see the old name displayed, is this a browser thing or asp.net core framework thing?
public class HomeController : Controller
{
private IRepository repository;
public HomeController(IRepository repo)
{
repository = repo;
}
// ...
public IActionResult Create() // create a Employer that has a name in the browser
{
return View();
}
[HttpPost]
public IActionResult Create(Employee model)
{
model.Name = ""; // <----------------set it to null so I expect to see the Name being empty in the reponse
return View(model);
}
}
// view file, Create.cshtml:
#model Employee
#{
ViewData["Title"] = "Create Employee";
}
<h2>Create Employee</h2>
<form asp-action="Create" method="post">
<div class="form-group">
<label asp-for="Id"></label>
<input asp-for="Id" class="form-control" />
</div>
<div class="form-group">
<label asp-for="Name"></label>
<input asp-for="Name" class="form-control" />
</div>
<div class="form-group">
<label asp-for="DOB"></label>
<input asp-for="DOB" class="form-control" />
</div>
<div class="form-group">
<label asp-for="Role"></label>
<select asp-for="Role" class="form-control" asp-items="#new SelectList(Enum.GetNames(typeof(Role)))"></select>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
Firstly, a get method to invoke the Create() action method and fill some data:
After clicking submit button, the page still shows the original name where I exepct to see a empty name since I modify it in the post action method
so why the name still exists after the second request?
So why the name still exists after the second request?
Well, altough, you have rebind your model property value. However, your current model state still remain unchanged. Therefore, you are getting the older value even after the form resubmission. To overcome this issue, please try following code snippet:
Solution:
You have clear your current model state by using ModelState.Clear(); method before assigning the new value.
So, your create method should be as bellow:
[HttpPost]
public IActionResult Create(Employee model)
{
ModelState.Clear();
model.Name = "";
return View(model);
}
Note: Rest of the code will remain unchanged.
Output:
ModelState.Clear(); wwill resolve your issue completely.
I need to get an array of elements of one model from the form. Then return the same array back to shape.
Model:
public class Check
{
public int Id { get; set; }
public string Text { get; set; }
public int Number1 {get; set;}
public int Number2 {get; set;}
}
Controller:
[HttpGet]
public IActionResult Check(string tema)
{
IEnumerable<Check> less = _context.Checks.Where(e => e.Text == tema);
return View(less);
}
[HttpPost]
public IActionResult Check(IEnumerable<Check> Checks)
{
return View(Checks);
}
Form:
#model IEnumerable<Check>
#foreach (var check in Model)
{
<form asp-action="Check" asp-controller="Home" method="post">
<p>#check?.Text</p>
<p><input type="text"asp-for="#check.Number1"/></p>
#if(Number1 == Number2)
{<p>ok</p>}
<div class="form-group">
<input type="submit" value="Ok" class="btn btn-default" />
</div>
</form>
}
I have everything working for one model, and if I pass an array, I get an empty form. How to fix it?
1.If you just want to post single model, but the backend you want receive the IEnumerable<Check>, you need add name attribute(name="[0].propertyName") to override the asp-for generated name="check.propertyName"
2.For your code, it will generate multiple forms with submit button. If you want to post an array model, you need move <form> and submit button outside foreach loop.
3.If post array model from view to controller. The IEnumerable<Check> Checks is a list model, so the model binding system would find the name by [i].propertyName.
4.<p> element text cannot passed to backend by form submit. If you want to post it, try to add a hidden input.
If post array model from view to controller, change your view like below:
#model IEnumerable<Check>
#{
int i=0; //add this...
}
<form asp-action="Check" asp-controller="Home" method="post">
#foreach (var check in Model)
{
<p>
#check?.Text
<input type="text"asp-for="#check.Text" name="[#i].Text" hidden/> #* //add the hidden input*#
</p>
//add the name...
<p><input type="text"asp-for="#check.Number1" name="[#i].Number1"/></p>
#if(check.Number1 == check.Number2)
{<p>ok</p>}
i++; //add this...
}
<div class="form-group">
<input type="submit" value="Ok" class="btn btn-default" />
</div>
</form>
If you post single model, change your view like below:
#foreach (var check in Model)
{
<form asp-action="Check " asp-controller="Home" method="post">
<p>
#check?.Text
<input type="text"asp-for="#check.Text" name="[0].Text" hidden/> #* //add the hidden input*#
</p>
#*add the name...*#
<p><input type="text"asp-for="#check.Number1" name="[0].Number1"/></p>
#if(check.Number1 == check.Number2)
{<p>ok</p>}
<div class="form-group">
<input type="submit" value="Ok" class="btn btn-default" />
</div>
</form>
}
I currently am trying to build a "Index" form which has two submit boxes, and then when you hit submit, it adds the two numbers together and then displays the result.
I tried this without a Model (Using ViewData) and managed to get it working, but I'd like it to work with a model. I have attempted this a magnitude of ways but I just cannot see what I am missing.
Journey Class - Two string values and a value (this will be int once I get it working)
public class JourneyCalculator
{
public string PostCodeOne { get; set; }
public string PostCodeTwo { get; set; }
public string DistanceValue { get; set; }
}
Then my controller which has two actions, one standard Index form and one that is supposed to Calculate the mileage. With this I've tried the following:
Passing the Model as below
Trying to pass string values from the form (This just fails)
Just simply returning View(model)
public class MapsController : Controller
{
public IActionResult Index(JourneyCalculator journeyCalculator)
{
return View(journeyCalculator);
}
[HttpPost]
public IActionResult CalculateMileage(JourneyCalculator model)
{
model.DistanceValue = model.PostCodeOne + model.PostCodeTwo;
return RedirectToAction("Index",model);
}
}
My form which I would hope passes PostCodeOne and PostCodeTwo to the "CalculateMileage" action. And then we update that value and then the Model.DistanceValue displays
<form>
<div class="form-row">
<div class="form-group col-md-6">
<label>Postcode One</label>
<input type="text" class="form-control" asp-for="PostCodeOne" placeholder="Postcode One">
</div>
<div class="form-group col-md-6">
<label>Postcode Two</label>
<input type="text" class="form-control" asp-for="PostCodeTwo" placeholder="Postcode Two">
</div>
</div>
<button type="submit" class="btn btn-primary" asp-controller="Maps" asp-action="CalculateMileage">Calculate Mileage</button>
</form>
#if(Model != null)
{
<h2>#Model.DistanceValue</h2>
}
I feel I am missing something really obvious, so any help would be fantastic!
When I hit "Submit" the URL updates to https://localhost:44301/Maps/CalculateMileage?PostCodeOne=123&PostCodeTwo=123 or https://localhost:44301/Maps/Index?PostCodeOne=123&PostCodeTwo=123 and both are failing, but I want to just pass the values to the CalculateMileage.
Thank you,
Don't redirect to the Index action, but return the Index view straight from the CalculateMileage action passing the model: return View("Index", model);
// controller
[HttpPost]
public IActionResult CalculateMileage(JourneyCalculator model)
{
model.DistanceValue = model.PostCodeOne + model.PostCodeTwo;
return View("Index", model);
}
I would also prefer to put the action/method on the form rather than the button:
<form asp-controller="Maps" asp-action="CalculateMileage" method="post">
<div class="form-row">
...
</form>
Alternatively, add a method to handle POST requests on the Index action:
[HttpPost]
public IActionResult Index(JourneyCalculator model)
{
model.DistanceValue = model.PostCodeOne + model.PostCodeTwo;
return View(model);
}
I have this form in my view which leads me to some action in the controller. The thing is I don't know how to pass parameters to the action method.
<form class="full-search" asp-controller="Movies" asp-action="Search">
<input type="search" name="searchmovie" placeholder="Search..." class="search-form">
<select name="option" form="">
<option name="option" value="category1">category 1</option>
<option name="option" value="category2">category 2</option>
<option name="option" value="category3">category 3</option>
</select>
<i class="fas fa-caret-down"></i>
<input type="submit" name="" class="submit-full-search">
<div class="search-btn-submit">
<img src="~/img/search.svg">
</div>
</form>
And this is my controller:
[HttpGet("searchmoview/{option?}/{searchmovie}")]
public IActionResult Search(string option, string searchmovie)
{
//perform some search based on the filters
return View("Search", data);
}
But when I run my code, when I click on search the url looks like this:
https://localhost:33590/Information/Search
Instead of like this:
https://localhost:44320/seachmovie/category1/{searchString}
Any idea guys on how can I pass the parameters?
Long story short - there is no out of the box way of mapping form values to a route.
The best way to pass values back to your mvc controllers is to have a view model.
Create a search view model.
//View Model
public class SearchViewModel{
public string Query {get;set;}
public string Category {get;set;}
}
In your controller you'll want to pass this down to your view or partial view.
Use "ActionResult" in .NET and "IActionResult" in .Net Core
//Controller
public class SearchController{
[HttpGet]
public ActionResult SearchBox(){
return View();
}
[HttpPost]
public ActionResult SearchBox(SearchViewModel model){
//model is now populated with your values from your form.
//ex: model.Query
return View();
}
}
Create an enum for your categories, there is many other ways to do this but this one is the simplest.
//Enum
public enum Categories
{
Category1,
Category2
}
Now in your razor view bind your view model and you're good to go.
//Razor View
#model SearchViewModel
#using (Html.BeginForm("SearchBox", "Search", FormMethod.Post))
{
#Html.TextBoxFor(x => x.Query, new { #class = "search-form"})
#Html.DropDownListFor(x => x.Catergory,
new SelectList(Enum.GetValues(typeof(Categories))),
"Select Category", new { #class= "example-class"})
<i class="fas fa-caret-down"></i>
<input type="submit" name="" class="submit-full-search">
<div class="search-btn-submit">
<img src="~/img/search.svg">
</div>
}
If you want the form values to be posted to your url you can change it to FormMethod.Get
//Razor View
#model SearchViewModel
#using (Html.BeginForm("SearchBox", "Search", FormMethod.Get))
{
#Html.TextBoxFor(x => x.Query, new { #class = "search-form"})
#Html.DropDownListFor(x => x.Catergory,
new SelectList(Enum.GetValues(typeof(Categories))),
"Select Category", new { #class= "example-class"})
<i class="fas fa-caret-down"></i>
<input type="submit" name="" class="submit-full-search">
<div class="search-btn-submit">
<img src="~/img/search.svg">
</div>
}
If you change it to get, you'll also have to change your controller to expect these in your get method. You can also just bind the model and MVC will map the parameters back into the model for you automatically.
public class SearchController{
[HttpGet]
public ActionResult SearchBox(SearchViewModel model){
//if the model values are in the URL "model" will be populated, else it will be null.
return View();
}
}
You can't. That's not how things work. A form will by default send a POST request, in which case inputs in the form are sent as part of the request body (i.e. not the URL). You can set the method to GET, instead, but then the input values will be sent as part of the query string (i.e. ?foo=bar), not as part of your path, as you're looking for.
The only way to remotely achieve what you want is to use JavaScript to manipulate the form's action based on things like the category select box changing. You'd bind to the change event of that element, and then you'd alter the form element's action attribute in some way.
I have a form in my view:
#using(Html.BeginForm("Details", "Category", FormMethod.Get))
{
<input type="text" name="param1" />
<input type="submit" value="OK" />
}
This form is on page with parameter ~/category/details?view=list I get value of view parameter from route query string and want to pass it to request generated by my form. I want to have request query string like ?param1=inputedText&view=list instead of just ?param1=inputedText. How can I do that without adding, for example, hidden input to my form and setting view value to it?
public ActionResult Details(string view )
{
ViewBag.view = view ;
return View();
}
public ActionResult Details(string view ,string param1)
{
ViewBag.view = view ;
return View();
}
This approch may help you
#using(Html.BeginForm("Details", "Category", FormMethod.Get))
{
<input type="Hidden" name="view" value=ViewBag.view >
<input type="text" name="param1" />
<input type="submit" value="OK" />
}