Selected item in ASP.NET Core razor not being passed - c#

I'm using ASP.NET Core Razor for the first time. I'm trying to simply set a bound property with the selected value of a dropdown but the property is always null when the form is posted. Also, how does one get the form posted when a selection is made? TIA
Razor page:
<td>
<form method="post">
<select asp-for="#Model.selectedReport" asp-items="#Model.Reports" class="form-control">
</select>
</form>
</td>
Code behind:
public class SubscribedReportsModel : PageModel
{
[BindProperty]
public List<SelectListItem> Workspaces { get; private set; }
[BindProperty]
public List<SelectListItem> Reports { get; private set; }
[BindProperty]
public string selectedReport { get; set; }
public async Task OnGetAsync()
{
await GetWorkspaces();
}
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
if (selectedReport != null)
{
return Page();
}
return RedirectToPage("./Index");
}
}

I'm a bit rusty it appears. All I had to do was add onchange="this.form.submit()" to the control:
<td>
<form method="post">
<select asp-for="#Model.selectedReport" asp-items="#Model.Reports" class="form-control" onchange="this.form.submit()">
</select>
#*#Html.DropDownList("ddl_Reports", Model.Reports, new {#class="form-control"})*#
#*#Html.DropDownListFor(m => m.selectedReport, Model.Reports, "--Select Report--", new{#class="form-control"})*#
</form>
</td>

Related

How do you bind a checkbox in .net core razor pages?

How do you bind a checkbox in .net core razor pages?
I'm currently having problems where the checkbox value isn't coming back when I submit the form (using post method).
Below is my code.
domain classes:
public class Restaurant
{
public int Id { get; set; }
[Required, StringLength(80)]
public string Name { get; set; }
public Meals MealsServed { get; set; }
}
public class Meals
{
public int Id { get; set; }
public bool Breakfast { get; set; }
public bool Lunch { get; set; }
public bool Dinner { get; set; }
}
from page model:
[BindProperty]
public Restaurant Restaurant{ get; set; }
public EditModel(IRestaurantData restaurantData, IHtmlHelper htmlHelper)
{
this.restaurantData = restaurantData;
this.htmlHelper = htmlHelper;
}
public IActionResult OnGet(int? restaurantId)
{
Restaurant = restaurantData.GetById(restaurantId.Value);
Restaurant.MealsServed.Breakfast = true;
return Page();
}
public IActionResult OnPost()
{
if (!ModelState.IsValid)
{
return Page();
}
restaurantData.Update(Restaurant);
restaurantData.Commit();
TempData["Message"] = "Restaurant Saved";
return RedirectToPage("./Detail", new { restaurantId = Restaurant.Id });
}
from razor page:
<form method="post">
<input type="hidden" asp-for="Restaurant.Id" />
<div class="form-group">
<label asp-for="Restaurant.Name"></label>
<input asp-for="Restaurant.Name" class="form-control" />
<span class="text-danger" asp-validation-for="Restaurant.Name"></span>
</div>
<div class="form-group">
<input asp-for="Restaurant.MealsServed.Lunch" />
<label asp-for="Restaurant.MealsServed.Lunch"> </label>
</div>
<button type="submit" class="btn btn-primary">Save</button>
</form>
So, I figured out the problem. Everything was correct that I presented above.
After checking the checkbox for Lunch and saving the item, and then viewing the Restaurant item again, it would come back unchecked.
The issue was with the depth of the data that I was pulling from the database. It was only pulling the top level of data.
So, I had to change GetById method from:
public Restaurant GetById(int id)
{
return db.Restaurants.Find(id);
}
to:
public Restaurant GetById(int id)
{
return db.Restaurants.Where(r => r.Id == id).Include(r => r.MealsServed).FirstOrDefault();
}
explicitly telling it to pull the data for the object in the MealsServed property.

Why does HTML submit form re-instantiate PageModel in ASP.NET Core?

