Skip to content

Instantly share code, notes, and snippets.

@SteveSandersonMS
Created September 21, 2018 16:29
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save SteveSandersonMS/6ac9458e3fc5b49c199777627fff3fc8 to your computer and use it in GitHub Desktop.
Save SteveSandersonMS/6ac9458e3fc5b49c199777627fff3fc8 to your computer and use it in GitHub Desktop.
Validation mockup A: explicit <ValidateXyz> components that take a Func<T>
@* Unfortunately this has to be named BlazorForm, not Form, because we can't differentiate based on casing *@
<form onsubmit=@HandleSubmit>
@ChildContent(_context)
</form>
@functions {
private FormContext _context = new FormContext();
[Parameter] protected RenderFragment<FormContext> ChildContent { get; set; }
[Parameter] protected Action<FormContext> OnSubmit { get; set; }
public class FormContext
{
private List<Func<bool>> _validations = new List<Func<bool>>();
public void AddValidation(Func<bool> validation)
{
_validations.Add(validation);
}
public bool Validate()
{
var isValid = true;
foreach (var validation in _validations)
{
isValid &= validation();
}
return isValid;
}
}
public class ValidationGroup
{
private List<Func<bool>> _lastResultAccessors = new List<Func<bool>>();
public bool Valid
{
get => _lastResultAccessors.All(x => x());
}
public void AddValidation(Func<bool> lastResultAccessor)
{
_lastResultAccessors.Add(lastResultAccessor);
}
}
void HandleSubmit()
{
if (_context.Validate())
{
OnSubmit?.Invoke(_context);
}
}
}
@page "/"
<h1>Hello, world!</h1>
Welcome to your new app.
@* Would be good if we could put Content="form" here so all "context" below is replaced by "form" *@
<BlazorForm OnSubmit=@HandleSubmit>
@*
If we implemented the ability to share data with descendants as in #1 (like React's 'context') then we could do this,
i.e., could eliminate the Form and Value params:
<InputText bind=@email>
<ValidateRequired />
<ValidateEmail />
</InputText>
*@
<p>
@* Example of making use of validation group status *@
<input bind=@firstName style="background-color: @(firstNameValidations.Valid ? "" : "#ffcccc")" />
@* Specifying Value as a Func<T>, not T, is needed to be independent of render order *@
<ValidateRequired Form=context Value=@(() => firstName)>Type a first name</ValidateRequired>
</p>
<p>
@* Can work with arbitrary inputs, as we're validating a Func<T>, not a UI element *@
<input bind=@email />
<ValidateRequired Form=context Value=@(() => email) />
</p>
<button type="submit">Submit</button>
@** Can control where the validation messages get displayed, have custom rules, put them in groups *@
<ValidateCustom Group=@firstNameValidations Form=context Rule=@(() => !string.IsNullOrWhiteSpace(firstName))>First name is required</ValidateCustom>
<ValidateCustom Group=@firstNameValidations Form=context Rule=@(() => firstName != null && firstName.Length < 5)>First name is too long</ValidateCustom>
</BlazorForm>
<pre>@log</pre>
@functions {
BlazorForm.ValidationGroup firstNameValidations = new BlazorForm.ValidationGroup();
string log = "";
string firstName = "Bert";
string email = "email@example.com";
void HandleSubmit(BlazorForm.FormContext context)
{
log += "\nSubmitted for " + firstName;
StateHasChanged(); // Only needed for the log
}
}
@if (!isValid)
{
<span style="color:red">
@* Use child content as message if specified, otherwise use default *@
@if (ChildContent != null)
{
@ChildContent
}
else
{
<text>The valid is not valid</text>
}
</span>
}
@functions {
[Parameter] protected BlazorForm.FormContext Form { get; set; }
[Parameter] protected BlazorForm.ValidationGroup Group { get; set; }
[Parameter] protected Func<bool> Rule { get; set; }
[Parameter] protected RenderFragment ChildContent { get; set; }
private bool isValid = true;
protected override void OnInit()
{
Form.AddValidation(() =>
{
if (Rule() != isValid)
{
isValid = !isValid;
StateHasChanged();
}
return isValid;
});
Group?.AddValidation(() => isValid);
}
}
@* Example of specializing ValidateCustom with a pre-implemented rule *@
<ValidateCustom Form=@Form Group=@Group Rule=@Rule ChildContent=@(ChildContent ?? DefaultMessage) />
@functions {
[Parameter] protected BlazorForm.FormContext Form { get; set; }
[Parameter] protected BlazorForm.ValidationGroup Group { get; set; }
[Parameter] protected RenderFragment ChildContent { get; set; }
[Parameter] protected Func<string> Value { get; set; }
private void DefaultMessage(Microsoft.AspNetCore.Blazor.RenderTree.RenderTreeBuilder builder)
{
builder.AddContent(0, "A value is required");
}
private bool Rule()
=> !string.IsNullOrWhiteSpace(Value());
}
@hishamco
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment