Create/Update one-to-many relationship models on one page - asp.net-core

Can't find an example of this online that doesn't involve creating or updating rows for each individual model on separate pages. I have a simple visitation form, where the overall Visit is a model, with the host's information and other generic parameters. The second model is Visitor, of which a Visit can have many. Relationship works great, I can update them separately.
I've built a request form which I'd like to do everything on one page. Top part of the form is generic information about the visit and the bottom half is a javascript dynamic form section to add/remove visitors on the fly. Form works great, enters the Visit information just fine, but I can't take in the List from the information coming in. Names for them are following the 'Visitors[1].Name' etc etc format.
I've tried adding List Visitors as a variable inside the Visit model, I've also tried a combined custom model, containing both Visit and Visitors. Anyone have any suggestions?

According to your description, I guess this issue may be related with input's name value. Since the model binding will bind the value according to the parameter's name. I suggest you could check the input name to make sure it is match the model binding format.
For example:
If your visit and visitor's class as below:
public class Visit
{
public int id { get; set; }
public string visitname { get; set; }
public List visitors { get; set; }
}
public class Visitors
{
public int id { get; set; }
public string visitor { get; set; }
}
Then the visitor's input name should be visitors[0].id , visitors[1].id,visitors[2].id, visitors[0].visitor,visitors[1].visitor or else.
More details, you could refer to below codes:
Controller:
public class HomeController : Controller
{
Visit visits;//It is a global variable
public HomeController()
{
visits = new Visit
{
id = 10,
visitname = "visit1",
visitors = new List<Visitors>
{
new Visitors{ id=19, visitor="visitor1"},
new Visitors{ id=20, visitor="visitor2"}
}
};
}
public IActionResult Index()
{
return View(visits);
}
}
In Index.cshtml, the changes made by JavaScript to the view may affect the changes of the subscript in Visitors1.Name. So the index value should be changed when adding elements and deleting corresponding elements.
#model solution930.Models.Visit
#{
//Set a global variable
var count = Model.visitors.Count;
}
<form action="/home/get" method="post">
id
<input asp-for="#Model.id" />
visitname
<input asp-for="#Model.visitname" />
<div id="visitors">
#for (var i = 0; i <count; i++)
{
<div class="visitor">
<input name="visitors[#i].id" asp-for="#Model.visitors[i].id" />
<input name="visitors[#i].visitor" asp-for="#Model.visitors[i].visitor" />
<input type="button" name="name" value="deleterow" onclick="del(event,#Model.visitors[i].id)" />
</div>
}
</div>
<input type="submit" name="name" value="sub" />
</form>
<button id="addvisit" onclick="add()">add</button>
#section Scripts{
<script>
var hasCount=#count;
function del(e, id) {
if (index == 0) {
console.log(e.currentTarget.parentElement)
e.currentTarget.parentElement.remove()
return;
}
location.href = '/home/delete?id=' + id
}
function add() {
var ele = '<div class="visitor"> <input name="visitors[' + hasCount + '].id" type="number" data-val="true" data-val-required="The id field is required." id="visitors_' + hasCount + '__id" value=""> <input name = "visitors[' + hasCount + '].visitor" type = "text" id = "visitors_' + hasCount + '__visitor" value = "" > <input type="button" name="name" value="deleterow" onclick="del(event,0)"> </div>'
$('#visitors').last().parent().append(ele)
hasCount++
console.log(hasCount)
}
</script>
}
Result:

Related

Why are my Guids being cleared and time being stripped from my DateTime fields in AspnetCore app?

