Blazor data annotations error messages in a tooltip - c#

I have a blazor webassembly application. I added data annotations to my code and data validations are now displayed on top of page.
Now I have a requirement to display the validations error messages in submit tooltip.
Can anyone please help me how to show validations msgs in submit button tooltip.
Below is my code.
<EditForm Model="_product" OnValidSubmit="Submit" style="width:600px;">
<DataAnnotationsValidator />
<ValidationSummary />
<div class="form-group row">
<label for="name" class="col-md-2 col-form-label">Name:</label>
<div class="col-md-10">
<InputText id="name" class="form-control" #bind-Value="_product.Name" />
</div>
</div>
<div class="form-group row">
<label for="supplier" class="col-md-2 col-form-label">Supplier:</label>
<div class="col-md-10">
<InputText id="supplier" class="form-control" #bind-Value="_product.Supplier"/>
</div>
</div>
<div class="row">
<div class="col-md-12 text-right">
<button type="submit" class="btn btn-success" data-bs-toggle="tooltip" data-bs-placement="left" title="errorMessages">
Submit
</button>
</div>
</div>
</EditForm>
#code {
private string errorMessages = string.Empty;
private Product _product = new() {};
protected override async Task OnInitializedAsync()
{
await InvokeAsync(StateHasChanged);
}
private void Submit(){
Console.WriteLine("Submitted");
}
}
Below is code for Model
public class Product
{
[Required]
public string Name { get; set; }
[Required]
[SupplierValidation(ErrorMessage = "Wrong value entered", ValidSupplierValue = "Code-Maze")]
public string Supplier { get; set; }
}

Related

dotnet 5 MVC 404 error when I post to action

