I'm having an issue where I can get SfDataForm FormItems to be conditionally visible based on the value of a property of the model. Toggling the conditional value "on" works fine, the additional FormItems appear. However, toggling the conditional value "off" causes no change, and then toggling it back "on" causes duplicate FormItems to appear.
Below is some example code and I've attached a test project to demonstrate the problem.
Any suggestions would be greatly appreciated. Thanks.
<SfDataForm ID="MyForm" Model="@RegistrationDetails">
<FormItems>
@foreach (var property in typeof(RegistrationDetails).GetProperties())
{
// Display only the Email and TermsAndConditions fields or all fields if TermsAndConditions is true
if (property.Name == nameof(RegistrationDetails.Email) || property.Name == nameof(RegistrationDetails.TermsAndConditions) || RegistrationDetails.TermsAndConditions)
{
<FormItem Field="@property.Name" ></FormItem>
}
}
</FormItems>
</SfDataForm>
Hi Andrew Moore,
You can toggle the form items based on the Terms and Conditions checkbox using the following code snippet. This code snippet will render the form items dynamically based on whether the Terms and Conditions checkbox is checked (RegistrationDetails1.TermsAndConditions is true) or not. You can refer to the provided code snippet and sample for implementation details.
<SfDataForm ID="MyForm" Model="@RegistrationDetails1"> |
Regards,
Priyanka K
Hi Priyanka,
Thanks so much for your reply and assistance.
Sorry, I probably should have given a bit more context in my original post. I need to use reflection as I'm trying to create a "Dynamic Data Form" component that supports the case where the Model type is not known at compile time. I probably simplified the example code in my original post too much. I've updated the attached code to hopefully better demonstrate what I am trying to achieve. My "Dynamic Data Form" component also provides support for an IPropertyApplicability interface so that class specific rules for property applicability can be defined. I've included this in the attachment also.
I've been experimenting with various workarounds and if I supply a Template for each FormItem the correct form items are visible, except that each time a property is changed which affects the number of visible controls in the data form, additional whitespace is left behind.
i.e. The following HTML elements seems to be left behind in the DOM each time, creating more whitespace.
<div style="gap: 0 ;" class="e-grid-col-1 e-form-layout"><div class="e-colspan-1 e-label-position-top"><!--!--></div></div>
My Razor for my component is the following:
<SfDataForm Model="Model">
<FormItems>
@foreach (var property in Model.GetType().GetProperties())
{
var attribute = property.GetCustomAttribute<BindableAttribute>();
// If the property has a BindableAttribute and it's not bindable, skip this property
if (attribute != null && !attribute.Bindable)
{
continue;
}
// If the model implements IPropertyApplicability and the property is not applicable, skip this property
if (Model is IPropertyApplicability propertyApplicability && !propertyApplicability.IsPropertyApplicable(property))
{
continue;
}
<FormItem Field="@property.Name">
<Template >
<label class="e-form-label">@property.Name</label>
@switch (property.PropertyType)
{
// Create a different input component based on the type of the property
case Type t when t == typeof(bool):
<SfCheckBox TChecked="bool" Checked="(bool)GetPropertyValue(property)" CheckedChanged="@((bool newValue) => SetPropertyValue(property, newValue))"></SfCheckBox>
break;
case Type t when t == typeof(string):
<SfTextBox Value="@((string)GetPropertyValue(property))" ValueChanged="@((string newValue) => SetPropertyValue(property, newValue))"></SfTextBox>
break;
case Type t when t == typeof(int):
<SfNumericTextBox TValue="int" Value="(int)GetPropertyValue(property)" ValueChanged="@((int newValue) => SetPropertyValue(property, newValue))"></SfNumericTextBox>
break;
case Type t when t == typeof(decimal):
<SfNumericTextBox TValue="decimal" Value="(decimal)GetPropertyValue(property)" ValueChanged="@((decimal newValue) => SetPropertyValue(property, newValue))"></SfNumericTextBox>
break;
case Type t when t == typeof(DateTime):
<SfDatePicker TValue="DateTime" Value="(DateTime)GetPropertyValue(property)" ValueChanged="@((DateTime newValue) => SetPropertyValue(property, newValue))"></SfDatePicker>
break;
default:
<SfTextBox Value="@((string)GetPropertyValue(property))" ValueChanged="@((string newValue) => SetPropertyValue(property, newValue))"></SfTextBox>
break;
}
</Template>
</FormItem>
}
</FormItems>
</SfDataForm>
Hi Andrew Moore,
We have considered the reported issue "When we conditionally toggling the formitem, the extra whitespace is added on each time" as a bug from our end and the fix for the issue will be included on upcoming patch release which is scheduled at the end of June 2024.
Now you can track the status of the reported issue through the feedback below,
Feedback link:https://www.syncfusion.com/feedback/58607/when-we-conditionally-toggling-the-formitem-the-extra-whitespace-is-added-on-each
Disclaimer: “Inclusion of this solution in the weekly release may change due to other factors including but not limited to QA checks and works reprioritization.”
Regards,
Priyanka K
Hi Priyanka,
That's great news!
Thanks for your prompt assistance with this issue, it's much appreciated.
Regards,
Andrew
Hi Andrew Moore,
After thorough validation, it was observed that conditionally rendering the editor component alone within a <Template> renderer causes the component to be added and removed from the DOM without its parent elements being removed, leading to unwanted space.
To address this issue, we recommend the following approaches:
Suggestion 1:
In the provided sample, the editor component is rendered conditionally inside <Template>, maintaining the parent element and conditionally removing and rendering the child elements (editor component). To ensure proper functionality of the component, we suggest rendering the entire <FormItem> conditionally, as shown in the code snippet below:
DynamiDataForm.razor
@switch (property.PropertyType) { // Create a different input component based on the type of the property case Type t when t == typeof(bool): <FormItem Field="@property.Name">
<Template> <label class="e-form-label">@property.Name</label>
<SfCheckBox TChecked="bool" Checked="(bool)GetPropertyValue(property)" CheckedChanged="@((bool newValue) => SetPropertyValue(property, newValue))"></SfCheckBox> case Type t when t == typeof(string): <FormItem Field="@property.Name">
<Template> <label class="e-form-label">@property.Name</label>
<SfTextBox Value="@((string)GetPropertyValue(property))" ValueChanged="@((string newValue) => SetPropertyValue(property, newValue))"></SfTextBox> </Template> </FormItem> break; // Rest of the code follows } } |
Suggestion 2:
If you prefer to continue using your method of rendering the editor components, you can use the following CSS styles to hide the empty <div> element, which causes unwanted space when toggling the editor components:
DynamiDataForm.razor
<style> .e-data-form .e-form-layout .e-label-position-top:empty { display: none; } </style>
|
Regards,
Priyanka K