kendo-ui-core
kendo-ui-core copied to clipboard
The CheckBox TagHelper does not render the attribute "required" and a custom class
Bug report
The CheckBox TagHelper does not render the attribute "required" and a custom class.
Reproduction of the problem
The CheckBox TagHelper definition:
<kendo-checkbox id="KendoCheckboxRequiredByHtml" class="form-check-input" required> </kendo-checkbox>
It generates the following markup:
<input id="KendoCheckboxRequiredByHtml" name="KendoCheckboxRequiredByHtml" type="checkbox" value="true" data-role="checkbox" class="k-checkbox k-checkbox-md k-rounded-md"> <input name="KendoCheckboxRequiredByHtml" type="hidden" value="false">
A sample for reproduction is attached.
Expected/desired behavior
The attribute "required" and the class "form-check-input" to be set to the generated input element.
Environment
- **Kendo UI version: 2022.2.510
- **Browser: [all]
Hi Mihaela!
To add some context here: the sample here is based on ASP.NET Core and MVC. There is a model class where some of the properties have the Required data annotation attribute. The MVC and Kendo taghelpers read this information, and create corresponding HTML.
An oddity of the way MVC does things, is that it doesn't output HTML5 required attributes etc. Instead, it outputs it's own data-val-required attribute, and the Kendo Validator has a set of rules designed to find these, and convert them to regular required rules.
From my testing so far, it seems like the MVC and Kendo taghelpers are correctly outputting the data-val-required attributes, and that the Validator is picking up on these, validating them accordingly, and adding corresponding items in its error collection. The issue seems to be the step after that - where submission is blocked, and the errors are shown.
There's another oddity of the MVC taghelper, which is that when rendering the HTML for a checkbox, it actually creates two (one of which is hidden, and stores false values to be posted back to the server). The Kendo taghelper does this too (which is expected).
As a pure HTML checkbox with the required attribute works as intended, and both the MVC and Kendo taghelpers (with their double checkboxes) fail, it seems likely that the problem is a result of the double checkbox output.
To add on: I spent some time digging into this issue. I can confirm that the problem with the MVC tagghelper is as follows:
Per Microsoft - when the MVC taghelper receives a bool, it outputs a corresponding checkbox widget. The rendered HTML contains two inputs:
-
Input 1 has it's id set to the name of the property, name set to the name of the property, type set to "checkbox" and value set to "true". Any validation rule attributes are also set on this input.
-
Input 2 also has it's name set to the name of the property, type set to "hidden", and value set to "false".
When the Validator validates, it iterates through every input in the form, and calls validateInput() on each one. In turn, this calls _checkValidity(), which iterates through the available validation rules and passes the input to each. If the rule returns a response of "Valid", the process continues until all rules are exhausted; if the rule returns a response of "Invalid", the process halts.
validateInput() then responds accordingly. If the input is found to be invalid, then an error message is added to the errors collection, the input is highlighted, and it's validation message is shown. If the input is valid, then any error messages in the errors collection are cleared, the input has it's highlighting cleared, and it's validation message is hidden.
Crucially, these changes are all keyed on the name of the input. This is the source of the problem: MVC outputs two inputs with the same name.
-
Input 1 is validated, and fails a validation rule. The error message in the errors collection is added based on the name of the input; highlighting is applied based on the name; and the validation message is shown based on the name.
-
Input 2 is then validated and as it has no validation criteria, it is automatically found to be valid. Because it shares the same name, the error message just added to the errors collection is removed; highlighting is removed from the first matching input in the form (which is Input 1); and the validation message with the same name is hidden.
This is why the validation works when checking and unchecking the checkbox - because the Validator only checks the triggering input. When submitting the form, all inputs are checked; and this is when the results for Input 2 overwrite those of Input 1.
Interestingly, it appears that this issue has been around for several years. (This is also why, per the comments in that thread, the issue is prevented when Input 2 is moved above Input 1.)
Hi @lmastin , The issue is also reproducible on MVC Form Validation demo and Core Form Validation demo. Although the field is required, client-side and server-side validation errors are not triggered for a not checked checkbox:
[Required]
public bool Agree
I have found that declaring .Hint("some hint") for the boolean field renders a <div> element between the two input elements you mention. And in this way validation works as expected. So indeed the issue seems related to the two inputs and being one after the other. But the hint does not render a required attribute, so this attribute may not be causing the issue.
The main difference between our checkbox tag helper and the ASP.Net tag helper is that we append the hidden input right after the visible one. While the default tag helper appends the hidden input at the end of the output element
- https://github.com/dotnet/aspnetcore/blob/main/src/Mvc/Mvc.TagHelpers/src/InputTagHelper.cs#L340

When the hidden input is not right after the visible element, the validator inspects whether the checkbox is checked to validate it:
- https://github.com/telerik/kendo/blob/master/src/aspnetmvc/kendo.validator.aspnetmvc.js#L153
However, if the hidden input is right after the visible element, we validate it by using the value of the hidden element
- https://github.com/telerik/kendo/blob/master/src/aspnetmvc/kendo.validator.aspnetmvc.js#L151
- https://github.com/telerik/kendo/blob/master/src/aspnetmvc/kendo.validator.aspnetmvc.js#L160
As a result, the validation will always pass. At first glance, I cannot think of a reason to validate the checkbox this way. The user cannot change the value, the only state that is in control of the user is its checked property. I suppose this is a legacy from when the only visible part of the checkbox was the label.
Validating that the checkbox is checked, when it's defined as required makes the most sense.
What do you guys think @zdravkov @Dimitar-Goshev? Should we remove this hidden input value check?
We had to revert the changes as by default boolean fields are required in both aspnet framework and core. Thus whenever CheckoxFor is used for a non-nullable boolean data-val-required is rendered. As a result, even if the property does not have a Required attribute, the validation will expect the checkbox to be checked.
We can suggest implementing a custom validation rule to validate required checkboxes as shown below:
$.extend(true, kendo.ui.validator.rules, {
requiredcheckbox: function (input) {
if (input.is('[type="checkbox"][data-val-required="true"]')) {
return input.is(":checked");
}
return true;
}
});