How can I get the selected item from a Telerik UI for ASP.NET Core DropDownList - telerik

I'm using a kendo-dropdownlist tag helper from the Telerik UI for ASP.NET Core library. So far I have been able to bind the values that can be selected, but I can't figure out how to get the selected item when a post request is sent.
I have a login form:
#page
#model PITS.Areas.Authentication.Pages.Login2Model
#{
}
<form method="post">
<input class="form-control k-textbox" asp-for="UserName" type="text" />
<input class="form-control k-textbox" asp-for="Password" type="password" />
<kendo-dropdownlist name="administraties"
filter="FilterType.Contains"
placeholder="Selecteer Administratie"
style="width: 100%;"
bind-to="Model.Organizations">
</kendo-dropdownlist>
<input type="submit" class="btn btn-primary pull-right" value="Login">
</form>
and a PageModel
public class Login2Model : PageModel
{
[BindProperty]
public string UserName { get; set; }
[BindProperty]
public string Password { get; set; }
[BindProperty]
public IEnumerable<SelectListItem> Organizations { get; set; }
public void OnGet()
{
this.Organizations = _getOrganizations();
}
private IList<SelectListItem> _getOrganizations()
{
return new List<SelectListItem>
{
new SelectListItem {Value = Guid.NewGuid().ToString(), Text = "Google"},
new SelectListItem {Value = Guid.NewGuid().ToString(), Text = "Apple"},
new SelectListItem {Value = Guid.NewGuid().ToString(), Text = "Microsoft"}
};
}
}
I would expect an attribute on the kendo-dropdownlist taghelper but I haven't found it yet. Could someone tell me how to get the selected item?

This assumes you are using Razor.
Use the "for" property to bind the kendo-dropdownlist to a page model property.
for="MySelection"
Then in your page model.
public string MySelection { get; set; }

Related

Display validation error message - compare one input against another

Entered Max value must be greater than Min. Right now my code displays error message when Max is same as Min (using compare). Is there validator that can be used to compare one input against another?
MyData.cs:
public class MyData
{
[Required]
public double Min { get; set; }
[Compare("Min", ErrorMessage = "checks for matching min value")]
public double Max { get; set; }
}
Form.razor:
<div class="modal-body">
<EditForm EditContext="#context">
<DataAnnotationsValidator />
<label class="form-label" for="Min">Min</label>
<input class="form-control" #bind=model.Min type="text">
<label class="form-label" for="Max">Max</label>
<input class="form-control" #bind=model.Max type="text">
<ValidationMessage For="#(() => model.Max)" />
</EditForm>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" #onclick="() => Done()">Apply</button>
</div>
#code {
private MyData model = new MyData();
private EditContext context;
protected override void OnInitialized()
{
model = (MyData)(modalDialog?.ModalRequest.InData ?? new MyData());
context = new EditContext(model);
}
private void Done()
{
if (#model.Max < #model.Min)
{
context.Validate(); #*this displays error message*#
}
else
{
modalDialog?.Close(ModalResult.OK(model));
}
}
To validate for greater than or less than against another property instead of a value using Data Annotations you require to create a custom validation attribute as shown below:
GreaterThan attribute
// Custom attribute for validating greater than other property
public class GreaterThan : ValidationAttribute
{
private readonly string _comparisonProperty;
public GreaterThan(string comparisonProperty)
{
_comparisonProperty = comparisonProperty;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
ErrorMessage = ErrorMessageString;
var currentValue = (double)value; // cast to double same as property type
var property = validationContext.ObjectType.GetProperty(_comparisonProperty);
if (property == null)
throw new ArgumentException("Property with this name not found");
var comparisonValue = (double)property.GetValue(validationContext.ObjectInstance); // cast to property type
// comparison condition
if (currentValue < comparisonValue)
return new ValidationResult(ErrorMessage);
return ValidationResult.Success;
}
}
LessThan attribute
You can use the same code above to create for LessThan attribute by changing the name and comparison condition to currentValue > comparisonValue.
Below is an example on how to use Data Annotations to validate your model and display validation errors in the form. It includes GreaterThan custom validation attribute together with other common validation attribute.
Demo
Class:
public class MyData
{
[Required]
[MaxLength(40, ErrorMessage = "Name should be less than 40 characters")]
[MinLength(4, ErrorMessage ="Name should be more than 4 characters")]
public string Name { get; set; }
[Required]
[DataType(DataType.Date)]
public DateTime? BirthDate { get; set; }
[Required]
public double Min { get; set; }
[GreaterThan("Min", ErrorMessage = "Max must be greater than Min")]
public double Max { get; set; }
[Required(ErrorMessage = "Password is required.")]
public string Password { get; set; }
[Required(ErrorMessage = "Confirmation Password is required.")]
[Compare("Password", ErrorMessage = "Password and Confirmation Password must match.")]
public string ConfirmPassword { get; set; }
}
Razor:
Note: you can upgrade the styling for your form fields and validation message to your liking.
#page "/"
#using BlazorApp1.Models
<EditForm Model="#myData" OnValidSubmit="#HandleValidSubmit">
<DataAnnotationsValidator/>
<ValidationSummary/>
<p>
<label for="Name">Name: </label>
<InputText id="Name" #bind-Value="myData.Name"/>
<ValidationMessage For="() => myData.Name"/>
</p>
<p>
<label for="Min">Min: </label>
<InputNumber id="Min" #bind-Value="myData.Min"/>
<ValidationMessage For="() => myData.Min"/>
</p>
<p>
<label for="Max">Max: </label>
<InputNumber id="Max" #bind-Value="myData.Max"/>
<ValidationMessage For="() => myData.Max"/>
</p>
<p>
<label for="BirthDate">BirthDate: </label>
<InputDate id="BirthDate" #bind-Value="myData.BirthDate"/>
<ValidationMessage For="() => myData.BirthDate"/>
</p>
<p>
<label for="Password">Password: </label>
<InputText id="Password" #bind-Value="myData.Password"
type="password"/>
<ValidationMessage For="() => myData.Password"/>
</p>
<p>
<label for="ConfirmPassword">ConfirmPassword: </label>
<InputText id="ConfirmPassword" #bind-Value="myData.ConfirmPassword"
type="password"/>
<ValidationMessage For="() => myData.ConfirmPassword"/>
</p>
<button type="submit">Submit</button>
</EditForm>
#code {
private readonly MyData myData = new();
private void HandleValidSubmit()
{
// Save the data
}
}
Output:

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

