Hi there,
we use tailwind-dark theme in our blazor app.
in SF input fields (like TextBox) changes the border color after successfully changing it's data. This is a very sophisticated behaviour, which is great for users to visualize and note the altered data. After the user edit the form data, (s)he normally want to save it. After saving to db, data will reload from web api.
After data reload (not F5 Browser Reload) it's need to changes border color back to normal, because there the previously altered data is now current and the user need this visual note for confirmation (more subtle as ato show a Message Box "Your data is stored,Dude!").
As we checked the html, we found the css class "e-success" which is responsible for this shiny effect and will set on changed values.
Is there a internal way to set the css classes back to "normal", meaning without the "success" class?
Or do we need write an JScript to delete this css class from all input fields by ourself?
Regards
Stefan
|
<EditForm Model="@annotation" OnValidSubmit="@OnValidSubmit" OnInvalidSubmit="@OnInvalidSubmit">
<DataAnnotationsValidator />
<div class="form-group">
<div>
<label for="Name" class="textbox-label">Name</label>
<SfTextBox @bind-Value="@annotation.Name" Placeholder="Enter a name" CssClass="custom"></SfTextBox>
<ValidationMessage For="@(() => annotation.Name)" />
</div>
</div>
<div class="sfButton">
<SfButton type="submit" IsPrimary="true">Submit</SfButton>
</div>
</EditForm>
<style>
span.custom.e-input-group.e-control-container.e-control-wrapper {
border-color: grey;
}
</style> |
Hi Ponmani,
thanks for your suggestions about custom css classes. Unfortunately this is not the droid i'm searching for.
In your example you show a message on submit and after 2 secs the form will render completely new. This behaviour is just "too much rendering" for a blazor app. It's more common to change only the content/viewmodel instead of the complete form and input fields.
Based on your sample i modified the index page, so you can (hopefully) see exactly what my situation is.
How i could solve the problem.
Regards
Stefan
|
<EditForm Model="@annotation" OnValidSubmit="@OnValidSubmit2" OnInvalidSubmit="@OnInvalidSubmit2">
<DataAnnotationsValidator />
<div class="form-group">
<div>
<label for="Name" class="textbox-label">Name</label>
<SfTextBox @bind-Value="@annotation.Name" Placeholder="Enter a name" CssClass="@CssClass" ValueChange="OnValueChange"></SfTextBox>
<ValidationMessage For="@(() => annotation.Name)" />
</div>
</div>
<div class="form-group">
<div>
<label for="Name2" class="textbox-label">Name</label>
<SfTextBox @bind-Value="@annotation.Name2" Placeholder="Enter a name" CssClass="@CssClass1" ValueChange="OnValueChange1"></SfTextBox>
<ValidationMessage For="@(() => annotation.Name2)" />
</div>
</div>
<div class="sfButton">
<SfButton type="submit" IsPrimary="true">Submit</SfButton>
</div>
</EditForm>
@code {
public string CssClass { get; set; }
public string CssClass1 { get; set; }
async void OnValidSubmit2()
{
Message2 = "Form Submitted totally Successfully!";
this.CssClass = "addCustomClass";
this.CssClass1 = "addCustomClass1";
await Task.Delay(12000);
Message2 = string.Empty;
annotation.Name = null;
annotation.Name2 = null;
StateHasChanged();
}
public void OnValueChange()
{
this.CssClass = "removeCustomClass";
}
public void OnValueChange1()
{
this.CssClass1 = "removeCustomClass1";
}
} |
|
|
Dear Ponmani,
thank you, your suggestion is working and on the surface it is an solution.
Thats because i marked it as an answer.
But underneath its just an overriding of css behaviours. Really no offence, i consider it a little bit kind of "dirty".
My Suggestion is:
A kind of init function or trigger, that removes the css classes "e-success", "e-valid-input" and "modified", which were added on valuechanged event. And it's maybe could be triggered auto by OnValid of Editform ?
maybe its worth for a new extension in SF ?
Based on your answer and my suggestion i build an example with JS Interop to solve it as a " possibly clean" way.
Step 1: Set an id to Editform
<EditForm id="edit2" Model="@annotation" OnValidSubmit="@OnValidSubmit2" ... >
Step 2: create a js file in wwwroot (i.e. blazorUtils.js) with following function
function InitInputClasses(formId)
{
let formElement = document.getElementById(formId);
//get all SF Textboxes within the current HTML-form
let formElement2 = formElement.getElementsByClassName("e-input-group");
if (formElement2 == null) return;
//remove all setted "validation classes" by SF
for (i = 0; i < formElement2.length; i++) {
let el = frm_elements[i];
if (el.classList.contains("modified")) {
el.classList.remove("modified");
}
if (el.classList.contains("e-success")) {
el.classList.remove("e-success");
}
if (el.classList.contains("e-valid-input")) {
el.classList.remove("e-valid-input");
}
}
}
Step 3: update the _Host.cshtml <script src="blazorUtils.js"></script>
Step 4: create a wrapper class for the javascript functions (i.e. BlazorUtils.cs)
using System.Threading.Tasks;
using Microsoft.JSInterop;
namespace Blazor_server_app_5.Data
{
public class BlazorUtils
{
private IJSRuntime _jsRuntime;
public BlazorUtils(IJSRuntime jsRuntime)
{
_jsRuntime = jsRuntime;
}
public async Task InitInputClasses(string formId)
{
if (_jsRuntime == null) return;
await _jsRuntime.InvokeVoidAsync("InitInputClasses", formId);
}
}
}
Step 5: register wrapper-service as Scoped in Startup
services.AddScoped<BlazorUtils>();
Step 6: inject wrapper-service in Page
[Inject] BlazorUtils blazorUtils { get; set; }
Step 7 : call "InitInputClass" in OnValidateSubmit()
async void OnValidSubmit2()
{
Message2 = "Form Submitted totally Successfully!";
await Task.Delay(2000);
Message2 = string.Empty;
annotation.Name = null;
annotation.Name2 = null;
await blazorUtils.InitInputClasses("edit2");
StateHasChanged();
}
It's not perfect and it could be extended and optimized, but it's a first step. Maybe it's an inspiration for more ;-)
I've uploaded my example for your convenience.
Regards
Stefan
The SyncFusion solution is weak sauce. The script solution has issues too. It doesn't reset the initial state that the current value is compared against to see if there is a change, so anything that causes a re-render will put the green box back.
After hacking at it for a while I settled on this...
private void SetEditContext()
{
_editContext = new EditContext(Model);
_editContext.OnFieldChanged += async (o, args) => await EditContext_OnFieldChanged(o, args);
}
private async Task EditContext_OnFieldChanged(object? sender, FieldChangedEventArgs args) { await InvokeAsync(StateHasChanged); }
Then simply call SetEditContext() after the update and/or reload.
Note that I also tried _editContext.MarkAsUnmodified();
but this doesn't work with SyncFusion controls.
In the above example your EditForm would have to be set ...
<EditForm EditContext="@_editContext">
For manual EditForm validation, you need to create a new instance for the model as like in below code snippet.
|
@using Syncfusion.Blazor.Inputs @using System.ComponentModel.DataAnnotations;
<EditForm EditContext="@EC"> <DataAnnotationsValidator /> <ValidationSummary /> <div class="form-group d-flex justify-content-between"> <SfTextBox ID="name" @bind-Value="@model.Name" Placeholder="Name"></SfTextBox> </div> <button type="button" onclick="@SubmitHandler" class="btn btn-primary">Submit</button> </EditForm>
@code { public class ModelClass { [Required] public string Name { get; set; } } private ModelClass model { get; set; } = new ModelClass { Name = "" }; private EditContext EC { get; set; } protected override void OnInitialized() { EC = new EditContext(model); base.OnInitialized(); } private void SubmitHandler() { // manually trigger the validation here bool IsValid = EC.Validate(); if (IsValid) { model = new ModelClass { Name = "" }; EC = new EditContext(model); } } } |
[Video illustration]