Add a placeholder to InputNumber in blazor - c#

I have a simple question, I would like to add a placeholder to InputNumber component. I tried this code but It didn't work.
//Code behind
public int? Hour { get; set; }
//razor page
<EditForm Model="FilteredEmployees">
<InputNumber #bind-Value="Hour" min="0" class="form-control" max="10" placeholder="Hour"/>
</EditForm>
Thanks for help.

Could be several reasons depending on how you have your code setup. Please try to add more code for a repeatable example, or show the specific error you are getting.
If this input box is displaying 0 instead of "Hour", it's most likely because you are using and int backing field instead of an int? backing field. I just double checked it and having a backing property of
public int? Hour { get; set; }
shows the placeholder text correctly when the textbox content is null.
If you are getting errors (eg null reference errors), it's most likely you are forgetting the EditForm. Please see https://learn.microsoft.com/en-us/aspnet/core/blazor/forms-validation?view=aspnetcore-5.0
As a minimum example:
This works as expected for me, with or without the validation elements
#page "/"
<EditForm Model="#this">
#*<DataAnnotationsValidator />
<ValidationSummary />*#
<InputNumber #bind-Value="Hour" min="0" class="form-control" max="10" placeholder="Hour" />
</EditForm>
#code {
public int? Hour { get; set; }
}

Related

Problem with tag helper and codebehind validation

I have 2 fields ine a model defined like this :
public string FileName { get; set; }
public string Image { get; set; }
In the view they are defined like that in the form :
<input type="hidden" asp-for="Image" />
<input type="hidden" asp-for="FileName" />
All is fine so far, but when I return code behind the ModelState is invalid because these 2 fields are flaged as required (error message in the ModelState.Values list).
Any idea why. So to avoid error I remove them from the model state because they CAN BE null and are not required at all.
Any help welcome.

Blazor set value inside other component

I'm building my page and I was wondering if I can make my life easier and put some simple custom Input boxes inside method and them pass reference to my values to them
<div class="col-12 row">
<label class="col-2">#Caption</label>
<InputNumber class="form-control col-3" #bind-Value="#Value"/>
</div>
<br />
#code {
[Parameter]
public string Caption { get; set; }
[Parameter]
public int Value { get; set; }
}
And then use it like
<CustomInputNumber Caption="Price" Value="#Product.Price" />
It is possible to set value like that? Or pass object as reference? Thanks for help!
The way I would go about this is inheriting from inputbase and basically building your own input. Chrissainty has an excellent blog post about, which I think is much clearer then me citing half of what he already explains in that post.
https://chrissainty.com/building-custom-input-components-for-blazor-using-inputbase/
If however you really want to wrap the already existing inputcomponent, you could do it like this:
The component
<div class="col-12 row">
<label class="col-2">#Caption</label>
<InputNumber class="form-control col-3" Value="Value" TValue="int" ValueChanged="HandleValueChanged" ValueExpression="(()=>Value)" />
</div>
<br />
#code{
[Parameter]
public string Caption { get; set; }
[Parameter]
public int Value { get; set; }
[Parameter]
public EventCallback<int> ValueChanged { get; set; }
public void HandleValueChanged(int newValue)
{
ValueChanged.InvokeAsync(newValue);
}
}
Usage like:
<ExampleComponent #bind-Value="ExampleValue"/>
Here you basically override the existing events that exist on a default inputfield. When the default component notices a change, you call your own valuechanged event to pass the event to it's parent.
Though again, I think the first solution is much cleaner.

ASP.NET Core: Validate Individual Dynamic String Input Fields

