Displaying the appropriate fields for the selected unit in the data record form in ASP.NET Core MVC application - asp.net-core-mvc

Scenario: there are units to request a job, and the fields to be filled in the job request registration form differ according to these units. What method should be followed to realize this scenario?
I only have one registration form. Fields in the registration form
Unit to be requested,....etc Among the units to be requested by the user, for example Technical Services, Biomedical... etc. units can be selected.If the user chooses technical service in the registration form, they should see different fields to fill in. If he chooses biomedical, they should see different fields.
registration form design,
registration form cshtml
Correction in the Question
We have an application where employees in an enterprise can report problems such as electrical failure, wall painting, computer hardware failure to the technical units and these problems can be monitored by the technical units. The fields to be filled while forwarding the question to the relevant department differ according to the department to which the question will be forwarded.
For example: When a problem is reported to the computer technical service, the requested information is different.
If a problem is to be reported to the biomedical unit, different information must be entered.
For this reason, different forms were asked to be designed and the user was asked to fill in the relevant information from these forms after choosing the support unit regarding the problem to be reported.
I may have explained it wrong to Rena while explaining my question. But as a result, rena actually posted some code so I can call other views from a single view. I'm trying to implement what Rena did.

Here is a simple demo:
Model:
public class job
{
public int Id { get; set; }
public string JobName { get; set; }
}
public class RegisterForm
{
public string JobName { get; set; }
public int Age { get; set; }
public string Name { get; set; }
}
View:
Index.cshtml(located in /Views/Home/Index.cshtml):
model RegisterForm
<form>
<div class="form-group">
<label asp-for="JobName" class="control-label"></label>
<select asp-for="JobName" asp-items="ViewBag.Jobs" class="form-control">
<option>Choose A Job</option>
</select>
<span asp-validation-for="JobName" class="text-danger"></span>
</div>
<div id="result"></div>
</form>
#section Scripts
{
<script>
$('#JobName').on('change', function () {
$.ajax({
url: '#Url.Action("ReturnPartial", "Home")',
type: 'GET',
data: { JobName: $("#JobName").val() },
success: function (res) {
$("#result").html(res); //add this...
},
error: function () {
}
});
});
</script>
}
PartialView:
Biomedical.cshtml(located in /Views/Home/Biomedical.cshtml):
#model RegisterForm
<h1>Biomedical RegisterForm</h1>
<div class="form-group">
<label asp-for="Age" class="control-label"></label>
<input asp-for="Age" class="form-control" />
<span asp-validation-for="Age" class="text-danger"></span>
</div>
Technical.cshtml(located in /Views/Home/Technical.cshtml):
#model RegisterForm
<h1>Technical RegisterForm</h1>
<div class="form-group">
<label asp-for="Name" class="control-label"></label>
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
Controller:
public class HomeController : Controller
{
public IActionResult Index()
{
var jobs = new List<job>()
{
new job(){Id=1,JobName="Technical"},
new job(){Id=2,JobName="Biomedical"}
};
ViewBag.Jobs = new SelectList(jobs, "JobName", "JobName");
return View();
}
public IActionResult ReturnPartial(string JobName)
{
return PartialView(JobName);
}
}
Result:

Related

What is the best way to get a configured value to a class library for validation?