How to use CheckBoxFor in MVC forms with other form controls

Basically, i have a form with a textbox, radio button and a check box control. now i face problem with the checkbox control when i submit my page
I have a model like this
public class PersonDetails
{
public int personID { get; set; }
public string PersonName { get; set; }
public string Gender { get; set; }
public List<Education> Education { get; set; }
public string EmailID { get; set; }
public string Address { get; set; }
}
public class Education
{
public string Qualification { get; set; }
public bool Checked { get; set; }
public List<Education> GetQualification()
{
return new List<Education>{
new Education {Qualification="SSC",Checked=false},
new Education {Qualification="HSC",Checked=false},
new Education {Qualification="Graduation",Checked=false},
new Education {Qualification="PostGraduation",Checked=false}
};
}
}
and i have a view like this
#using (Html.BeginForm("GetDetails", "User", FormMethod.Post, new { id = "person-form" }))
{
<div class="col-xs-12">
<label>Person Name</label>
#Html.TextBoxFor(x => x.PersonName)
</div>
<div class="col-xs-12">
<label>Gender</label>
#Html.RadioButtonFor(x => x.Gender, "Male")
#Html.RadioButtonFor(x => x.Gender, "Female")
</div>
<div class="col-xs-12">
<label>Education</label>
#{
Html.RenderPartial("Qualification", new LearnAuthentication.Controllers.Education().GetQualification());
}
</div>
<div class="col-xs-12">
<input type="submit" value="Submit" />
</div>
}
and the partial view like this
#model List<LearnAuthentication.Controllers.Education>
<br />
#for (int i = 0; i < Model.Count(); i++)
{
#Html.HiddenFor(x => Model[i].Qualification)
#Html.CheckBoxFor(x => Model[i].Checked)
#Html.DisplayFor(x => Model[i].Qualification)
<br />
}
and my action method is this
[HttpPost]
public ActionResult GetDetails(PersonDetails personDetails)
{
return View();
}
now when i run my app i tend to get all the information but when i submit the page i get this property with null values
public List Education { get; set; }
can any of you guys help me on what i am doing wrong or could you direct me to the right path on how to achieve this.
Your use of a partial to generate the controls for Education is generating inputs such as
<input type="hidden" name="[0].Qualification" ... />
<input type="hidden" name="[1].Qualification" ... />
but in order to bind, they need to have name attributes which match your model
<input type="hidden" name="Education[0].Qualification" ... />
<input type="hidden" name="Education[1].Qualification" ... />
Rename you partial to Education.cshtml (to match the name of the class) and move it to your /Views/Shared/EditorTemplates folder (or /Views/yourControllerName/EditorTemplates if you want a specific template just for that controller)
Then change the partial to
#model LearnAuthentication.Controllers.Education
#Html.HiddenFor(m => m.Qualification)
#Html.LabelFor(m => m.Checked)
#Html.CheckBoxFor(m => m.Checked)
#Html.DisplayFor(m => m.Qualification)
and in the main view replace
<label>Education</label>
#{ Html.RenderPartial("Qualification", new LearnAuthentication.Controllers.Education().GetQualification()); }
with
<span>Education</span> // its not a label
#Html.EditorFor(m => m.Education)
which will correctly generate the correct html for each item in your collection
Side note: Other alternatives which would work would be to change the POST method signature to
[HttpPost]
public ActionResult GetDetails(PersonDetails personDetails List<Education> educationDetails)
or to pass the HtmlFieldPrefix to the partial as explained in getting the values from a nested complex object that is passed to a partial view