I have a page that is supposed to submit data for a patient evaluation. The page to submit the evaluation comes up fine. It is based on a ViewModel. Here is the ViewModel:
public class SPEPatientEvalVM
{
public int Id { get; set; }
public int ApptId { get; set; }
public int ActivityID { get; set; }
public int VendorID { get; set; }
public int PatientID { get; set; }
[Required(ErrorMessage ="A selection is required.")]
public int OverallRating { get; set; }
[Required(ErrorMessage = "A selection is required.")]
public int AppearProfessComp { get; set; }
[Required(ErrorMessage = "A selection is required.")]
public int EffGatheredInfo { get; set; }
[Required(ErrorMessage = "A selection is required.")]
public int ListenActively { get; set; }
[Required(ErrorMessage = "A selection is required.")]
public int EstabPersRapport { get; set; }
[Required(ErrorMessage = "A selection is required.")]
public int AppropExploreMyFeelings { get; set; }
[Required(ErrorMessage = "A selection is required.")]
public int AddressedMyFeelings { get; set; }
[Required(ErrorMessage = "A selection is required.")]
public int MetMyNeeds { get; set; }
public string PatientComments { get; set; } = String.Empty;
public DateTime DateSubmitted { get; set; }
}
This is mapped and reverse mapped to the model using IMapper.
Anyway this is my controller code:
[HttpGet("[controller]/[action]/{IDAppt}/{ActivityID}/{VendorID}/{PatientID}")]
public async Task<IActionResult> AddSPEPatientEval(int IDAppt, int ActivityID, int VendorID, int PatientID)
{
var patientChoice = GetPatientChoiceList();
patientChoice[0].Selected = true;
ViewBag.PatientChoice = patientChoice;
var evalParams = await _speRepo.GetOpenPatientEval(IDAppt);
ViewBag.Patient = evalParams.SPEPatient;
var speEval = new SPEPatientEvalVM
{
ApptId = IDAppt,
ActivityID = ActivityID,
VendorID = VendorID,
PatientID = PatientID
};
return View(speEval);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> AddSPEPatientEval(SPEPatientEvalVM model)
{
var patientChoice = GetPatientChoiceList();
patientChoice[0].Selected = true;
ViewBag.PatientChoice = patientChoice;
model.DateSubmitted = DateTime.Now;
if (ModelState.IsValid)
{
var spePatientEval = _mapper.Map<SPEPatientEval>(model);
var success = await _speRepo.AddSPEPatientEval(spePatientEval);
if (!success)
{
return View(model);
}
return View("Index");
}
return View(model);
}
This is all for the form AddSPEPatientEval.cshtml
#model SPEPatientEvalVM
#{
ViewData["Title"] = "AddSPEPatientEval";
}
<div class="col-md-8 m-auto">
<h1 class="text-center">Patient SPE Evaluation</h1>
<hr />
</div>
<div class="col-md-8 m-auto">
<div class="row">
<div class="col-md-12">
<form asp-controller="SPE" asp-action="AddSPEPatientEval" method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="ApptId" />
<input type="hidden" asp-for="ActivityID" />
<input type="hidden" asp-for="VendorID" />
<input type="hidden" asp-for="PatientID" />
<input type="hidden" asp-for="DateSubmitted" value="#String.Format("{0:MM/dd/yyyy}", DateTime.Now)" />
<div class="row">
<div class="col-md-8 m-auto">
<div class="form-group">
<label asp-for="OverallRating" class="control-label">As #ViewBag.Patient, rate your overall level of satisfaction with this encounter.</label>
<select asp-for="OverallRating" class="form-control"
asp-items="#(new SelectList(ViewBag.PatientChoice, "Value", "Text"))"></select>
<span asp-validation-for="OverallRating" class="text-danger"></span>
</div>
<br />
<hr />
</div>
</div>
<br />
<div class="row">
<div class="col-md-8 m-auto">
<div class="form-group">
<label asp-for="AppearProfessComp" class="control-label">Appeared professional competent - seemed to know what s/he was
doing; inspired my comfidence; appeared to have my interests at heart.
</b></label>
<select asp-for="AppearProfessComp" class="form-control"
asp-items="#(new SelectList(ViewBag.PatientChoice, "Value", "Text"))"></select>
<span asp-validation-for="AppearProfessComp" class="text-danger"></span>
</div>
</div>
</div>
<div class="row">
<div class="col-md-8 m-auto">
<div class="form-group">
<label asp-for="EffGatheredInfo" class="control-label">Effectively gathered information - collected information in a way that
seemed organized; began with several open-ended questions and progressed through interview using a balanced ratio of open-
to closed-ended questions; summarized periodically.
</label>
<select asp-for="EffGatheredInfo" class="form-control"
asp-items="#(new SelectList(ViewBag.PatientChoice, "Value", "Text"))"></select>
<span asp-validation-for="EffGatheredInfo" class="text-danger"></span>
</div>
</div>
</div>
<div class="row">
<div class="col-md-8 m-auto">
<div class="form-group">
<label asp-for="ListenActively" class="control-label">Listened actively - paid attention to both my verbal and non-verbal
cues; used facial expressions/body language to express encouragement; avoided interruptions; asked questions to make sure
s/he understood what I said.
</label>
<select asp-for="ListenActively" class="form-control"
asp-items="#(new SelectList(ViewBag.PatientChoice, "Value", "Text"))"></select>
<span asp-validation-for="ListenActively" class="text-danger"></span>
</div>
</div>
</div>
<div class="row">
<div class="col-md-8 m-auto">
<div class="form-group">
<label class="control-label">Established personal rapport - introduced self warmly; verbally/non-verbally showed interest
in me as a person, not just my condition; avoided technical jargon.
</label>
<select asp-for="EstabPersRapport" class="form-control"
asp-items="#(new SelectList(ViewBag.PatientChoice, "Value", "Text"))"></select>
<span asp-validation-for="EstabPersRapport" class="text-danger"></span>
</div>
</div>
</div>
<div class="row">
<div class="col-md-8 m-auto">
<div class="form-group">
<label asp-for="AppropExploreMyFeelings" class="control-label">Appropriately explored my perspective - encouraged me to
identify everything that I needed to say.
</label>
<select asp-for="AppropExploreMyFeelings" class="form-control"
asp-items="#(new SelectList(ViewBag.PatientChoice, "Value", "Text"))"></select>
<span asp-validation-for="AppropExploreMyFeelings" class="text-danger"></span>
</div>
</div>
</div>
<div class="row">
<div class="col-md-8 m-auto">
<div class="form-group">
<label asp-for="AddressedMyFeelings" class="control-label">Addressed my feelings - acknowledged and demonstrated interest in my
expressed and/orunexpressed feelings and experience.
</label>
<select asp-for="AddressedMyFeelings" class="form-control"
asp-items="#(new SelectList(ViewBag.PatientChoice, "Value", "Text"))"></select>
<span asp-validation-for="AddressedMyFeelings" class="text-danger"></span>
</div>
</div>
</div>
<div class="row">
<div class="col-md-8 m-auto">
<div class="form-group">
<label asp-for="MetMyNeeds" class="control-label">Met my needs - worked toward a plan which addressed both the diagnosis and
my concerns about my illness.
</label>
<select asp-for="MetMyNeeds" class="form-control"
asp-items="#(new SelectList(ViewBag.PatientChoice, "Value", "Text"))"></select>
<span asp-validation-for="MetMyNeeds" class="text-danger"></span>
</div>
</div>
</div>
<div class="row">
<div class="col-md-8 m-auto">
<div class="form-group">
<label asp-for="PatientComments" class="control-label"></label>
<textarea asp-for="PatientComments" class="form-control"
placeholder="Please add any additional comments you would like to express about this examination."></textarea>
</div>
</div>
</div>
<div class="row">
<div class="col-md-8 m-auto">
<div class="form-group">
<input type="submit" value="Submit" class="btn btn-primary" />
</div>
</div>
</div>
</form>
</div>
</div>
</div>
<div class="row">
<div class="col-md-8 m-auto">
<div class="text-center">
<a asp-action="Index">Back to List</a>
</div>
</div>
</div>
#section Scripts {
#{
await Html.RenderPartialAsync("_ValidationScriptsPartial");
}
}
Once the form is completed and all required fields are entered, if I hit the "Submit" button, I get:
This localhost page can’t be found
No webpage was found for the web
address:
https://localhost:5001/SPE/AddSPEPatientEval/18659/15129/235/4
HTTP ERROR 404
What am I missing guys? I am sure it is something that is in plain site. I am just have looked at it too long and cannot find what is going on.
What have I tried... I feel like everything. I have tried adding a route to the HttpPost statement. I have tried making sure all the model fields are not null. I do not know what else to try.
Thanks in advance for any help.
Your POST route should match the GET route:
[HttpPost("[controller]/[action]/{IDAppt}/{ActivityID}/{VendorID}/{PatientID}")]
Add [FromRoute] to the action parameters:
[HttpGet("[controller]/[action]/{IDAppt}/{ActivityID}/{VendorID}/{PatientID}")]
public async Task<IActionResult> AddSPEPatientEval([FromRoute]int IDAppt, [FromRoute] int ActivityID, [FromRoute] int VendorID, [FromRoute] int PatientID)
You should also change HttpGet to HttpPost.