I currently have a Blazor app that references a class library. One of the pages in the web app is used for updating an instance of a class model in the class library. To validate I'm using Validation attributes on the class model. One of the fields for input is email which, for our software, is validated via a configurable regular expression (because each of our sites can be different).
I think the best way to do this is using a custom ValidationAttribute but I don't know the best way to get a value from the web app's app settings to the custom Validation class.
The following code is an example of what I'm trying to accomplish:
Blazor page:
<EditForm Model="#name" OnValidSubmit="HandleValidSubmit">
<DataAnnotationsValidator />
<ValidationSummary />
<div>
<label for="FirstName">First:</label>
</div>
<div>
<input id="FirstName" class="form-control profile-form-control" #bind-value="name.FirstName" #bind-value:event="oninput" type="text" maxlength="30" autocomplete="off" />
</div>
<div>
<label for="LastName">Last:</label>
</div>
<div>
<input id="LastName" class="form-control profile-form-control" #bind-value="name.LastName" #bind-value:event="oninput" type="text" maxlength="100" autocomplete="off" />
</div>
<div>
<label for="Email">Email:</label>
</div>
<div>
<input id="Email" class="form-control profile-form-control" #bind-value="name.Email" #bind-value:event="oninput" type="text" maxlength="100" autocomplete="off" />
</div>
<div>
<button type="submit" class="btn btn-primary">
Save
</button>
</div>
</EditForm>
Model (in separate class library):
public class Person
{
[Required(ErrorMessage = "First name required")]
public string FirstName { get; set; }
[Required(ErrorMessage = "Last name required")]
public string LastName { get; set; }
[Required(ErrorMessage = "Email required")]
[EmailFromRegexValidator(ErrorMessage = "Email not valid")]
public string Email { get; set; }
}
Custom Validation:
public class EmailFromRegexValidator : ValidationAttribute
{
private const string defaultEmailValidationRegex = "^[\\w-]+(\\.[\\w-]+)*#([a-zA-Z0-9-]+(\\.[a-zA-Z0-9-]+)*?\\.[a-zA-Z]{2,6}|(\\d{1,3}\\.){3}\\d{1,3})(:\\d{4})?$";
protected override ValidationResult IsValid(object value, ValidationContext context)
{
string emailRegexString = null;
var configurationBuilder = new ConfigurationBuilder();
var path = Path.Combine(Directory.GetCurrentDirectory(), "appsettings.json");
if (File.Exists(path))
{
configurationBuilder.AddJsonFile(path, false);
var root = configurationBuilder.Build();
emailRegexString = root.GetSection("AppConfiguration").GetSection("EmailRegex").Value;
}
emailRegexString = emailRegexString ?? defaultEmailValidationRegex;
Regex emailRegex = new Regex(emailRegexString);
if (value is string && emailRegex.IsMatch(value as string))
{
return ValidationResult.Success;
}
else
{
return new ValidationResult(FormatErrorMessage(context.DisplayName));
}
}
}
The above code works but building configuration from a file path within a class library does not feel optimal. So I was curious if anyone had any better ideas for how to get a configurable value to the EmailFromRegexValidator?
Thanks!
Welcome! You can use ValidationContext to access a service that provides your configuration value.
protected override ValidationResult IsValid(object value, ValidationContext context)
{
var appConfig = (AppConfiguration) validationContext
.GetService(typeof(AppConfiguration));
string emailRegexString = appConfig.EmailRegex;
...
}
public class AppConfiguration
{
public string EmailRegex { get; set; }
}
You can bind AppConfiguration from the config file using to make it available in your DI container:
services.Configure<AppConfiguration>(configuration.GetSection("AppConfiguration"))

How to set radio button to be checked by default

I'm implementing asp.net core 3.1. I have three radio buttons in my razor view and with the following code, I want to send the selected radio button value to Index action in controller in order to show its related data. My problem is, I can't set one of those radio buttons to be checked by default.
#model CSD.ChartObjects
<form method="post">
#foreach (var year in Model.Years)
{
<input type="radio" asp-for="Year" value="#year" />#year<br />
}
<input type="submit" asp-action="Index" />
</form>
Here is my model object that is read in razor
public class ChartObjects
{
public List<ChartModel> Percent { get; set; }
public List<ChartModel> Time { get; set; }
public List<ChartModel> Avg { get; set; }
public List<ChartModel> Total { get; set; }
public string Year { get; set; }
public string[] Years = new[] { "1398", "1399", "1400" };
}
And here is the body of my HomeController:
[HttpGet]
public IActionResult Index()
{
return (BuildIndexModel("1399"));
}
[HttpPost]
public IActionResult Index([FromForm] string currentYear)
{
return (BuildIndexModel(currentYear));
}
public IActionResult BuildIndexModel(string currentYear)
{
...
}
I think this will work:
#foreach (var year in Model.Years)
{
var fi = (year == Model.Years[0]) ? true : false ;
<input type="radio" asp-for="Year" value="#year" checked="#fi" />#year<br />
}
My problem is, I can't set one of those radio buttons to be checked by default.
To set a default checked radio button, you can try following code snippet.
<form method="post">
#foreach (var year in Model.Years)
{
<input type="radio" asp-for="Year" value="#year" checked="#(year == Model.Years.FirstOrDefault() ? "checked" : null)"/>#year<br />
}
<input type="submit" asp-action="Index" />
</form>
Update:
my data by default is for 1399
You can pass default year through ViewData, like below.
In controller action
ViewData["defaultyear"] = "1399";
In view page
<input type="radio" asp-for="Year" value="#year" checked="#(year == ViewData["defaultyear"].ToString() ? "checked" : null)"/>#year<br />
I don't know how to use asp.net, but In JS, I just simply have to access the attributes of the HTML Input tag so you can then assign the attribute "checked" to true.
I guess is something like this:
HtmlElement Input1 = webBrowser1.Document.GetElementById("ID"); // consider adding an ID
Input1.Attributes.Add("checked", "true");
Check this two links:
How to: Set HTML Attributes for Controls in ASP.NET Web Pages
HtmlDocument.GetElementById(String) Method