The App: It is fed an order #, and the part ID, part #, and quantity for each part are displayed (so 3 input fields per line, per part). All those values appear as input fields that can be modified. In the PageModel, I store all those values in Lists and then iterate through them to find ones that have been modified. If I find modifications, I update the appropriate parts of the database. The app is to be used 100% in-house by people who know what they are doing. Everything I mentioned above is working swimmingly. The functionality is there.
The relevant front-end is this:
#{ int index = 0;}
#foreach (var part in Model.PartInfoList)
{
<tr class="row table-bordered">
<td class="col-4">
<input type="hidden" asp-for="OldPartIDList" value="#part.PART_ID" name="OldPartIDList"/> #*This holds the "old" part ID's so the order can still be looked up when the user wants to change the part ID.*#
<input type="text" class="form-control mb-2 mr-sm-2" asp-for="NewPartIDList" value="#part.PART_ID" id="#part.PART_ID" name="NewPartIDList[#index]" onchange="HighlightField(this)" />
#*The id here is used to determine if the field should be highlighted or not.*#
<span class="text-danger" asp-validation-for="NewPartIDList[index]"></span>
</td>
<td class="col-4">
<input type="text" class="form-control mb-2 mr-sm-2" asp-for="PartNumberList" value="#part.PART_NUMBER" id="#part.PART_NUMBER" name="PartNumberList[#index]" onchange="HighlightField(this)" />
#*The id here is used to determine if the field should be highlighted or not.*#
<span class="text-danger" asp-validation-for="PartNumberList[index]"></span>
</td>
<td class="col-4">
<input type="text" class="form-control mb-2 mr-sm-2" asp-for="QuantityList" value="#part.QUANTITY" id="#part.QUANTITY" name="QuantityList[#index]" onchange="HighlightField(this)" />
#*The id here is used to determine if the field should be highlighted or not.*#
<span class="text-danger" asp-validation-for="QuantityList[index]"></span>
</td>
</tr>
index++;
}
The lists are declared as such:
[DisplayName("Part ID"), BindProperty, Required]
//[Range(1, 999999999, ErrorMessage = "Please enter a number between 1 and 999,999,999.")]
[ResubmitSampleCenterOrderValidatorClass(FieldOptions.PART_ID, 1, 999999999, null)]
public List<int> NewPartIDList { get; set; }
[DisplayName("Part Number"), BindProperty, Required]
//[StringLength(48, MinimumLength = 1, ErrorMessage = "Please enter a part number.")]
//[ResubmitSampleCenterOrderValidatorClass(FieldOptions.PART_NUMBER, 1, 999999999, PartNumberArray: PartNumberList.ToArray())]
public List<string> PartNumberList { get; set; }
[DisplayName("Quantity"), BindProperty, Required]
//[Range(1, 99999, ErrorMessage = "Please enter a number between 1 and 99,999.")]
[ResubmitSampleCenterOrderValidatorClass(FieldOptions.QUANTITY, 1, 99999, null)]
public List<int> QuantityList { get; set; }
The comments are what I've been trying. More on that later.
The Problem: Validation, on the part number input fields specifically. I cannot use
[StringLength(48, MinimumLength = 1, ErrorMessage = "Please enter a part number.")]
because the code will try to convert the string list to a string and that obviously can't happen. I just want to:
1) Make sure each part # cannot exceed 48 characters.
2) Have the error show up below that exact input field and NOT the others (This is actually an issue with the part ID and quantity fields as well, but I care more about the part # fields right now.)
Things I've tried:
As you can see from the comments above, I tried normal validation and it did not work for the above reasons. I then decided to go to the custom validation route:
public class ResubmitSampleCenterOrderValidatorClass: ValidationAttribute
{
public FieldOptions FieldDescriptor { get; set; }
public int MinLength { get; set; }
public int MaxLength { get; set; }
public List<string> PartNumberList { get; set; }
public string[] PartNumberArray { get; set; }
public ResubmitSampleCenterOrderValidatorClass(FieldOptions FieldDescriptor, int MinLength, int MaxLength, string[] PartNumberArray)
{
this.FieldDescriptor = FieldDescriptor;
this.MinLength = MinLength;
this.MaxLength = MaxLength;
this.PartNumberArray = PartNumberArray;
PartNumberList = new List<string>();
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
switch(FieldDescriptor)
{
case FieldOptions.PART_NUMBER:
break;
}
return null;
}
}
FieldOptions is just an enum, of which PART_NUMBER is 1. Anyways, I couldn't find a way to pass the string list into the custom validator because it isn't and cannot be static. I also couldn't find a way to get my numbers out of value for the part ID and quantity, but that's a separate issue.
I also put indexes on the name attributes for my input fields on the front end, hoping that could eventually lead to a solution. It was a trick I used in a JS/jQuery app I wrote a few years ago to do something very similar. Is there a way to access specific elements from the back-end, so I could just iterate through each one of them and validate them myself?
I have googled and googled, but I can't find anyone else with this issue. Maybe I haven't found the magic search words yet. I feel that if my part # validation woes are solved, I can use that solution on the problems I'm having with the other two fields.
All ideas are welcome! Let me know if you need more clarification! Thank you and have a fantastic day!
EDIT: More information on the syntax error I get when I try to pass
public List<string> PartNumberList { get; set; }
to the custom validator class. The class itself is found above. The syntax error reads:
An object reference is required for the non-static field, method, or property 'ReviewOrderModel.PartNumberList'.

Require field if checkbox is checked

I have a field in a form that I need to be required if a checkbox in the same form is checked.
There doesn't seem to be a way to do this with regular model validation, so I created a remote validation method in my controller.
The problem is, since the field isn't required always, the validation doesn't even fire if I put it on the field. So I tried putting the validation on the checkbox, and now I get a different problem where the validation doesn't fire when I add text to the field.
Is there a way to do what I'm needing with custom validation, or do I need to do something in JavaScript? If so, what do I need to do?
Form:
<form>
<input type="checkbox" asp-for="NotRecommended" checked=#Model.NotRecommended /> <label>Not Recommended</label>
<textarea class="form-control" id="Notes" asp-for="Notes"></textarea>
<span asp-validation-for="NotRecommended" class="text-danger"></span>
</form>
Model:
public class DetailsViewModel
{
[DisplayName("Not Recommended")]
[Remote("RequireNoteForFlag", AdditionalFields = "Notes", ErrorMessage = "Note is required when flagging someone.")]
public bool NotRecommended { get; set; }
[DataType(DataType.MultilineText)]
[MaxLength(1500)]
[DisplayName("Notes")]
public string Notes { get; set; }
}
Remote Validator
public IActionResult RequireNoteForFlag(bool NotRecommended, string Notes)
{
if (NotRecommended && String.IsNullOrWhiteSpace(Notes)) return Json("Note is required when flagging an Assessor");
else return Json(true);
}
Joe gave me pretty much the whole answer in the comments, but hasn't posted it as an answer, so I'll post it for anyone else who might need to do this.
Create the Attribute
https://github.com/joeaudette/cloudscribe/blob/master/src/cloudscribe.Web.Common/DataAnnotations/RequiredWhenAttribute.cs
Joe's Client-Side Validation
https://github.com/cloudscribe/cloudscribe/blob/master/src/cloudscribe.Web.StaticFiles/js/cloudscribe-validation-requiredwhen.js
Modified Client-Side Validation
jQuery.validator.addMethod("requiredwhen", function (value, element, param) {
var otherPropId = $(element).data('val-other');
if (otherPropId) {
var otherProp = $(otherPropId)[0].checked;
if (otherProp) {
return element.value.length > 0;
}
}
return true;
});
jQuery.validator.unobtrusive.adapters.addBool("requiredwhen");
View
<input type="checkbox" asp-for="NotRecommended" /> <label asp-for="NotRecommended"></label>
<textarea class="form-control" id="Notes" asp-for="Notes"></textarea>
<span asp-validation-for="NotRecommended" class="text-danger"></span>
Model
[DisplayName("Not Recommended")]
public bool NotRecommended { get; set; }
[DataType(DataType.MultilineText)]
[MaxLength(1500)]
[DisplayName("Notes")]
[RequiredWhen("NotRecommended", true, AllowEmptyStrings = false, ErrorMessage ="A note is required when flagging an Assessor.")]
public string Notes { get; set; }
I have implemented this with a custom validator in a similar way. However I applied the annotation to the field that would be required and included a condition in the arguments, eg:
[RequiredIf("NotRecommended = true")]
public string Notes { get; set; }
But, as you may already be aware, there is no way to cross-reference another property in data annotations so you will need to build in the javascript to handle this.
Unobtrusive validation adds a data-val="true" and data-val-required="error message" attribute to the input element so adding this in via javascript when the checkbox is checked will work.
You will also need to reinitialise the form validation so that the inputs with newly applied validation attributes are added.

Obtaining list of objects from client side in asp.net core 2.0

I'm new to asp.net core. I've been reading and I know now how to submit data on a form as an object to the code behind, through post methods. That's fine. But how about sending a list of objects? What I would need to do would be (in this example it would be for an hotel management room types and rate types) something like a grid where I have a list of room types, where each one has a list of rate types. How can I do this? as I've done it, I can pass through the name of the room type (only as one object, though) but not any rates.
I have it like this (models):
public class RateType
{
public string Name { get; set; }
public bool IsActive { get; set; }
}
public class RoomType
{
public string Name { get; set; }
public List<RateType> Rates { get; set; } = new List<RateType>();
}
In the HomeController:
public IActionResult RoomConfig()
{
return View(room);
}
[HttpGet]
public ViewResult SaveRoomConfig()
{
return View("RoomConfig");
}
[HttpPost]
public ViewResult SaveRoomConfig(RoomType room)
{
return View("RoomConfig");
}
And in the view:
#model RoomType
#{
ViewData["Title"] = "RoomConfig";
}
<h2>#Model.Name</h2>
<form class="p-a-1" asp-action="SaveRoomConfig" method="post">
#foreach (var item in Model.Rates)
{
<label asp-for="Name">#item.Name </label>
<input class="form-control" asp-for="Name" />
<input asp-for="#item.IsActive" type="checkbox" />
}
<button type="submit">Send</button>
</form>
So, I send an initial object with some pre-defined values just for testing and they are shown on the page. But then, when I click "send" I can only send the first object (makes sense, since I only have an object parameter, so it will send only the first one. Still, I tried putting a list of RoomType's to see what it did but it shows count as zero. So, how can I do it? Pass a list of objects with another list of objects (nested). Is this possible? It doesn't make sense to make to only have the possibility to pass one and only single object.
Ok, I realized why it wasn't working. I was doing everything right, except for the fact that I was binding items to an empty list which, by definition, didn't have any items. So, instead of a 'foreach', I wrote a 'for' statement, in order to get to the index. That way, I binded the object to the position on the list, instead of a non-existing object. Like this:
#model RoomType
#{
ViewData["Title"] = "RoomConfig";
}
<form class="p-a-1" asp-action="SaveRoomConfig" method="post">
<input asp-for="Name" />
#for (int i=0;i< Model.Rates.Count;i++)
{
<div class=" form-control">
<label asp-for="#Model.Rates[i].Name">#Model.Rates[i].Name </label>
<input asp-for="#Model.Rates[i].Name" />
<input asp-for="#Model.Rates[i].IsActive" type="checkbox" />
</div>
}
<button type="submit">Send</button>
</form>
Of course, if anyone has a better way to do this I'm all ears. :)

Categories

Resources