Net 5.0
I have several fields on a form, two are readonly fields. One is a Guid the other is a DateTime.
When changes are submitted, the Guid is set to 00000000-0000-0000-0000-000000000000 and the time is removed from my DateTime column 2021-04-13 02:36:37.4567940 becomes 2021-04-13 00:00:00.0000000
<div class="form-group">
<label asp-for="Record.MyGuid" class="control-label"></label>
<input asp-for="Record.MyGuid" class="form-control" readonly/>
<span asp-validation-for="Record.MyGuid" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Record.FormInserted" class="control-label"></label>
<input asp-for="Record.FormInserted" class="form-control" readonly/>
<span asp-validation-for="Record.FormInserted" class="text-danger"></span>
</div>
As they appear on the model
[Display(Name="Website ID")]
[Required(ErrorMessage = "There should not be a record without an Id. Please report this.")]
[Editable(false)]
public Guid MyGuid { get; private set; }
[Display(Name="Submission date")]
[DataType(DataType.Date)]
[Required(ErrorMessage = "Submission date is required.")]
public DateTime FormInserted { get; set; }
When the record is retrieved, it has a complete Guid and date. However, when the Post action starts those values are changed even though the form still correctly displays the values.
Here is the Post handler. When I inspect the object Record, the values have been changed when it reaches the if(Record.Id > 0) step.
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
// At this point, the Guid is now all Zeros
// and the time has been removed from the DateTimee
if(Record.Id > 0)
{
await _RecordData.Update(Record);
}
else
{
// No Id means new record. Add it.
_RecordData.Add(Record);
}
// Commit the changes to the database
await _RecordData.Commit();
// Message to pass to the details page
TempData["StatusMessage"] = "Record saved!";
// Redirect to Details page
return RedirectToPage("./Edit", new { RecordId = Record.MyGuid });
}
What do I need to do to prevent this from happening and preserve the read-only data?
The issue is caused by your model class.
You can change your model like following.
[Display(Name="Website ID")]
[Required(ErrorMessage = "There should not be a record without an Id. Please report this.")]
public Guid MyGuid { get; set; }
[Display(Name="Submission date")]
[DataType(DataType.DateTime)]
[Required(ErrorMessage = "Submission date is required.")]
public DateTime FormInserted { get; set; }
Delete the line [Editable(false)] and change DataType.Date to DataType.DateTime.
Besides delete the code [Editable(false)],you also need to delete the code private.

Razor Pages CheckBox throwing error in Edit Page

I need some help in inserting checkbox value to mssql database and retrieving the same in edit page.
This is model class
public class RequestForm{
[Key]
public int ID { get; set; }
public string OtherCompetitorsChecked { get; set; }
public string OtherCompetitorName { get; set; }
}
This is my RequestForm.cshtml file
<div class="tr">
<div class="td">
<input id="ChkOthers" style="margin-left:40px;" asp-for="RequestForm.OtherCompetitorsChecked" type="checkbox" value="Others" /> Others
</div>
<div class="td">
<input id="CompetitorsOthersName" title="Please Fill In Other Competitor Name" asp-for="RequestForm.OtherCompetitorName" type="text" class="form-control-slp" required disabled style="width:50%" />
</div>
</div>
When checking im inserting the checkbox value into database thats why i used string datatype in model class.Im able to insert the data to the database,when im fetching the data its showing error like below
InvalidOperationException: Unexpected expression result value 'Others' for asp-for. 'Others' cannot be parsed as a 'System.Boolean'.
is there any way to fix this?
InvalidOperationException: Unexpected expression result value 'Others'
for asp-for. 'Others' cannot be parsed as a 'System.Boolean'.
public string OtherCompetitorsChecked { get; set; }
This issue relates the OtherCompetitorsChecked data type. The Input Tag Helper sets the HTML type attribute based on the .NET type. Form the following list, we can see that, for the Input checkbox, the .Net type should be Bool type.
So, to solve the above issue, change the OtherCompetitorsChecked's data type to the bool type:
public class RequestForm
{
[Key]
public int ID { get; set; }
public bool OtherCompetitorsChecked { get; set; }
public string OtherCompetitorName { get; set; }
}
Then, in the Get or Post method, when you set its value, it should be true or false.
public void OnGet()
{
RequestForm = new RequestForm() {
ID = 1001,
OtherCompetitorName = "AA",
OtherCompetitorsChecked = true
};
}
Besides, in your application, might be you want to display a list of selected items using checkbox, and want to use a string type to store the selected value. If that is the case, you could try to use the html <input type="checkbox" name ="selectedCourses"> element to display the items (without using Tag helper) or use a <select> tag, then, in the Post method, get the selected option's value via the html element's name property.
Code like this (you could change the checkbox value to the model data, model detail information, refer this tutorial):
<input type="checkbox" name="selectedCompetitorName" value="Others" />
the Post method:
public void OnPost(string selectedCompetitorName)
{
if (ModelState.IsValid)
{
var data = RequestForm;
}
}
<input id="CompetitorsOthersName" title="Please Fill In Other Competitor Name" asp-for="RequestForm.OtherCompetitorName" type="text"
class="form-control-slp" required disabled style="width:50%" />
At the end, according to the above code, I assume you want to make the CompetitorsOthersName text box readonly, if that is the case, try to remove the disabled attribute, and add the readonly attribute. Because, if using the disabled attribute, after submitting the form, the submitted model's CompetitorsOthersName property will be null. You can check it.