I have a Razor page in ASP.NET core meant for editing product data. It receives a productId and has input controls for editing the properties of said product. It has a binding to the product instance saved in the PageModel. I populate the product instance OnGet, which works fine, as it populates the fields properly. When I submit the form, and OnPost is called, I see that the product instance is now null.
After debugging I realised that when I submit the form, the PageModel is re-instantiated (the constructor is called again), so the product instance is reset and is now null.
Is this expected behaviour?
Should this binding between the property in my PageModel and it's Razor Page presentation not persist?
In The course I am following, this behaves differently, the product instance persists to the OnPost method call.
My Page Model:
public class EditModel : PageModel
{
private readonly IProductData productData;
private readonly IHtmlHelper htmlHelper;
[BindProperty]
public Product Product { get; set; }
public IEnumerable<SelectListItem> Category { get; set; }
public EditModel(IProductData productData, IHtmlHelper htmlHelper)
{
this.productData = productData;
this.htmlHelper = htmlHelper;
}
public IActionResult OnGet(int productId)
{
Category = htmlHelper.GetEnumSelectList<ProductType>();
Product = productData.GetById(productId);
if(Product == null)
{
return RedirectToPage("./NotFound");
}
return Page();
}
public IActionResult OnPost()
{
Product = productData.Update(Product);
productData.Commit();
return Page();
}
}
My Razor Page:
#page "{productId:int}"
#model LearningASPdotNETCore.Pages.Products.EditModel
#{
ViewData["Title"] = "Edit";
}
<h1>Editing #Model.Product.Name</h1>
<form method="post">
<input type="hidden" asp-for="Product.Id" />
<div class="form-group">
<label asp-for="Product.Name"></label>
<input asp-for="Product.Name" class="form-control" />
</div>
<div class="form-group">
<label asp-for="Product.Country"></label>
<input asp-for="Product.Country" class="form-control" />
</div>
<div class="form-group">
<label asp-for="Product.Type"></label>
<select class="form-control" asp-for="Product.Type" asp-items="Model.Category">
</select>
</div>
<button type="submit" class="btn btn-primary">Save</button>
Yes, this is expected behavior. Virtually everything in the request pipeline, including PageModel, controllers, etc. are request-scoped. They are instantiated at the beginning of the request and disposed when the request is finished. If you need something set, then that should be set for each request, regardless of method (GET, POST, etc.).
I have found the solution. It seems like the product instance could not be instantiated because I have not implemented the property get/set. This confuses me as I thought these two were equal, but I guess not. Before (did't work):
public class Product
{
public int Id;
public string Name;
public string Country;
public ProductType Type;
}
After (works):
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public string Country { get; set; }
public ProductType Type { get; set; }
}

How to get selecteditem of a ASP.Net core select element

I have a List<Incident> which I get by calling an API and use it to populate a select element in a ASP.Net core MVC app. I would like to get the selected Incident and display the value of its Option property in a label.
In the View I have the following:
#model List<Incident>
#{
var incidents = (List<Incident>)ViewData["incidents"];
ViewData["Title"] = "Dashboard";
}
<select class="browser-default custom-select mb-2">
<option selected>Open this select menu</option>
#foreach (var incident in #incidents)
{
<option value="#incident.id">#incident.Name</option>
}
</select>
<div class="custom-control custom-checkbox mb-2">
<input type="checkbox" class="custom-control-input" id="defaultUnchecked">
<label class="custom-control-label" for="defaultUnchecked">#SelectedIncident.Option</label> CAN THIS BE DONE?
</div>
The controller's code is like this:
public async Task<IActionResult> Index()
{
var incidents = await GetIncidentsAsync();
ViewData[nameof(incidents)] = incidents;
return View(incidents);
}
The incident class is:
public class Incident
{
public int Id { get; set; }
public string Name { get; set; }
public string Option { get; set; }
public string Description { get; set; }
public string OtherDescription { get; set; }
}
Can this be done without a post-back to the server? Something like Syncfusion's DropDownList.
A simple method is to set the Option property value of each option to id attribute, and then use jquery to get the id of the selected item and assign it to lable
<select class="browser-default custom-select mb-2" id="ddlIncidents">
<option selected>Open this select menu</option>
#foreach (var incident in #incidents)
{
<option id="#incident.Option" value="#incident.Id">#incident.Name</option>
}
</select>
<div class="custom-control custom-checkbox mb-2">
<input type="checkbox" class="custom-control-input" id="defaultUnchecked">
<label class="custom-control-label" for="defaultUnchecked" id="selectedItem"></label>
</div>
#section Scripts
{
<script>
$("#ddlIncidents").change(function () {
var value = $(this).children(":selected").attr("id");
console.log(value);
$("#selectedItem").text(value);
});
</script>
}
Result:

Passing button value from view to controller aspnet 5