Populate DropDown List in MVC App that Consumes a Web API

I have a simple MVC app that is consuming a web api via REST. The controller in the MVC app makes http calls to the web api to populate views within the MVC app with razor syntax.
I am trying to figure out how to populate a drop down list on one of the 'create' actions. I'm currently just using the scaffolded page:
#model ComicBookInventory.Shared.ComicBookWithAuthorsAndCharactersViewModel
#{
ViewData["Title"] = "CreateComicBook";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h1>CreateComicBook</h1>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="CreateComicBook">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Id" class="control-label"></label>
<input asp-for="Id" class="form-control" />
<span asp-validation-for="Id" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Title" class="control-label"></label>
<input asp-for="Title" class="form-control" />
<span asp-validation-for="Title" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Description" class="control-label"></label>
<input asp-for="Description" class="form-control" />
<span asp-validation-for="Description" class="text-danger"></span>
</div>
<div class="form-group form-check">
<label class="form-check-label">
<input class="form-check-input" asp-for="IsRead" /> #Html.DisplayNameFor(model => model.IsRead)
</label>
</div>
<div class="form-group">
<label asp-for="DateRead" class="control-label"></label>
<input asp-for="DateRead" class="form-control" />
<span asp-validation-for="DateRead" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Rating" class="control-label"></label>
<input asp-for="Rating" class="form-control" />
<span asp-validation-for="Rating" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Genre" class="control-label"></label>
<input asp-for="Genre" class="form-control" />
<span asp-validation-for="Genre" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="CoverUrl" class="control-label"></label>
<input asp-for="CoverUrl" class="form-control" />
<span asp-validation-for="CoverUrl" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
#section Scripts {
#{
await Html.RenderPartialAsync("_ValidationScriptsPartial");
}
}
Which gets populated from this action in the controller:
public async Task<IActionResult> CreateComicBook(ComicBookWithAuthorsAndCharactersViewModel model)
{
string uri = $"https://localhost:5001/api/comicbook/add-book/";
HttpClient client = _httpClientFactory.CreateClient(
name: "ComicBookInventory.Api");
var postTask = await client.PostAsJsonAsync<ComicBookWithAuthorsAndCharactersViewModel>(uri, model);
if (postTask.IsSuccessStatusCode)
{
return RedirectToAction("GetAllComics");
}
else
{
return View(model);
}
}
Here is the view model definition:
namespace ComicBookInventory.Shared
{
public class ComicBookWithAuthorsAndCharactersViewModel
{
public int Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public bool IsRead { get; set; }
public DateTime? DateRead { get; set; }
public int? Rating { get; set; }
public string Genre { get; set; }
public string? CoverUrl { get; set; }
/// <summary>
/// Navigation properties
/// </summary>
/// a book can have many authors
public ICollection<string>? AuthorNames { get; set; }
public ICollection<string>? CharacterNames { get; set; }
}
}
My question is, I want to add a drop down checklist to the view, so that when I am creating a comic book, I can select Authors that currently exist in the database. The same for characters.
Here is the entire code base in case anyone is interested: https://github.com/rnemeth90/ComicBookInventoryApp
I normally try and figure things out on my own (I'm relatively new to EF Core, and have very little experience with many-to-many relationships in EF core). I have tried various things and struggled with this for most of my weekend. I feel like this should be relatively simple but cannot figure it out. Please help.
In your Code, I noticed that you used #Html.DropDownList to realzie the selector element, and using form submit to handle the data. So I did a test in my side:
#{
List<SelectListItem> listItems= new List<SelectListItem>();
listItems.Add(new SelectListItem
{
Text = "Exemplo1",
Value = "Exemplo1_v"
});
listItems.Add(new SelectListItem
{
Text = "Exemplo2",
Value = "Exemplo2_v",
Selected = true
});
listItems.Add(new SelectListItem
{
Text = "Exemplo3",
Value = "Exemplo3_v"
});
}
<form asp-action="Create">
<div class="form-group">
AuthorNames
<div class="col-md-10">
#Html.DropDownListFor(model => model.Author, listItems, "-- Select author --")
</div>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
My model contains a public string? Author { get; set; } property, and when I click the submit button, the select value will be submitted, so when you want to pass the FullName to your controller, you need to set it as the Value of the SelectListItem.

