using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text.Json; using System.Threading.Tasks; using AntDesign.Forms; using AntDesign.Internal; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Forms; using OneOf; namespace AntDesign { public partial class Form : AntDomComponentBase, IForm { private readonly string _prefixCls = "ant-form"; [Parameter] public string Layout { get; set; } = FormLayout.Horizontal; [Parameter] public RenderFragment ChildContent { get; set; } [Parameter] public ColLayoutParam LabelCol { get; set; } = new ColLayoutParam(); [Parameter] public AntLabelAlignType? LabelAlign { get; set; } [Parameter] public OneOf LabelColSpan { get { return LabelCol.Span; } set { LabelCol.Span = value; } } [Parameter] public OneOf LabelColOffset { get { return LabelCol.Offset; } set { LabelCol.Offset = value; } } [Parameter] public ColLayoutParam WrapperCol { get; set; } = new ColLayoutParam(); [Parameter] public OneOf WrapperColSpan { get { return WrapperCol.Span; } set { WrapperCol.Span = value; } } [Parameter] public OneOf WrapperColOffset { get { return WrapperCol.Offset; } set { WrapperCol.Offset = value; } } [Parameter] public string Size { get; set; } [Parameter] public string Name { get; set; } [Parameter] public TModel Model { get { return _model; } set { if (!(_model?.Equals(value) ?? false)) { var wasNull = _model is null; _model = value; if (!wasNull) BuildEditContext(); } } } [Parameter] public bool Loading { get; set; } [Parameter] public EventCallback OnFinish { get; set; } [Parameter] public EventCallback OnFinishFailed { get; set; } [Parameter] public EventCallback OnFieldChanged { get; set; } [Parameter] public EventCallback OnValidationRequested { get; set; } [Parameter] public EventCallback OnValidationStateChanged { get; set; } [Parameter] public RenderFragment Validator { get; set; } = _defaultValidator; /// /// Enable validation when component values change /// [Parameter] public bool ValidateOnChange { get; set; } [Parameter] public FormValidateMode ValidateMode { get; set; } = FormValidateMode.Default; private static readonly RenderFragment _defaultValidator = builder => { builder.OpenComponent(0); builder.CloseComponent(); }; [Parameter] public FormValidateErrorMessages ValidateMessages { get; set; } [CascadingParameter(Name = "FormProvider")] private IFormProvider FormProvider { get; set; } public bool IsModified => _editContext.IsModified(); private EditContext _editContext; private IList _formItems = new List(); private IList _controls = new List(); private TModel _model; private FormRulesValidator _rulesValidator; ColLayoutParam IForm.WrapperCol => WrapperCol; ColLayoutParam IForm.LabelCol => LabelCol; EditContext IForm.EditContext => _editContext; AntLabelAlignType? IForm.LabelAlign => LabelAlign; string IForm.Size => Size; string IForm.Name => Name; object IForm.Model => Model; bool IForm.ValidateOnChange => ValidateOnChange; bool IForm.IsModified => _editContext.IsModified(); FormValidateMode IForm.ValidateMode => ValidateMode; FormValidateErrorMessages IForm.ValidateMessages => ValidateMessages; public event Action OnFinishEvent; protected override void OnInitialized() { base.OnInitialized(); _editContext = new EditContext(Model); if (FormProvider != null) { FormProvider.AddForm(this); } if (OnFieldChanged.HasDelegate) _editContext.OnFieldChanged += OnFieldChangedHandler; if (OnValidationRequested.HasDelegate) _editContext.OnValidationRequested += OnValidationRequestedHandler; if (OnValidationStateChanged.HasDelegate) _editContext.OnValidationStateChanged += OnValidationStateChangedHandler; if (ValidateMode.IsIn(FormValidateMode.Rules, FormValidateMode.Complex)) { _editContext.OnFieldChanged += RulesModeOnFieldChanged; _editContext.OnValidationRequested += RulesModeOnValidationRequested; } } private void OnFieldChangedHandler(object sender, FieldChangedEventArgs e) => InvokeAsync(() => OnFieldChanged.InvokeAsync(e)); private void OnValidationRequestedHandler(object sender, ValidationRequestedEventArgs e) => InvokeAsync(() => OnValidationRequested.InvokeAsync(e)); private void OnValidationStateChangedHandler(object sender, ValidationStateChangedEventArgs e) => InvokeAsync(() => OnValidationStateChanged.InvokeAsync(e)); protected override void Dispose(bool disposing) { if (OnFieldChanged.HasDelegate) _editContext.OnFieldChanged -= OnFieldChangedHandler; if (OnValidationRequested.HasDelegate) _editContext.OnValidationRequested -= OnValidationRequestedHandler; if (OnValidationStateChanged.HasDelegate) _editContext.OnValidationStateChanged -= OnValidationStateChangedHandler; if (ValidateMode.IsIn(FormValidateMode.Rules, FormValidateMode.Complex)) { _editContext.OnFieldChanged -= RulesModeOnFieldChanged; _editContext.OnValidationRequested -= RulesModeOnValidationRequested; } base.Dispose(disposing); } protected override void OnParametersSet() { base.OnParametersSet(); SetClass(); } protected void SetClass() { this.ClassMapper.Clear() .Add(_prefixCls) .Get(() => $"{_prefixCls}-{Layout.ToLowerInvariant()}") .If($"{_prefixCls}-rtl", () => RTL) ; } private async Task OnValidSubmit(EditContext editContext) { await OnFinish.InvokeAsync(editContext); OnFinishEvent?.Invoke(this); } private async Task OnInvalidSubmit(EditContext editContext) { await OnFinishFailed.InvokeAsync(editContext); } private void RulesModeOnFieldChanged(object sender, FieldChangedEventArgs args) { if (!ValidateMode.IsIn(FormValidateMode.Rules, FormValidateMode.Complex)) { return; } _rulesValidator.ClearError(args.FieldIdentifier); var formItem = _formItems .Single(t => t.GetFieldIdentifier().FieldName == args.FieldIdentifier.FieldName); var result = formItem.ValidateField(); if (result.Length > 0) { var errors = new Dictionary>(); errors[args.FieldIdentifier] = result.Select(r => r.ErrorMessage).ToList(); _rulesValidator.DisplayErrors(errors); } } private void RulesModeOnValidationRequested(object sender, ValidationRequestedEventArgs args) { if (!ValidateMode.IsIn(FormValidateMode.Rules, FormValidateMode.Complex)) { return; } _rulesValidator.ClearErrors(); var errors = new Dictionary>(); foreach (var formItem in _formItems) { var result = formItem.ValidateField(); if (result.Length > 0) { errors[formItem.GetFieldIdentifier()] = result.Select(r => r.ErrorMessage).ToList(); } } _rulesValidator.DisplayErrors(errors); } public void Reset() { _controls.ForEach(item => item.Reset()); BuildEditContext(); } void IForm.AddFormItem(IFormItem formItem) { _formItems.Add(formItem); } void IForm.AddControl(IControlValueAccessor valueAccessor) { this._controls.Add(valueAccessor); } void IForm.RemoveControl(IControlValueAccessor valueAccessor) { if (_controls.Contains(valueAccessor)) { this._controls.Remove(valueAccessor); } } public void Submit() { var isValid = _editContext.Validate(); if (isValid) { if (OnFinish.HasDelegate) { OnFinish.InvokeAsync(_editContext); } OnFinishEvent?.Invoke(this); } else { OnFinishFailed.InvokeAsync(_editContext); } } public bool Validate() => _editContext.Validate(); public void ValidationReset() => BuildEditContext(); public EditContext EditContext => _editContext; public void BuildEditContext() { if (_editContext == null) return; var newContext = new EditContext(Model); foreach (var kv in GetEventInfos()) { FieldInfo fieldInfo = kv.Value.fi; EventInfo eventInfo = kv.Value.ei; Delegate mdel = fieldInfo.GetValue(_editContext) as Delegate; if (mdel != null) { foreach (Delegate del in mdel.GetInvocationList()) { eventInfo.RemoveEventHandler(_editContext, del); eventInfo.AddEventHandler(newContext, del); } } } _editContext = newContext; } static BindingFlags AllBindings { get { return BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; } } static Dictionary _eventInfos; static Dictionary GetEventInfos() { if (_eventInfos is null) { _eventInfos = new(); Type contextType = typeof(EditContext); foreach (EventInfo eventInfo in contextType.GetEvents(AllBindings)) { Type declaringType = eventInfo.DeclaringType; FieldInfo fieldInfo = declaringType.GetField(eventInfo.Name, AllBindings); if (fieldInfo is not null) { _eventInfos.Add(eventInfo.Name, (fieldInfo, eventInfo)); } } } return _eventInfos; } } }