mvc3 checkbox value after submit

I have a form with 2 fields a dropdownlist and a checkbox. I have everything working correctly but i can not for some reason obtain the value of a checkbox if it is checked this is my code..
[HttpPost]
public ActionResult view(string pick)
{
switch (pick)
{
case "Deny":
// capture checkbox value here
break;
case "Accept":
// capture checkbox value here
break;
}
return View();
}
This is my view
#using (Html.BeginForm("view", "grouprequest", FormMethod.Post, new {}))
{
#Html.DropDownList("pick", new SelectList(new List<Object>{
new{ Text ="Accept", Value= "Accept"},new{ Text ="Deny", Value= "Deny"}}, "Value", "Text"), new {})
<input type="submit" name="work" id="work" value="Update" style="font-size:16px" />
foreach (var item in Model)
{
<input type="checkbox" id="#item.grouprequestID" name="#item.grouprequestID" value="#item.grouprequestID" />
}
}
Basically the dropdownlist has 2 options which are Accept and Deny I can capture which one the user chooses via the SWITCH-case in the controller now how can I capture the value of the checkboxes? If you notice the Checkboxes have a variable to them named #groupRequestID so every checkbox has a different unique value like 1,2,3 etc.. any help would be greatly appreciated !!
The Model
public class grouprequest
{
[Key]
public int grouprequestID { get; set; }
public int? profileID { get; set; }
public int? registrationID { get; set; }
public DateTime expires { get; set; }
public int? Grouplink { get; set; }
}
Check boxes when posted to the server act a little strange.
If a box is checked the browser will send name=value as in
<input type="checkbox" name="name" value="value" />
But if the checkbox is not checked the server doesn't send anything.
<input type="checkbox" name="Check1" id="Checks1" value="Hello" checked="checked"/>
<input type="checkbox" name="Check1" id="Checks1" value="Hello1" />
<input type="checkbox" name="Check1" id="Checks1" value="Hello2" />
Will result in Check1 = Hello
What this means is if all your check boxes are related, naming them the same will populate the same attribute of your ActionMethod. If that attribute is an enumeration it will contain only the ones that are checked.
If you have this in your view:
<input type="checkbox" name="MyValues" value="1" checked="checked"/>
<input type="checkbox" name="MyValues" value="2" />
<input type="checkbox" name="MyValues" value="3" />
and this as your controller action method:
public ActionMethod MyAction(IEumerable<int> myValues)
The myValues variable will look like this:
myValues[0] == 1
You should also note that if you are using the Html helper extension:
#Html.CheckBoxFor(m => m.MyValue)
Where MyValue is a bool the extension will create a checkbox input tag and also a hidden input tag with the same name, meaning a value will always be passed into the controller method.
Hope this helps.

Resources