Form validation - Field X must be a number

I'm having a problem with the forms validation of asp.net core when using a decimal field in my viewmodel, where the input's value receives a number with a comma but upon the submit it doesn't permit it..
ViewModel:
public class MyViewModel
{
public decimal Price { get; set; }
}
Razor page:
<div class="form-group row">
<label asp-for="Price" req-asterisk="true" class="col-md-3 col-lg-2 col-form-label"></label>
<div class="col-md-9 col-lg-10">
<input asp-for="Price" class="form-control" rows="4" />
<span asp-validation-for="Price" class="text-danger"></span>
</div>
</div>
So, if for example the Price property takes 4000, the input takes 4000,00 and if I click submit it says "The field Price must be a number."
Considering your Price field needs to contain the value, you will need to use a string to access your value. Here is a sample of what you can do with a string property with some memory saving options:
public class MyViewModel
{
private string _priceDisplay;
[DataType(DataType.Currency)]
public string PriceDisplay {
get => _priceDisplay ?? _price.ToString();
set => _priceDisplay = value;
}
private decimal? _price;
public decimal Price
{
get => _price ?? decimal.Parse(PriceDisplay);
set => _price = value;
}
}
You can now map your input to PriceDisplay

get value in input text box and send it to controller

What are the ways to render the input value filename and send it to a controller :
<div id="fileuploaddiv" class="fileuploaddivclass">
<form action="#Model.FormAction" method="#Model.FormMethod"
enctype="#Model.FormEnclosureType">
<input type="hidden" name="key" value="uploads/${filename}" id="filename" />
<input type="hidden" name="AWSAccessKeyId" value="#Model.AWSAccessKey" />
<input type="hidden" name="Content-Type" value="image/jpeg">
<div>
Please specify a file, or a set of files:
<input type="file" name="file" />
</div>
<input type="submit" value="Upload" />
</form>
</div>
You need to look up some MVC3 conventions (I'd recommend NerdDinner as a good starting tutorial), but here is a somewhat similar approach to what you want to do:
#Model YourViewModel
<div id="fileuploaddiv" class="fileuploaddivclass">
#using(Html.BeginForm(Model.FormAction, Model.FormController, FormMethod.Post)
#Html.HiddenFor(model.key => ${fileName})
#Html.HiddenFor(model.AWSAccessKeyID)
#Html.HiddenFor(model.Content-Type)
#<input type="submit" value="Submit My Form" />
#Html.EndForm()
</div>
Your model would look like (And I'm confused here because you seem to be dynamically setting the controller and action, which is unusual):
public class YourViewModel
{
public string FormAction { get; set; }
public string FormController { get; set; }
public int AWSAccessKeyID { get; set; }
public string Content-Type { get; set; }
}
Now on to controllers:
[HttpGet]
public ActionResult WhateverControllerName()
{
YourViewModel yvm = new YourViewModel();
//Initalize viewmodel here
Return view(yvm);
}
[HttpPost]
public ActionResult WhateverControllerName(YourViewModel yvm)
{
if (ModelState.IsValid) {
//Do whatever you want here. Perhaps a redirect?
}
return View(yvm);
}
Note: I am garbage at syntax, so you'll have to check this, but Visual Studio should tell you what works.

MVC3: Portions of Model Not Reconstituted on Postback

Portions of my models are not being correctly reconstructed on postback.
Models
public class DemographicsModel
{
public List<QuestionModel> Questions { get; set; }
}
public abstract class QuestionModel
{
[HiddenInput(DisplayValue = false)]
public int ID { get; set; }
[HiddenInput(DisplayValue = false)]
public string Title { get; set; }
}
public abstract class ChooseQuestionModel : QuestionModel
{
public abstract List<SelectListItem> Items { get; set; }
}
public class ChooseManyQuestionModel : ChooseQuestionModel
{
[Required]
[DataType("CheckBoxList")]
public override List<SelectListItem> Items { get; set; }
}
Views
ChooseManyQuestionModel.cshtml
#model X.Y.Z.ChooseManyQuestionModel
<div class="Form Wide NoLabel">
<div class="Title">#this.Model.Title</div>
#Html.TypeStamp()
#Html.EditorFor(m => m.ID)
#Html.EditorFor(m => m.Title)
#Html.EditorFor(m => m.Items)
</div>
CheckBoxList.cshtml
#model IEnumerable<SelectListItem>
#if (!this.Model.IsNullOrEmpty())
{
foreach (var item in this.Model)
{
<div>
#Html.HiddenFor(m => item.Value)
#Html.HiddenFor(m => item.Text)
#Html.CheckBoxFor(m => item.Selected)
#Html.LabelFor(m => item.Selected, item.Text)
</div>
}
}
I believe the issue lies within CheckBoxList.cshtml since these items are not being re-constituted on postback.
HTML Output
<div class="Form Wide NoLabel">
<div class="Title">Question title displays here?</div>
<input id="Questions_1___xTypeStampx_" name="Questions[1]._xTypeStampx_" type="hidden" value="Hrxh2HjDRorBAZWo18hsC0OvbJwyswpDkfTBfNF2NC8=" />
<input data-val="true" data-val-number="The field ID must be a number." data-val-required="The ID field is required." id="Questions_1__ID" name="Questions[1].ID" type="hidden" value="76" />
<input id="Questions_1__Title" name="Questions[1].Title" type="hidden" value="Question title displays here?" />
<div>
<input id="Questions_1__Items_item_Value" name="Questions[1].Items.item.Value" type="hidden" value="148" />
<input id="Questions_1__Items_item_Text" name="Questions[1].Items.item.Text" type="hidden" value="Organization Type 1" />
<input data-val="true" data-val-required="The Selected field is required." id="Questions_1__Items_item_Selected" name="Questions[1].Items.item.Selected" type="checkbox" value="true" /><input name="Questions[1].Items.item.Selected" type="hidden" value="false" />
<label for="Questions_1__Items_item_Selected">Organization Type 1</label>
</div>
</div>
</div>
Controller
public class AccountController : BaseController
{
public ActionResult Demographics()
{
return this.View(new DemographicsModel());
}
[HttpPost]
public ActionResult Demographics(DemographicsModel model)
{
return this.View(model);
}
}
On postback, the DemographicsModel is populated with the correct types (I'm using MvcContrib to handle abstract type binding). The List<Question> is populated with all of the correct data including the ID and Title of each question from the hidden fields. However, List<SelectListItem> within each question is set to null.
Update 1
The issue is definitely occurring because the fields are not named correctly. For instance, the "item" field names are being generated like this:
Questions_1__Items_item_Value
When they should really look like this (addition of item index and removal of erroneous "item"):
Questions_1__Items_1__Value
Similarly, the field IDs are being generated like this (addition of item index and removal of erroneous "item"):
Questions[1].Items.item.Value
Instead of:
Questions[1].Items[0].Value
Using Fiddler with the correct IDs being posted back, the model is constructed correctly with all radio buttons and checkboxes in place.
Try the following.
In ChooseManyQuestionModel.cshtml, change #Html.EditorFor(m => m.Items) to:
#Html.EditorForModel(m => m.Items)
Then, in CheckBoxList.cshtml, change #model IEnumerable<SelectListItem> to:
#model SelectListItem
Finally, in each item, modify each lambda expression, and change item to m, then remove the foreeach loop. This will allow the Editor to iterate through the collection, and should give you correct id generation for each element.
When foreach loop is used the ids generated in HTML are all same.
When for look is used the ids generated with the for loops index so binding is happening correctly and all the data is available after post back.
In this scenario, it seems the Helper class is not doing what you want it to do. I would suggest writing your own helper class to name your inputs exactly as you require them to be.

Resources