Why is my controller return wrong response data?

I`m using Ajax call to remove items from list. When user clicks on remove button (each button for row with id) it sends Ajax call (Post) to Order/RemoveFromCart. When backend is reached i remove the item from list. Im sure when returning data from controller the item i want to remove is truly removed. The problem is when i take my div and call html(data) from response it removes always the last item in the list.
Thank you for help.
I have tried update dotnet to latest version.
I have tried to remove Ajax call and reload whole view - that fixes the problem but i want to use an Ajax call.
#for (int i = 0; i < Model.CartItems.Count; i++)
{
<input asp-for="CartItems[i].ProductId" type="hidden">
<tr>
<td>
<input type="text" class="inputTextWithoutBorder" readonly="readonly" asp-for="CartItems[i].Product">
</td>
<td>
<select class="select-table" asp-for="#Model.CartItems[i].Packaging" asp-items="#Model.CartItems[i].PackageTypes"></select>
</td>
<td>
<input type="text" class="inputTextWithoutBorder" asp-for="#Model.CartItems[i].Amount" placeholder="Vyberte množství...">
</td>
<td>
<button id="removeFromCartId" onclick="removeFromCart(this);" type="button" value="#i" class="removeButton"></button>
</td>
</tr>
}
[HttpPost]
public IActionResult RemoveFromCart(OrderModel model, int itemToRemove)
{
if (model.CartItems[itemToRemove] != null)
{
model.CartItems.RemoveAt(itemToRemove);
}
return PartialView("_OrderCartWithCalendarPartial", model);
}
Model:
public class CartModel
{
public int ProductId { get; set; }
public int Amount { get; set; }
public string Packaging { get; set; }
public string Product { get; set; }
public List<SelectListItem> PackageTypes { get; } =
new List<SelectListItem>{
new SelectListItem {Value = "", Text = ""},
};
}
Javascript:
function removeFromCart(button) {
var index = button.value;
var placeholderElement = $('#orderFormId');
var myformdata = placeholderElement.serialize()
+ "&itemToRemove=" + index;
var result = $.post("/Order/RemoveFromCart", myformdata);
result.done(function (data) {
$("#cartWithCalendarDiv").html(data);
setDateFieldToZero();
});
}
Problem scenario:
Sending via Ajax list with two items (item0, item1). The controller receives exact list with two items. Remove item0 in controller and return partial view with that updated model. The page reloads only with item0. Why?

MVC : Pass values from textbox to controller action

I am new to MVC.Basically I need to pass values entered in the textbox from my view to controller action method. As I enter the values in the text box and click the enter button I need to display the value on the screen. I am currently unable to do so. Please find my code below
The model class
public class ProteinTrackingService
{
public int? Total { get; set; }
public int Goal { get; set; }
public void AddProtein(int? amount)
{
Total += amount;
}
}
The controller class
public class ProteinTrackerController : Controller
{
ProteinTrackingService proteinTrackingService = new ProteinTrackingService();
// GET: ProteinTracker
public ActionResult Index()
{
ViewBag.Total = proteinTrackingService.Total;
ViewBag.Goal = proteinTrackingService.Goal;
return View();
}
// GET: ProteinTracker/Details/5
public ActionResult AddProtein(ProteinTrackingService model)
{
proteinTrackingService.AddProtein(model.Total);
ViewBag.Total = proteinTrackingService.Total;
ViewBag.Goal = proteinTrackingService.Goal;
return View("Index");
}
}
The view
using (Html.BeginForm("ProteinTracker", "AddProtein",FormMethod.Post))
{
#Html.AntiForgeryToken()
<form>
<div class="form-horizontal">
<h4>Protein Tracker</h4>
<hr />
Total : #ViewBag.Total
Goal : #ViewBag.Goal
<input id="Text1" type="text" value="TextInput" /> <input type="Submit" value="Add" />
</div>
</form>
}
I am modifying the code above based on your suggestions. I basically need to display the following in the view
Total : value
Goal : value
Textbox control (To enter the total) Button (pass the total to contoller) Please note that when the user clicks the Add button the total should show in above field Total : value.
New View
#using (Html.BeginForm( "AddProtein","ProteinTracker", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Protein Tracker</h4>
<hr />
#Html.LabelFor(m => m.Total, "Total" ) <hr />
#Html.LabelFor(m => m.Goal, "Goal") <hr />
#Html.TextBoxFor(m => m.Total) <hr />
<input type="Submit" value="Add" />
</div>
}
New Controller
public class ProteinTrackerController : Controller
{
ProteinTrackingService proteinTrackingService = new ProteinTrackingService();
// GET: ProteinTracker
public ActionResult Index()
{
var model = new ProteinTrackingService()
{ Total = proteinTrackingService.Total, Goal = proteinTrackingService.Goal };
return View(model);
}
// GET: ProteinTracker/Details/5
public ActionResult AddProtein(ProteinTrackingService model)
{
proteinTrackingService.AddProtein(model.Total);
model.Total = proteinTrackingService.Total;
model.Goal = proteinTrackingService.Goal;
return View("Index",model);
}
}
You need to add the HttpPost attribute to your action.Looking at your form #using (Html.BeginForm( "AddProtein","ProteinTracker", FormMethod.Post)) , apparently you are sending a post request to your controller.
[HttpPost]
public ActionResult AddProtein(ProteinTrackingService model)
{
proteinTrackingService.AddProtein(model.Total);
model.Total = proteinTrackingService.Total;
model.Goal = proteinTrackingService.Goal;
return View("Index",model);
}
First of all your this syntax
using (Html.BeginForm("ProteinTracker", "AddProtein", FormMethod.Post))
already creates a form tag when html generates. No need to create from tag again in it.
So for your want, in view you need give to your input field a name
<input id="Text1" type="text" value="TextInput" name="textname"/>
and add this name as parameter in your controller method like that
public ActionResult AddProtein(ProteinTrackingService model,string textname)
{
// your code
return View("Index");
}
It will pass your textfield value from view to controller. For clearing your concept you may visit Loopcoder.com

Display selected value from listbox (MVC)

I have database, which consists of table "Jobs": job_id (int, primary key), job_nm (nchar(50)).
In "Model" folder I add ADO.NET Entity data model.
Controller is:
namespace ListBox_proj.Controllers
{
public class HomeController : Controller
{
myDBEntities1 db = new myDBEntities1();
[HttpGet]
public ActionResult Index()`enter code here`
{
var jobs = db.Jobs;
ViewBag.Jobs = jobs;
return View(jobs);
}
[HttpPost]
public ActionResult Index(string sel1)
{
ViewBag.Result = sel1;
return View();
}
}
}
View is:
#{
ViewBag.Title = "Index";
}
<html>
<head>
<meta name="viewport" content="width=device-width"/>
<title>Index</title>
</head>
<body>
<h1 class="label">Please, select the job you interested in:</h1> <br /><br />
<select name="sel1" id ="sel1">
<option>All</option>
#foreach (var j in ViewBag.Jobs)
{
<option><p>#j.job_nm</p></option>
}
</select>
<form action="/home/index" method="post">
<input type="submit" value ="Search">
<input type="text">#ViewBag.Result</input>
</form>
</body>
</html>
but when I choose the item in selectbox, and push "Search", I have error message:
![enter image description here][1]
MESSAGE IN ENGLISH - Object reference not set to an instance of the object.
Please, help me! What I do wrong?
How Can I correct it?
You have two issues here.
The first is that ViewBag.Jobs is only being populated on the [HttpGet]; you will need to ensure that the ViewBag is also populated in [HttpPost]. A better solution generally would be to use a proper view model - for example,
public class JobViewModel {
public Jobs JobsList { get; set; }
public string Result { get; set; }
}
Simply populate that in both the [HttpGet] and [HttpPost] procedures, and pass it into the View() call. Then add:
#model JobsViewModel
at the top of the view to allow you to access it through the (strongly-typed) Model.JobsList property.
A secondary issue that you'll come across is that sel isn't being submitted, as it falls outside of your <form> tags - remember, the only information submitted in a form is that contained within it.
Restructure your view to:
<form action="/home/index" method="post">
<select name="sel1" id ="sel1">
<option>All</option>
#foreach (var j in Model.JobsList)
{
<option><p>#j.job_nm</p></option>
}
</select>
<input type="submit" value ="Search">
<input type="text">#Model.Result</input>
</form>
and that problem should also be solved.