I have a razor page create form. The model it accepts is a NewHireClassModel:
public class NewHireClassModel
{
public int new_hire_class_id { get; set; }
public int district_id { get; set; }
public string district_name { get; set; }
public int class_size { get; set; }
[DisplayFormat(DataFormatString = "MM dd yyyy")]
public DateTime week_ending_date { get; set; }
public bool is_cancelled { get; set; }
public string approval_status_name { get; set; }
public int? new_requested_class_size { get; set; }
public bool cancellationed_requested { get; set; }
public string created_by { get; set; }
public DateTime created_on { get; set; }
public string modified_by { get; set; }
public DateTime modified_on { get; set; }
public List<ClassLocationModel> class_locations { get; set; }
}
Notice the last item is a List of ClassLocations.
ClassLocationModel:
public class ClassLocationModel
{
public int class_location_id { get; set; }
[DisplayFormat(DataFormatString = "0:d")]
public DateTime start_date { get; set; }
[DisplayFormat(DataFormatString = "0: dd hh")]
public DateTime start_time { get; set; }
public int location_size { get; set; }//refers to the number of new hires at this location
public string location_category { get; set; } //District Office, Hotel, or Other
public List<LocationModel> location_choices { get; set; }
public string location_address { get; set; }
public string created_by { get; set; }
public DateTime created_on { get; set; }
public string modified_by { get; set; }
public DateTime modified_on { get; set; }
public bool is_deleted { get; set; }
}
I can create a list of ClassLocations and use that to create a NewHireClass and pass that to a view and show the items in the NewHireClass and the NewHireClass's ClassLocations. That's no problem.
What I can't figure out is how to build a create form that will accept a NewHireClass and the first location (new hire classes MUST have at least one ClassLocation). Here is my code so far.
Controller that builds a blank ClassLocation and NewHireClass with defaults and parameters specified from an HTTP Get:
public IActionResult CreateNewClassRequestForm(int _district_id, string _district_name, string _week_ending_date, int _class_size)
{
List<LocationModel> _location_choices = new List<LocationModel>();
List<ClassLocationModel> blank_location = new List<ClassLocationModel>();
_location_choices.Add(new LocationModel { id = 1, location = "District Office" });
_location_choices.Add(new LocationModel { id = 2, location = "Hotel" });
_location_choices.Add(new LocationModel { id = 3, location = "Other" });
blank_location.Add(new ClassLocationModel{
location_size = _class_size
, start_date = Convert.ToDateTime(_week_ending_date).AddDays(-6)
, location_address = "tbd"
, start_time = new DateTime(1,1,1,8,0,0)
, location_choices = _location_choices
});
NewHireClassModel blank_request = new NewHireClassModel {
new_hire_class_id = 0
, district_id = _district_id
, district_name = _district_name
, class_size = _class_size
, week_ending_date = Convert.ToDateTime(_week_ending_date)
, approval_status_name = ""
, cancellationed_requested = false
, created_by = ""
, created_on = DateTime.Now
, modified_by = ""
, modified_on = DateTime.Now
, class_locations = blank_location
};
return View(blank_request);
}
The create form view:
@model OperationalForecast.Models.NewHireClassModel
<h4>ClassRequestModel</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="ProcessCreateNewClassRequest">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<input type=hidden asp-for="new_hire_class_id" class="form-control" />
</div>
<div class="form-group">
<input type=hidden asp-for="district_id" class="form-control" />
</div>
<div class="form-group">
<label asp-for="district_name" class="control-label"></label>
<input readonly asp-for="district_name" class="form-control" />
<span asp-validation-for="district_name" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="week_ending_date" class="control-label"></label>
<input readonly type="date" asp-for="week_ending_date" class="form-control" />
<span asp-validation-for="week_ending_date" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="class_locations.ElementAt(0).start_date" class="control-label"></label>
<input type="date" asp-for="class_locations.ElementAt(0).start_date" class="form-control" min="@Model.class_locations.ElementAt(0).start_date.ToString("yyyy-MM-dd")" max="@Model.week_ending_date.ToString("yyyy-MM-dd")" />
<span asp-validation-for="class_locations.ElementAt(0).start_date" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="class_size" class="control-label"></label>
<input asp-for="class_size" class="form-control" />
<span asp-validation-for="class_size" class="text-danger"></span>
</div>
@foreach (var item in Model.class_locations.ElementAt(0).location_choices)
{
<br />
<input asp-for="class_locations.ElementAt(0).location_category" type="radio" value="@item.location" /> @(item.location)
}
<br />
<div class="form-group">
<label asp-for="@Model.class_locations.ElementAt(0).location_address" class="control-label"></label>
<input asp-for="@Model.class_locations.ElementAt(0).location_address" class="form-control" />
<span asp-validation-for="@Model.class_locations.ElementAt(0).location_address" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="class_locations.ElementAt(0).start_time" class="control-label"></label>
<input type="time" asp-for="class_locations.ElementAt(0).start_time" class="form-control" />
<span asp-validation-for="class_locations.ElementAt(0).start_time" class="text-danger"></span>
</div>
<div class="form-group">
<input type=hidden asp-for ="approval_status_name" class="form-control" />
</div>
<div class="form-group">
<input type=hidden asp-for="cancellationed_requested" class="form-control" />
</div>
<div class="form-group">
<input type=hidden asp-for="created_by" class="form-control" />
</div>
<div class="form-group">
<input type=hidden asp-for="created_on" class="form-control" />
</div>
<div class="form-group">
<input type=hidden asp-for="modified_by" class="form-control" />
</div>
<div class="form-group">
<input type=hidden asp-for ="modified_on" class="form-control" />
</div>
<div class="form-group">
<input type="submit" value="Initiate New Class Request" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
Highlighting two of the ClassLocation inputs:
<div class="form-group">
<label asp-for="@Model.class_locations.ElementAt(0).location_address" class="control-label"></label>
<input asp-for="@Model.class_locations.ElementAt(0).location_address" class="form-control" />
<span asp-validation-for="@Model.class_locations.ElementAt(0).location_address" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="class_locations.ElementAt(0).start_time" class="control-label"></label>
<input type="time" asp-for="class_locations.ElementAt(0).start_time" class="form-control" />
<span asp-validation-for="class_locations.ElementAt(0).start_time" class="text-danger"></span>
Notice I tried two different ways. Both worked great for populating the form with the default value. Neither saved the value back to a ClassLocation within the NewHireClass.
I was able to find a solution with some trial and error.
Step 1: Add name attribute to <input>
tag
<div class="form-group">
<label asp-for="@Model.class_locations.ElementAt(0).location_address" class="control-label"></label>
<input asp-for="@Model.class_locations.ElementAt(0).location_address" **name="_location_address"** class="form-control" />
<span asp-validation-for="@Model.class_locations.ElementAt(0).location_address" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="class_locations.ElementAt(0).start_time" class="control-label"></label>
<input type="time" asp-for="class_locations.ElementAt(0).start_time" **name="_start_time"** class="form-control" />
<span asp-validation-for="class_locations.ElementAt(0).start_time" class="text-danger"></span>
</div>
That will pass the parameters to the method specified in the action attribute <form asp-action="ProcessCreateNewClassRequest">
Step 2: take the arguments from the method and use to create a ClassLocation model and add that to the NewHireClass model
public IActionResult ProcessCreateNewClassRequest(NewHireClassModel newhireclass, string _location_address, DateTime _start_date, DateTime _start_time, string _location_category)
{
List<ClassLocationModel> first_location = new List<ClassLocationModel>();
first_location.Add(new ClassLocationModel { location_address = _location_address, start_date = _start_date, start_time = _start_time, location_category = _location_category });
newhireclass.class_locations = first_location;
List<NewHireClassModel> new_hire_classes = new List<NewHireClassModel>();
new_hire_classes.Add(newhireclass);
return View(new_hire_classes);
}
Note this function simply returns the NewHireClass model to a view to display to verify it is correctly populated for testing purposes.