I'm looping through a list to populate my questionnaire with selects. I haven't found any tutorial explaining how to extract values from multiple selects in one click. Any ideas?
Here's the view:
<div class="text-center">
<form method="post" asp-page-handler="Answer">
#foreach (var question in Model.QuestionList)
{
<p>#question.Query</p>
<select asp-for="Answer">
<option value="">Select a number</option>
<option value="#question.Option1">#question.Option1</option>
<option value="#question.Option2">#question.Option2</option>
<option value="#question.Option3">#question.Option3</option>
</select>
}
<br>
<br>
<button type="submit">Send</button>
</form>
</div>
Here's the .cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using myquiz.Models;
using myquiz.Services;
namespace myquiz.Pages
{
public class QuizModel : PageModel
{
[ViewData]
[BindProperty]
public string Name { get; set; }
[BindProperty]
public Visitor Visitor { get; set; }
[BindProperty]
public List<Question> QuestionList { get; set; }
public string Answer { get; set; }
public void OnGet()
{
///QuestionList = new List<Question>();
// if (QuestionList is null)
// QuestionList = new List<Question>();
}
public void OnPost()
{
Name = Visitor.Name;
var quizService = new QuizService();
QuestionList = quizService.GetQuestions();
}
public void OnPostAnswer() {
Console.WriteLine("Yay, Answer works");
}
}
}
Here's the model
namespace myquiz.Models
{
public class Question
{
public int Id { get; set; }
public string Query { get; set; }
public string Option1 { get; set; }
public string Option2 { get; set; }
public string Option3 { get; set; }
public string Answer { get; set; }
}
}
Here are two ways to get the select answers:
The first way
All the select lists have the same name Answer,so you could receive an array named Answer in the backend and use BindProperty to bind the values:
public class IndexModel : PageModel
{
[ViewData]
[BindProperty]
public string Name { get; set; }
[BindProperty]
public List<Question1> QuestionList { get; set; }
[BindProperty]
public string[] Answer { get; set; } //modify this...
public void OnPostAnswer()
{
Console.WriteLine("Yay, Answer works");
}
}
View(be the same as yours):
<div class="text-center">
<form method="post" asp-page-handler="Answer">
#foreach (var question in Model.QuestionList)
{
<p>#question.Query</p>
<select asp-for="Answer">
<option value="">Select a number</option>
<option value="#question.Option1">#question.Option1</option>
<option value="#question.Option2">#question.Option2</option>
<option value="#question.Option3">#question.Option3</option>
</select>
}
<br>
<br>
<button type="submit">Send</button>
</form>
</div>
Result:
The second way:
If you want to get the question id with selected answer in QuestionList,you need learn how does model binding system work firstly:
For each property of the complex type, model binding looks through the sources for the name pattern prefix.property_name. If nothing is found, it looks for just property_name without the prefix.For QuestionList is a List,you need give the name like:QuestionList[index].Answer.
<div class="text-center">
<form method="post" asp-page-handler="Answer">
#{
int i = 0;
} //add this...
#foreach (var question in Model.QuestionList)
{
<input hidden asp-for="QuestionList[i].Id" /> //add this..
<p>#question.Query</p>
<select asp-for="QuestionList[i].Answer"> //change asp-for
<option value="">Select a number</option>
<option value="#question.Option1">#question.Option1</option>
<option value="#question.Option2">#question.Option2</option>
<option value="#question.Option3">#question.Option3</option>
</select>
i++; //add this...
}
<br>
<br>
<button type="submit">Send</button>
</form>
</div>
Result:
Update:
Change <select asp-for="Answer"> to:
<select name="Answer">
//..
</select>
Result:
Related
What is my mistake? AllFindedBrands property is not passing to SearchBrandResult Action of Controller
Controller:
public async Task<IActionResult> Search(string Articul, int idClient)
{
List<BrandList> findedBrands = new List<BrandList>();
#ViewBag.list = woDupes;
SearchViewModel model = new SearchViewModel();
model.Articul = Articul;
model.idClient = idClient;
model.AllFindedBrands = new List<BrandList>(findedBrands);
return View(model);
}
[HttpPost]
public async Task<IActionResult> SearchBrandResult(SearchViewModel model)
{
return View();
}
View:
<form asp-controller="Search" asp-action="SearchBrandResult" asp-route-
Articul="#Model.Articul"
asp-route-AllFindedBrands="#Model.AllFindedBrands" asp-route-
idClient="#Model.idClient" method="post" enctype="multipart/form-data">
<select asp-for="SelectedBrand" asp-items="#(new SelectList(#ViewBag.list,
nameof(FindedBrand.Name),
nameof(FindedBrand.Name)))"
multiple="true" class="form-control brand-chosen">
</select>
<input type="submit" />
All other properties of ViewModel is successfully passed to th Action
AllFindedBrands is type of complex model and asp-route-* cannot dynamic bind the value. You can F12 in the browser to check the generated url in the form.
Two ways you can follow:
1.By using asp-all-route-data and foreach the AllFindedBrands to bind the value which passes the value by route data.
Assume your model like below:
public class SearchViewModel
{
public string Articul { get; set; }
public string idClient { get; set; }
public List<BrandList> AllFindedBrands { get; set; }
public List<string> SelectedBrand { get; set; }
}
public class BrandList
{
public string Name { get; set; }
}
View(For easy testing, I just hard-coded the dropdownlist):
#model SearchViewModel
#{
var data = new Dictionary<string, string>();
for(int i=0;i<Model.AllFindedBrands.Count();i++)
{
data.Add("AllFindedBrands[" + i + "].Name", Model.AllFindedBrands[i].Name);
}
}
<form asp-action="SearchBrandResult" asp-route-Articul="#Model.Articul" asp-all-route-data="#data" asp-route-idClient="#Model.idClient" method="post" enctype="multipart/form-data">
<select asp-for="SelectedBrand" multiple="true" class="form-control brand-chosen">
<option value="aaa">aaa</option>
<option value="bbb">bbb</option>
<option value="ccc">ccc</option>
</select>
<input type="submit" />
</form>
2.By listing the property and make them hidden inputs which passes the value by form data:
#model SearchViewModel
<form asp-action="SearchBrandResult" asp-route-Articul="#Model.Articul" asp-route-idClient="#Model.idClient" method="post" enctype="multipart/form-data">
#for (int i = 0; i < Model.AllFindedBrands.Count(); i++)
{
<input asp-for="#Model.AllFindedBrands[i].Name" hidden />
}
<select asp-for="SelectedBrand" multiple="true" class="form-control brand-chosen">
<option value="aaa">aaa</option>
<option value="bbb">bbb</option>
<option value="ccc">ccc</option>
</select>
<input type="submit" />
</form>
I've been working on my version of the app made in this tutorial (https://learn.microsoft.com/pl-pl/aspnet/core/data/ef-rp/complex-data-model?view=aspnetcore-5.0&tabs=visual-studio). And I've got question about a connection between two objects. My idea is to add more than one Material to Paczka. I've manage to connect them together but I can add only one Material. So my question is what should I do to be able to connect more than one?
Object Material
public class Material
{
[Key]
public int PaczkaID { get; set; }
public string Name { get; set; }
public string PN { get; set; }
public string Cert { get; set; }
public string Qty { get; set; }
public Paczka Paczka { get; set; }
}
And object Paczka
public class Paczka
{
public int PaczkaID { get; set; }
public string CRS { get; set; }
public string WO { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public Material Material { get; set; }
}
Here is how I can add Material to Paczka
public class MaterialModel : PageModel
{
private readonly Pasto.Data.PastoContext _context;
public MaterialModel(Pasto.Data.PastoContext context)
{
_context = context;
}
[BindProperty]
public Paczka Paczka { get; set; }
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null)
{
return NotFound();
}
Paczka = await _context.Paczkas
.Include(i => i.Material)
.AsNoTracking()
.FirstOrDefaultAsync(m => m.PaczkaID == id);
if (Paczka == null)
{
return NotFound();
}
return Page();
}
public async Task<IActionResult> OnPostAsync(int? id)
{
if (id == null)
{
return NotFound();
}
var paczkaToUpdate = await _context.Paczkas
.Include(i => i.Material)
.FirstOrDefaultAsync(s => s.PaczkaID == id);
if (paczkaToUpdate == null)
{
return NotFound();
}
if (await TryUpdateModelAsync<Paczka>(
paczkaToUpdate,
"Paczka",
i => i.Material))
{
if (String.IsNullOrWhiteSpace(
paczkaToUpdate.Material?.Name))
{
paczkaToUpdate.Material = null;
}
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
return Page();
}
}
And HTML
a<form method="post">
<table class="table">
<thead>
<tr>
<th>
<strong>Name</strong>
</th>
<th>
<strong>P/N</strong>
</th>
<th>
<strong>Certificate</strong>
</th>
<th>
<strong>Quantity</strong>
</th>
</tr>
</thead>
<div class="form-group">
<tbody>
<tr>
<td>
<input asp-for="Paczka.Material.Name" class="form-control" />
</td>
<td>
<input asp-for="Paczka.Material.PN" class="form-control" />
</td>
<td>
<input asp-for="Paczka.Material.Cert" class="form-control" />
</td>
<td>
<input asp-for="Paczka.Material.Qty" class="form-control" />
</td>
</tr>
</tbody>
</div>
</table>
<div class="form-group">
<input type="submit" value="Save" class="btn btn-primary" />
<a asp-page="./Index" class="btn btn-danger">Back to List</a>
</div>
I want Paczka to have many Materials. The material should belong only
to one Paczka because I want to create them while creating Paczka
From your description, the Paczka and Materials should be configured one-to-many relationship. In the Paczka class, use List or ICollection to define the navigation property (Material), code like this:
public class Paczka
{
public int PaczkaID { get; set; }
public string CRS { get; set; }
public string WO { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public List<Material> Material { get; set; }
}
public class Material
{
[Key]
public int PaczkaID { get; set; }
public string Name { get; set; }
public string PN { get; set; }
public string Cert { get; set; }
public string Qty { get; set; }
//the foreign key
public int PaczkaForeignKey { get; set; }
[ForeignKey("PaczkaForeignKey")]
public Paczka Paczka { get; set; }
}
Then, in the View page, you could use #for statement to loop through the Material and display the related data, code like this:
#page
#model RazorPageSample.Pages.MaterialModel
#{
}
<div class="row">
<div class="col-md-4">
<form method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="Paczka.PaczkaID" />
<div class="form-group">
<label asp-for="Paczka.CRS" class="control-label"></label>
<input asp-for="Paczka.CRS" class="form-control" />
<span asp-validation-for="Paczka.CRS" class="text-danger"></span>
</div>
#for (var i = 0; i < Model.Paczka.Material.Count; i++)
{
<div class="form-group">
<label asp-for="Paczka.Material[i].PaczkaID" class="control-label"></label>
<input asp-for="Paczka.Material[i].PaczkaID" class="form-control" />
<span asp-validation-for="Paczka.Material[i].PaczkaID" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Paczka.Material[i].Name" class="control-label"></label>
<input asp-for="Paczka.Material[i].Name" class="form-control" />
<span asp-validation-for="Paczka.Material[i].Name" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Paczka.Material[i].Cert" class="control-label"></label>
<input asp-for="Paczka.Material[i].Cert" class="form-control" />
<span asp-validation-for="Paczka.Material[i].Cert" class="text-danger"></span>
</div>
}
<div class="form-group">
<input type="submit" value="Save" class="btn btn-primary" />
</div>
</form>
</div>
</div>
Then, in the Post method, get the update value and insert or update the data to database, screenshot like this:
More detail information about relationships and update related entity, refer the following articles:
Relationships
Configuring One To Many Relationships in Entity Framework Core
Update related data - ASP.NET MVC with EF Core
I created a search bar like this
<form method="post" asp-action="Search">
<input type="text" name="search" placeholder="Enter here the Name " />
<select name="type" id="type" class="form-control">
<option value="Success">Inactive Reservation</option>
<option value="Approved">Active Reservation</option>
<option value="Pending">Pending Reservation</option>
</select>
<input type="submit" value="Search" />
</form>
and the method in controller:
public async Task<IActionResult> Search(string search,string type)
{
var allRsv = from m in _db.Reservation
select m;
var Rsv = allRsv
.Where(x => x.ClientName.ToLower().Contains(search.ToLower()) &&
x.Status.ToLower().Contains(type.ToLower()));
return View(Rsv);
}
What I want: to send in search page something like 'You searched for #search and type: #type.
return View has no option to do this ,neither return to action ..
Can I do it in a simple way ?
My single idea it is to send query string and then request query in search view
What I want: to send in search page something like 'You searched for #search and type: #type.
You can try to pass data to search page via ViewData etc, like below.
In View Page
<form method="post" asp-action="Search">
<input type="text" name="search" placeholder="Enter here the Name " />
<select name="type" id="type" class="form-control">
<option value="Success">Inactive Reservation</option>
<option value="Approved">Active Reservation</option>
<option value="Pending">Pending Reservation</option>
</select>
<input type="submit" value="Search" />
</form>
<h3>You searched for "#ViewData["search"]" and type: #ViewData["type"].</h3>
In Action Method
public async Task<IActionResult> Search(string search, string type)
{
var allRsv = from m in _db.Reservation
select m;
var Rsv = allRsv
.Where(x => x.ClientName.ToLower().Contains(search.ToLower()) &&
x.Status.ToLower().Contains(type.ToLower()));
ViewData["search"] = search;
ViewData["type"] = type;
return View(Rsv);
}
Test Result
Using ViewData to pass data between controllers and views is fine as long as there are not many pieces of data in between. If you have lots of going, it will make everybody else harder to understand what's going on with ViewData because it's a weak-typed and you have no idea what it contains, what's available to get, etc. And then you have to go back to the controller and see what's being passed. What if there are multiple controllers returning this same view...yucky!
In addition, it's not a good practice to display what you have from your database directly from the controller to the view.
Hence you should use one of the alternatives of ViewData, that is ViewModel, which is strongly-typed!
Fake Entity Models
Since I don't have your database, for this demo, I am setting up two fake entity models that represent the data from your persistence storage.
namespace DL.NetCore.EmptySolution.Web.UI.Models.Reservation
{
public class FakeReservationStatusEntity
{
public string StatusId { get; set; }
public string Status { get; set; }
}
public class FakeReservationEntity
{
public int ReservationId { get; set; }
public int ClientId { get; set; }
public string ClientName { get; set; }
public DateTime StartTimeUtc { get; set; }
public FakeReservationStatusEntity ReservationStatus { get; set; }
public int CreatedByUserId { get; set; }
}
}
There is one-to-many relationship between reservation and reservation status I assumed. And please notice I purposely made it so that it has more properties than the view model!
Viewmodels
They're just POCOs (Plain Old CLR Objects) that serve as data containers to travel between controllers and views.
namespace DL.NetCore.EmptySolution.Web.UI.Models.Reservation
{
public class ReservationFiltersViewModel
{
[Display(Name = "Client name")]
public string ClientNameSearchQuery { get; set; }
[Display(Name = "Reservation type")]
public string ReservationTypeSearchQuery { get; set; }
public IDictionary<string, string> AvailableReservationTypes { get; set; }
}
public class ReservationViewModel
{
public int ReservationId { get; set; }
public string ReservationType { get; set; }
public string ClientName { get; set; }
public DateTime StartTime { get; set; }
}
public class ReservationListViewModel
{
public ReservationFiltersViewModel Filters { get; set; }
public IEnumerable<ReservationViewModel> Reservations { get; set; }
}
}
Controller
namespace DL.NetCore.EmptySolution.Web.UI.Controllers
{
public class ReservationController : Controller
{
public IActionResult Index(string c, string t)
{
var vm = new ReservationListViewModel
{
Filters = new ReservationFiltersViewModel
{
ClientNameSearchQuery = c,
ReservationTypeSearchQuery = t,
// You would normally get the list from your database
AvailableReservationTypes = GetFakeReservationStatusesFromDb()
.ToDictionary(x => x.StatusId, x => x.Status)
},
Reservations = Enumerable.Empty<ReservationViewModel>()
};
// You would normally get the list of reservations from your database
var reservationsFromDb = GetFakeReservationsFromDb();
// Filters
if (!String.IsNullOrWhiteSpace(c))
{
reservationsFromDb = reservationsFromDb
.Where(x => x.ClientName.Contains(c, StringComparison.InvariantCultureIgnoreCase));
}
if (!String.IsNullOrWhiteSpace(t))
{
reservationsFromDb = reservationsFromDb
.Where(x => x.ReservationStatus.StatusId.Contains(t, StringComparison.InvariantCultureIgnoreCase));
}
// See you only want to explore what you want on the view
vm.Reservations = reservationsFromDb
.Select(x => new ReservationViewModel
{
ReservationId = x.ReservationId,
ClientName = x.ClientName,
ReservationType = x.ReservationStatus.Status,
StartTime = x.StartTimeUtc.ToLocalTime()
});
return View(vm);
}
[HttpPost]
public IActionResult Search(ReservationFiltersViewModel filters)
{
return RedirectToAction(nameof(Index),
new { c = filters.ClientNameSearchQuery, t = filters.ReservationTypeSearchQuery });
}
...
}
}
Index View
#model DL.NetCore.EmptySolution.Web.UI.Models.Reservation.ReservationListViewModel
#{
ViewBag.Title = "Reservations";
var selectList = new SelectList(Model.Filters.AvailableReservationTypes, "Key", "Value");
}
<h2>Reservations</h2>
<p class="text-muted">
List of reservations you can manage
</p>
<div class="row">
<div class="col-lg-4">
<div class="card">
<div class="card-body">
<form method="post" asp-area="" asp-controller="reservation" asp-action="search">
<div class="form-group">
<label asp-for="Filters.ClientNameSearchQuery"></label>
<input asp-for="Filters.ClientNameSearchQuery" class="form-control" />
</div>
<div class="form-group">
<label asp-for="Filters.ReservationTypeSearchQuery"></label>
<select asp-for="Filters.ReservationTypeSearchQuery" class="form-control"
asp-items="selectList">
<option value="">- select -</option>
</select>
</div>
<button type="submit" class="btn btn-success">Search</button>
</form>
</div>
</div>
</div>
<div class="col-lg-8">
<!-- This could be better optimized, i.e., only display non-empty search -->
<div class="alert alert-info">
<i class="fas fa-info-circle"></i>
You searched for <strong>#Model.Filters.ClientNameSearchQuery</strong>
and <strong>#Model.Filters.ReservationTypeSearchQuery</strong>
</div>
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th>#</th>
<th>Client name</th>
<th>Start from</th>
<th>Type</th>
</tr>
</thead>
<tbody>
#foreach (var reservation in Model.Reservations)
{
<tr>
<td>#reservation.ReservationId</td>
<td>#reservation.ClientName</td>
<td>#reservation.StartTime.ToShortDateString()</td>
<td>#reservation.ReservationType</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
With ViewModel, there is no magic string flowing around like with ViewData. Everything is strongly typed. And the form only posts back the filter model that contains only what we need to the server.
Screenshots
Source code
My demo project's source code is at https://github.com/davidliang2008/DL.NetCore.EmptySolution. The checkin specifically for this demo is at https://github.com/davidliang2008/DL.NetCore.EmptySolution/commit/32087b989de06e316cf747ad49da6ad4b24b61b8
I have to implement more than 30 Checkboxes for a Model and I am not sure about the correct way to implement it. Currently I am doing it this way.
I have Model class with more that 30 checkboxes I am not including all of them here Venue.cs
using System;
using System.Collections.Generic;
namespace firstp.Models
{
public class Venue
{
public int Id { get; set; }
public string Name { get; set; }
public string Description {get;set;}
public string Category { get; set; }
public string Address { get; set; }
// Facilities
public bool AirCondition {get;set;}
public bool CableTV {get;set;}
public bool Computer {get;set;}
public bool DVD {get;set;}
public bool UseOfPool {get;set;}
public bool Parking {get;set;}
public bool SmokingAllowed {get;set;}
public bool Internet {get;set;}
public bool Parking {get;set;
public bool Heater {get;set;
public bool Lift {get;set;}
public bool CoffeePot {get;set;}
public bool DishWasher {get;set;}
//Activities
public bool Concerts {get;set;}
public bool LiveShow {get;set;}
public bool Party {get;set;}
public bool Swimming {get;set;}
public bool Wedding {get;set;}
public bool Birthday {get;set;}
}
}
I am binding all the Properties of Checkbox one by one in Create.cshtml
#model Venue
<form asp-action="Create" asp-controller="Owner" method="POST">
<input type="hidden" asp-for="Id"/>
<input type="text" asp-for="Name"/>
<input type="text" asp-for="Description"/>
<input type="text" asp-for="Category"/>
<input type="text" asp-for="Address"/>
<ul class="facilities">
<li class="checkbox"><input type="checkbox" asp-for="AirCondition">Air conditioning </li>
<li class="checkbox"><input type="checkbox" asp-for="CableTV"> Cable </li>
<li class="checkbox"><input type="checkbox" asp-for="Computer" >Computering </li>
<li class="checkbox"><input type="checkbox" asp-for="DVD"> DVD </li>
<li class="checkbox"><input type="checkbox" asp-for="UseOfPool" > Use Of Pool </li>
<li class="checkbox"><input type="checkbox" asp-for="Parking"> Parking </li>
<li class="checkbox"><input type="checkbox" asp-for="SmokingAllowed">SmokingAllowed </li>
<li class="checkbox"><input type="checkbox" asp-for="Internet"> Internet </li>
<li class="checkbox"><input type="checkbox" asp-for="Lift">Lift </li>
<li class="checkbox"><input type="checkbox" asp-for="CoffeePot"> CoffeePot </li>
<li class="checkbox"><input type="checkbox" asp-for="DishWasher"> DishWasher </li>
<li class="checkbox"><input type="checkbox" asp-for="Parking"> Parking</li>
<li class="checkbox"><input type="checkbox" asp-for="Heater"> Heater</li>
</ul>
<ul class="activities">
<li class="checkbox"><input type="checkbox" asp-for="Concerts"> Concerts > </li>
<li class="checkbox"><input type="checkbox" asp-for="LoveShow"> LiveShow </li>
<li class="checkbox"><input type="checkbox" asp-for="Swimming" > Swimming </li>
<li class="checkbox"><input type="checkbox" asp-for="Party"> Party </li>
<li class="checkbox"><input type="checkbox" asp-for="Wedding" > Wedding </li>
<li class="checkbox"><input type="checkbox" asp-for="Birthday"> Birthday </li>
</ul>
<input type="submit" value="Save">
</form>
VenueController.cs
public async Task<IActionResult> Create(Venue v){
_context.Venues.Add(v);
_context.SaveChanges();
return RedirectToAction(nameof(Index));
}
Any thoughts on this. Is this the corect way to implement Multiple Checkbox. If not then how exactly can it be done.
Wrong way.
1) Add new table for Facility. Columns = ID, Name
2) Add new table for Activity. Columns = ID, Name
3) Add new table for Venue's Facility records. Columns = ID, VenueID, FacilityID
4) Add new table for Venue's Activity records. Columns = ID, VenueID, ActivityID
5) Add new 2 partial views in venue's view for facility and activity to load checkboxes. Loop models to bind checkbox.
6) Get data from facility and activity partial views and save data into Venue's Facility and Activity tables.
EDITED for clear explanation with screenshots.
1) Create new table for Facility
2) Create new table for Activity
3) Create new table for VenueFacility (facility records for venue). You need to create relationship between this table, venue table and facility table in SQL Server.
4) Create new table for VenueActivity (activity records for venue). You need to create relationship between this table, venue table and activity table in SQL Server.
5) Re-run Scaffold-DbContext command in tools > nuget > package manager console to update your models and dbcontext.
6) Add new partial view for Facility.
_FacilityList.cshtml
#model List<TestBenchmark.Models.VenueFacility>
#{
var db = new TestContext();
var list = db.Facility.AsQueryable();
int i = 0;
foreach (var item in list)
{
<input type="hidden" asp-for="#Model[i].FacilityId" value="#item.Id" />
<input type="checkbox" asp-for="#Model[i].IsChecked" /> #item.Name
i++;
}
}
7) Add new partial view for Activity.
_ActivityList.cshtml
#model List<TestBenchmark.Models.VenueActivity>
#{
var db = new TestContext();
var list = db.Activity.AsQueryable();
int i = 0;
foreach (var item in list)
{
<input type="hidden" asp-for="#Model[i].ActivityId" value="#item.Id" />
<input type="checkbox" asp-for="#Model[i].IsChecked" /> #item.Name
i++;
}
}
8) Use this code in your venue page
<form method="post" asp-action="Index">
Venue Name: <input type="text" asp-for="Name" />
<br />
Facility<br />
<partial name="_FacilityList" for="#Model.VenueFacility" />
<br /><br />
Activity<br />
<partial name="_ActivityList" for="#Model.VenueActivity" />
<br />
<button>Save</button>
</form>
How page looks like
Result
EDITED 2 for entity classes
Venue.cs
public partial class Venue
{
public Venue()
{
VenueActivity = new HashSet<VenueActivity>();
VenueFacility = new HashSet<VenueFacility>();
}
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<VenueActivity> VenueActivity { get; set; }
public virtual ICollection<VenueFacility> VenueFacility { get; set; }
}
Facility.cs
public partial class Facility
{
public Facility()
{
VenueFacility = new HashSet<VenueFacility>();
}
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<VenueFacility> VenueFacility { get; set; }
}
Activity.cs
public partial class Activity
{
public Activity()
{
VenueActivity = new HashSet<VenueActivity>();
}
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<VenueActivity> VenueActivity { get; set; }
}
VenueFacility.cs
public partial class VenueFacility
{
public int Id { get; set; }
public int? VenueId { get; set; }
public int? FacilityId { get; set; }
public bool IsChecked { get; set; }
public virtual Facility Facility { get; set; }
public virtual Venue Venue { get; set; }
}
VenueActivity.cs
public partial class VenueActivity
{
public int Id { get; set; }
public int? VenueId { get; set; }
public int? ActivityId { get; set; }
public bool IsChecked { get; set; }
public virtual Activity Activity { get; set; }
public virtual Venue Venue { get; set; }
}
UPDATED ON 5 FEB 2020
Venue.cs. Change HashSet to List. Do same for VenueFacility.
public partial class Venue
{
public Venue()
{
VenueActivity = new List<VenueActivity>();
VenueFacility = new List<VenueFacility>();
}
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<VenueActivity> VenueActivity { get; set; }
public virtual ICollection<VenueFacility> VenueFacility { get; set; }
}
YourVenueController.cs
public IActionResult Index()
{
var db = new TestContext();
var list = db.Venue.Include(x => x.VenueActivity).Include(x => x.VenueFacility).FirstOrDefault();
return View(list);
}
I have following html form
<input type="hidden" name="JsonCustomers" data-bind="value: ko.toJSON(customers)" />
<input type="hidden" name="JsonMaterials" data-bind="value: ko.toJSON(materials)" />
<button type="submit" class="btn btn-sm btn-primary">Submit</button>
and input model class
public class SubmitViewModel
{
public string JsonCustomers { get; set; }
public string JsonMaterials { get; set; }
}
Controller action
[HttpPost]
public IActionResult Submit(SubmitViewModel model)
{
throw new NotImplementedException();
}
it is possible to automap Json into something like this ?
public class SubmitViewModel
{
public IEnumerable<InputCustomer> Customers { get; set; }
public IEnumerable<InputMaterial> Materials { get; set; }
}
I would like to skip conversion step from the Json into collection and ideally use data annotations with ModelState.IsValid function. Any idea ?
UPDATE
html
<input type="hidden" name="JsonCustomers" data-bind="value: ko.toJSON(customers)" />
<input type="hidden" name="JsonMaterials" data-bind="value: ko.toJSON(materials)" />
<input type="hidden" name="Customers" data-bind="value: ko.toJSON(customers)" />
<input type="hidden" name="Materials" data-bind="value: ko.toJSON(materials)" />
content of JsonCustomers after form submit
[{"isChecked":true,"name":"CompanyA","volume":"80","expectedDateOfOrder":"1.1.2018"},{"isChecked":true,"name":"CompanyB","volume":"100","expectedDateOfOrder":"2.2.2018"},{"isChecked":true,"name":"CompanyC","volume":"150","expectedDateOfOrder":"3.3.2018"}]
customer class
public class Customer
{
public bool? IsChecked { get; set; }
public string Name { get; set; }
public string Volume { get; set; }
public string ExpectedDateOfOrder { get; set; }
}
the issue is that public IEnumerable<Customer> Customers collection has Count = 0, i dont know why.
this is from FormCollection
With help from #Alex Riabov and based on this discussion https://github.com/aspnet/Mvc/issues/5760
model.Customers = JsonConvert.DeserializeObject<IEnumerable<InputCustomer>>(model.JsonCustomers);
in the controller action did the trick.