I'm building a aspnet mvc6 application where I want to pass values from View to controller. My view has multiple buttons having IDs and I want to pass the ID of the button which is clicked to my controller using my viewmodel.
Controller:
public class HomeController : Controller
{
private SampleDbContext _context;
private HomeViewModel _viewmodel;
public HomeController(SampleDbContext context, HomeViewModel model)
{
_viewmodel = model;
_context = context;
}
public IActionResult Index()
{
_viewmodel.model = _context.MyModel.ToList();
return View(_viewmodel);
}
public IActionResult Test()
{
var x = _viewmodel.buttonID;
return View();
}
}
ViewModel:
public class HomeViewModel
{
public IEnumerable<MyModel> model { get; set; }
public int buttonID { get; set; }
}
View:
#model MyProject.ViewModels.HomeViewModel
<table>
#foreach (var item in Model.model)
{
<tr>
<td>
<form asp-controller="Home" asp-action="Test">
<button value="Add"/>
</form>
</td>
</tr>
}
</table>
What should be done in the view such that I can set the value of 'buttonID' of the viewmodel so that it is available in the controller?
You can define your form as:
<form asp-controller="Home" asp-action="Test">
<button value="Add" name="buttonName"/>
</form>
view model:
public class HomeViewModel
{
public IEnumerable<MyModel> model { get; set; }
public string buttonName { get; set; }
}
and controller:
public ActionResult Test(HomeViewModel model) {
if(model.buttonName == "Add")
....
}
You can just add an input field that will contain the data you want to send.
add this:
<form asp-controller="Home" asp-action="Test">
<input asp-for="buttonID" value="yourButtonID" type="hidden"/>
<button value="Add"/>
</form>

Submitting an actionLink to a form mvc4

We have a list of action links
Partial View
#foreach (var item in Model.Regions) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.RegionName)
</td>
<td>
<input type="submit" value="Select" />
</td>
#Html.HiddenFor(modelItem => Model.Id)
</tr>
}
</table>
I assume that this isn't the correct way to do this, but if you could point me in the right direction it would be appreciated. I want to submit this data into an existing form
Region View
#using (Html.BeginForm()){
<fieldset>
#Html.Partial("_RegionsPartial");
<legend>Create new region</legend>
<ol>
<li>#Html.LabelFor(m => m.RegionName)</li>
<li>#Html.EditorFor(m => m.RegionName)</li>
</ol>
<input type="submit" value="Next" />
#Html.HiddenFor(model => model.RegionId)
</fieldset>
}
So you can either submit a new one or submit an existing one. Im not sure how to get the id of an existing one into my model. Here is the controller:
public ActionResult Region()
{
var model = new WizardModel();
var getRegions = _facade.FetchRegion();
model.Regions = getRegions;
return View(model);
}
[HttpPost]
public ActionResult Region(WizardModel model)
{
if (model.RegionName != null)
{
var newRegion = _facade.CreateRegion(model.RegionName);
model.RegionId = newRegion.Id;
}
else
{
model.RegionName = _facade.FetchRegion(model.RegionId).RegionName;
}
TempData["suburbModel"] = model;
return RedirectToAction("Suburb");
}
Thanks for taking the time
So heres my example of passing an instance of a model. I've got a view with many courses so I need to click a button and fire an action, thus carrying all data (including relevant ID) of the course clicked. So in the end I carry the instance I need with the hidden fields.:)
My course model...
public class CourseModel
{
public int RecordId { get; set; }
public string StudentNameField { get; set; }
public string SubjectField { get; set; }
public string CatalogField { get; set; }
public string SectionField { get; set; }
public string InstrNameField { get; set; }
public string MtgStartField { get; set; }
public string MtgEndField { get; set; }
}
My main View...Called "CourseList" in Views folder
<div id="container">
<div class="selectLabel">Select a Course:</div><br />
#foreach (var item in Model)
{
#Html.DisplayFor(model=>item)
}
</div>
My Display template - Its a view called "CourseModel" in Shared\DisplayTemplates ...For your display template, you could make a unique model for existing & new. Using your "existing" model in the displaytemplate, it results in multiple forms, each using a button type=submit to submit the form with model instance. Use CSS to model the button like a link. If you still need to use actionlink, carry the iD as one of the params.
#using LecExamRes.Helpers
#model LecExamRes.Models.SelectionModel.CourseModel
#using (Html.BeginForm("CourseList", "Home", null, FormMethod.Post))
{
<div class="mlink">
#Html.AntiForgeryToken()
#Html.EncryptedHiddenFor(model => model.RecordId)
#Html.EncryptedHiddenFor(model => model.CatalogField)
#Html.EncryptedHiddenFor(model => model.SectionField)
#Html.EncryptedHiddenFor(model => model.SubjectField)
#Html.EncryptedHiddenFor(model => model.InstrNameField)
#Html.EncryptedHiddenFor(model => model.MtgStartField)
#Html.EncryptedHiddenFor(model => model.MtgEndField)
<p>
<input type="submit" name="gbtn" class="groovybutton" value="#Model.SubjectField - #Model.CatalogField - #Model.SectionField : #Model.InstrNameField">
</p>
</div>
}
My controller, Courselist [POST] Action...
[ValidateAntiForgeryToken]
[HttpPost]
public ActionResult CourseList(SelectionModel.CourseModel model)
{
//....do something with my model
}

Categories