Get the last added value in TABalance and show it in TSBalance input automatically when the page loaded

below my html code
<div class="border backgroundWhite border-info">
<div class="row">
<div class="col-6">
<div class="form-group row">
<div class="col-4">
<label asp-for="Shift.TSBalance"></label>
</div>
<div class="col-6">
<input asp-for="Shift.TSBalance" class="form-control" id="sbalance" readonly value="#Model.V" onkeyup="sum()" type="text" />
</div>
</div>
<div class="form-group row">
<div class="col-4">
<label asp-for="Shift.TSupply"></label>
</div>
<div class="col-6">
<input asp-for="Shift.TSupply" class="form-control" id="supply" value="0" onkeyup="sum()" type="text"/>
</div>
<span asp-validation-for="Shift.TSupply" class="text-danger"></span>
</div>
<div class="form-group row">
<div class="col-4">
<label asp-for="Shift.TTotal"></label>
</div>
<div class="col-6">
<input asp-for="Shift.TTotal" class="form-control" readonly id="trbalance" value="0" onkeyup="sum()" type="text"/>
</div>
<span asp-validation-for="Shift.TTotal" class="text-danger"></span>
</div>
</div>
<div class="col-6">
<div class="form-group row">
<div class="col-7">
<label asp-for="Shift.TCBalance"></label>
</div>
<div class="col-5">
<input asp-for="Shift.TCBalance" class="form-control" id="calcebalance" value="0" onkeyup="sum()" type="text" readonly/>
</div>
</div>
<div class="form-group row">
<div class="col-7">
<label asp-for="Shift.TABalance"></label>
</div>
<div class="col-5">
<input asp-for="Shift.TABalance" class="form-control" id="actualebalance" value="0" onkeyup="sum()" type="text" />
</div>
</div>
<div class="form-group row">
<div class="col-7">
<label asp-for="Shift.TDifferance" class="text-info"></label>
</div>
<div class="col-5">
<input asp-for="Shift.TDifferance" class="form-control" style="background-color:blue;color:white;font-size:larger" id="differance" value="0" onkeyup="sum()" type="text" readonly />
</div>
</div>
</div>
</div>
</div>
</div>
</div>
my page model
namespace Elwady.Pages.ShiftClosing
{
public class CreateModel : PageModel
{
private readonly ApplicationDbContext _db;
public SelectList Exlist { get; set; }
[BindProperty]
public Shift Shift { get; set; }
public CreateModel(ApplicationDbContext db)
{
_db = db;
}
public IActionResult OnGet()
{
this.Exlist = new SelectList(_db.ExpensesList, "Id", "ExName");
return Page();
}
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
};
_db.Shift.Add(Shift);
await _db.SaveChangesAsync();
return RedirectToPage("../Index");
}
}
}
my data table
public class Shift
{
public int Id { get; set; }
public int TSBalance { get; set; }
public int TSupply { get; set; }
public int TTotal { get; set; }
public int TCBalance { get; set; }
public int TABalance { get; set; }
public int TDifferance { get; set; }
}
i tried to get this value in variable in Get Handler but i couldn't assign it to page UI
if i need to print out this form after submit to specific printer, how can i do that?
I am new in Asp.Net
<input asp-for="Shift.TABalance" class="form-control" id="actualebalance" value="0" onkeyup="sum()" type="text" />
First, please check the input elements, since you add the value attribute (value="0"), in this scenario, even if the page model Shift contains data, it will display 0, because the value="0" attribute will override the asp-for="Shift.TABalance".
So try to remove the value="0" attribute from the input element.
Then, to get the TABalance entered value and assign it to the TSBalance, you can use the onInput event. code like this:
<div class="col-6">
<input asp-for="Shift.TSBalance" class="form-control" id="sbalance" readonly value="#Model.Shift.TSBalance" onkeyup="sum()" type="text" />
</div>
<div class="col-5">
<input asp-for="Shift.TABalance" class="form-control" id="actualebalance" onkeyup="sum()" oninput="TABalanceInput()" type="text" />
</div>
#section Scripts{
<script>
function sum(){
}
//update sbalance's value when update the actualebalance's value
function TABalanceInput(){
var actualebalance = document.getElementById("actualebalance");
var s = actualebalance.value;
var sbalance = document.getElementById("sbalance");
sbalance.value = s;
}
//after page loaded, trigger the TABalanceInput function and update the actualebalance's value
$(document).ready(function(){
TABalanceInput();
});
</script>
}
The result is like this: after page load, it will trigger the TABalanceInput function to update the value. And if modify the TABalance's value, it will also update the TSBalance's value.
Update:
First, this is the OnGet method in the above sample:
public IActionResult OnGet()
{
//this.Exlist = new SelectList(_db.ExpensesList, "Id", "ExName");
this.Exlist = new SelectList(new List<SelectListItem>()
{
new SelectListItem(){ Value="1", Text="A"},
new SelectListItem(){ Value="2", Text="B"},
new SelectListItem(){ Value="3", Text="C"},
});
// create a new shift, and display them in the view.
Shift = new Shift()
{
Id = 0,
TABalance = 2,
TCBalance = 0,
TDifferance = 0,
TSBalance = 0,
TSupply = 0,
TTotal = 0
};
return Page();
}
i need the last value added to databse in TABalance column retrieved
on TSBalance input box
In previous reply, I create a new Shift to render the data. If you want to get the last added data, you need to soft the shift data and get the last added item, then return it to view page. Refer the following code:
Shift = _db.Shifts.OrderByDescending(c => c.Id).First();
Then, the result is like this:

Why would a bool value not be displaying in view correctly?

I have a bool value that I'm using to distinguish between the first item in a series of other items being saved to a link table in my DB. The constructor for the ViewModel sets this to true by default. When my ViewModel is posted to the controller my controller action changes this value to false and passes the edited ViewModel back to the View where this value is saved in a hidden field.
The issue I have is that after the value has been changed, my View is still displaying this value as true. Does anyone have any idea where I'm going wrong?
I also have an issue where I reset the values back to default but these are being retained when the view is reloaded after the post action in executed.
ViewModel
public class NewScriptViewModel
{
public Patient Patient { get; set; }
public int PatientId { get; set; }
public int PrescriberId { get; set; }
public int DrugId { get; set; }
public int Qty { get; set; }
public string Directions { get; set; }
public bool FirstItem { get; set; }
public bool NextItem { get; set; }
public int ScriptId { get; set; }
public NewScriptViewModel()
{
FirstItem = true;
NextItem = false;
}
}
View
#model DispensingApp.Models.ViewModels.NewScriptViewModel
#{
ViewData["Title"] = "New Script";
}
<h1>#Model.Patient.FullName</h1>
<h3>HCN : #Model.Patient.HCN</h3>
<hr />
<div class="row">
<br />
<div class="col-12" onload="newScriptItemForm.reset()">
<form id="newScriptItemForm" asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input asp-for="ScriptId" type="hidden" value="#ViewData["ScriptId"]" />
<input asp-for="DrugId" type="hidden" />
<table id="drugSelectionTable" class="table table-hover">
<thead>
<tr class="table-secondary">
<th></th>
<th>Name</th>
<th>Pack Size</th>
<th>Generic Ingredients</th>
<th>Stock</th>
</tr>
</thead>
</table>
<div class="form-group">
<div><input asp-for="PatientId" type="hidden" /></div>
</div>
<div class="form-group row">
<div class="col-2">
<label asp-for="Patient.Prescriber.FullName" class="control-label"></label>
</div>
<div class="col-4">
#if (Model.FirstItem)
{
<select asp-for="PrescriberId" class="form-control" asp-items="ViewBag.PrescriberId"></select>
}
else
{
<input asp-for="PrescriberId" type="hidden" />
<input type="text" placeholder="#Model.PrescriberId" class="disabled" />
}
</div>
</div>
<div class="form-group row">
<div class="col-2">
<label asp-for="Qty" class="control-label"></label>
</div>
<div class="col-4">
<input asp-for="Qty" class="form-control" />
</div>
<span asp-validation-for="Qty" class="text-danger"></span>
</div>
<div class="form-group row">
<div class="col-2">
<label asp-for="Directions" class="control-label"></label>
</div>
<div class="col-4">
<textarea asp-for="Directions" rows="3" class="form-control"></textarea>
</div>
<span asp-validation-for="Directions" class="text-danger"></span>
</div>
<div class="form-group row">
<div class="col-2">
<input asp-for="FirstItem" type="hidden" />
<input id="nextItem" asp-for="NextItem" type="hidden" />
<button id="nextItemBtn" #*type="submit" value="Next Item"*# class="btn btn-primary form-control">Next Item</button>
</div>
<div class="col-2">
<button asp-action="NewScript" class="btn btn-success form-control">Next Script</button>
</div>
<div class="col-2">
<a asp-controller="Patients" asp-action="Details" asp-route-id="#Model.PatientId" class="btn btn-danger form-control">Cancel</a>
</div>
</div>
</form>
</div>
</div>
Controller Post
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(NewScriptViewModel viewModel,
[Bind("PatientId, PrescriberId")] Script script,
[Bind("DrugId, Qty, Directions")] ScriptDrug scriptDrug)
{
if (ModelState.IsValid)
{
//if first item of script -- create script entry
if (viewModel.FirstItem)
{
_context.Script.Add(script);
await _context.SaveChangesAsync();
viewModel.ScriptId = script.Id;
viewModel.FirstItem = false;
}
scriptDrug.ScriptId = (int)viewModel.ScriptId;
var drug = _context.Drug.Find(scriptDrug.DrugId);
drug.StockQty -= scriptDrug.Qty;
_context.ScriptDrug.Add(scriptDrug);
await _context.SaveChangesAsync();
return await Create(script.PatientId, viewModel);
}
viewModel.NextItem = false;
return await Create(script.PatientId, viewModel);
}
Controller Get
public async Task<IActionResult> Create(int id, NewScriptViewModel viewModel)
{
var patient = await _context.Patient.FindAsync(id);
var vm = new NewScriptViewModel();
//if not first item, reset view model but retain script id
if (!viewModel.FirstItem)
{
vm = viewModel;
}
vm.Patient = patient;
vm.PatientId = patient.Id;
return View(vm);
}
The HTML that is rendered to the browser after all of this looks like:
<div class="col-2">
<input type="hidden" data-val="true" data-val-required="The FirstItem field is required." id="FirstItem" name="FirstItem" value="True">
<input id="nextItem" type="hidden" data-val="true" data-val-required="The NextItem field is required." name="NextItem" value="True">
<button id="nextItemBtn" class="btn btn-primary form-control">Next Item</button>
</div>

Create list of entities in asp.net core mvc

I want to create an entity in asp.net core mvc. The entity represents an exercise workout, and it has a child collection of exercise sets:
public class Workout
{
public DateTime Date { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual ICollection<Set> Sets { get; set; }
}
public class Set
{
public int Id { get; set; }
public int ExerciseId { get; set; }
public int WorkoutId { get; set; }
public decimal Weight { get; set; }
public int Reps { get; set; }
public virtual Exercise Exercise { get; set; }
public virtual Workout Workout { get; set; }
}
The form looks like this:
The "Add Set" button uses JavaScript to add a group of "set" related fields to the form, so there can be any number of "set" related fields.
Here is my existing code for the view containing the form:
#model Weightlifting.Models.Workout
#{
ViewData["Title"] = "Add Workout";
}
<h2>Add Workout</h2>
<form asp-action="Create">
<div class="form-horizontal">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Date" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="Date" class="form-control" />
<span asp-validation-for="Date" class="text-danger" />
</div>
</div>
<div class="form-group">
<label asp-for="Name" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger" />
</div>
</div>
<div class="form-group">
<label asp-for="Description" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="Description" class="form-control" />
<span asp-validation-for="Description" class="text-danger" />
</div>
</div>
<div class="form-group">
<fieldset class="form-add-set">
<label for="sets" class="col-md-2 control-label">Sets</label>
<div class="col-md-10">
<div class="add-sets">
<div class="form-inline add-set">
<div class="form-group">
<label class="control-label">Exercise</label>
<select class="form-control" asp-items="ViewBag.Exercises"></select>
</div>
<div class="form-group">
<label asp-for="Sets.First().Reps" class="control-label"></label>
<input asp-for="Sets.First().Reps" placeholder="Reps" class="form-control" />
<span asp-validation-for="Sets.First().Reps" class="text-danger" />
</div>
<div class="form-group">
<label asp-for="Sets.First().Weight" class="control-label"></label>
<input asp-for="Sets.First().Weight" class="form-control" />
<span asp-validation-for="Sets.First().Weight" class="text-danger" />
</div>
<div class="form-group">
<button class="btn btn-remove-set" data-toggle="tooltip" title="Remove Set"><span class="glyphicon glyphicon-minus"></span></button>
</div>
</div>
</div>
<button class="btn btn-add-set">Add Set</button>
</div>
</fieldset>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-primary" />
</div>
</div>
</div>
</form>
<div>
<a asp-action="Index"><span class="glyphicon glyphicon-arrow-left"></span> Back</a>
</div>
#section Scripts {
<script type="text/javascript">
$(document).ready(function () {
var wrapper = $('.add-sets');
$(".btn-add-set").click(function(e) {
e.preventDefault();
$('.add-set:first-child').clone(true).appendTo(wrapper);
$('.add-set .btn-remove-set').show();
});
$('.btn-remove-set').click(function (e) {
e.preventDefault();
$(this).parents('.add-set').remove();
removeButton();
});
function removeButton() {
if ($('.add-set').length == 1) {
$('.add-set .btn-remove-set').hide();
}
}
});
</script>
}
And here is the controller action that the view posts to:
public async Task<IActionResult> Create([Bind("Id,Date,Description,Name")] Workout workout)
{
// do stuff
}
My question is this: How do I code the form so that the "Sets" post to the Set collection in the workout model?
Remove the [Bind("Id,Date,Description,Name")] from your action method.
public async Task<IActionResult> Create(Workout workout)
{
// do stuff
}
Then apply indexing on your Set properties like this.
<div class="form-inline add-set">
<div class="form-group">
<label class="control-label">Exercise</label>
<select name="Sets[0].ExerciseId" class="form-control"><option value="1">Push Up</option>
<option value="1">Set Up</option>
</select>
<div class="add-sets">
<div class="form-inline add-set">
<div class="form-group">
<label class="control-label">Exercise</label>
<select name="Sets[0].ExerciseId" class="form-control" asp-items="ViewBag.Exercises"></select>
</div>
<div class="form-group">
<label asp-for="Sets.First().Reps" class="control-label"></label>
<input asp-for="Sets.First().Reps" name="Sets[0].Reps" placeholder="Reps" class="form-control" />
<span asp-validation-for="Sets.First().Reps" class="text-danger" />
</div>
<div class="form-group">
<label asp-for="Sets.First().Weight" class="control-label"></label>
<input asp-for="Sets.First().Weight" name="Workout.Sets[0].Weight" class="form-control" />
<span asp-validation-for="Sets.First().Weight" class="text-danger" />
</div>
<div class="form-group">
<button class="btn btn-remove-set" data-toggle="tooltip" title="Remove Set"><span class="glyphicon glyphicon-minus"></span></button>
</div>
</div>
</div>
When you add new row dynamically make sure the indexing number must be sequential, start at 0 and not skip any iteration.

Categories

Resources