/// <summary> /// Validates form inputs. /// </summary> /// <param name="focusedControl">currently focused form control</param> /// <returns>true if the form is valid</returns> bool ValidateForm(FormControl focusedControl = null) { var values = new Dictionary <string, string>(); var errors = new Dictionary <string, string>(); foreach (var control in formControls) { if (control.InputField != null) { values.Add(control.Name, control.InputField.text); errors.Add(control.Name, null); } } foreach (var globalValidator in globalValidators) { globalValidator(values, errors); } foreach (var control in formControls) { if (control.InputField == null) { continue; } foreach (var validator in control.Validators) { string errorMessage = validator(values[control.Name]); if (errorMessage != null) { if (focusedControl == control || (focusedControl == null && errors[control.Name] == null)) { errors[control.Name] = errorMessage; } else if (focusedControl != null) { errors[control.Name] = ""; } break; } } } UpdateErrorPanel(errors, focusedControl); foreach (var rec in errors) { if (rec.Value != null) { return(false); } } return(true); }
/// <summary> /// Shows errors in form. /// </summary> /// <param name="errors">mapping of control names to errors</param> /// <param name="focusedControl">currently focused form control</param> private void UpdateErrorPanel(Dictionary <string, string> errors, FormControl focusedControl = null) { if (errorPanel == null) { errorPanel = (GameObject)GameObject.Instantiate(Resources.Load("Prefabs/FormErrorPanel")); errorTextComponent = errorPanel.GetComponentInChildren <Text>(true); errorPanel.SetActive(false); } // hide error panel at the start, show only when error is found errorPanel.SetActive(false); foreach (var control in formControls) { if (control.InputField == null) { continue; } string error = errors[control.Name]; var inputField = control.InputField; if (error == null || !control.Touched) { inputField.image.color = Color.white; } // do not show errors on controls that haven't been touched yet else if (control.Touched) { inputField.image.color = Color.red; // show error panel if there's an error with text // error panel is shown only only for one error - error from currently focused control // takes priority, if there's no such error we show panel for first error with text if (error != "" && (!errorPanel.activeInHierarchy || control == focusedControl) ) { errorPanel.SetActive(true); errorTextComponent.text = error; errorTextComponent.fontSize = errorFontSize > 0 ? errorFontSize : inputField.GetComponentInChildren <Text>().fontSize; var errorRt = errorPanel.GetComponent <RectTransform>(); var fieldRt = inputField.GetComponent <RectTransform>(); // copy rect transform errorRt.SetParent(fieldRt.parent); errorRt.anchorMin = fieldRt.anchorMin; errorRt.anchorMax = fieldRt.anchorMax; errorRt.pivot = fieldRt.pivot; errorRt.localScale = fieldRt.localScale; if (fieldRt.localPosition.x < 100) { errorRt.anchoredPosition = fieldRt.anchoredPosition + new Vector2(5 + fieldRt.rect.width, 0); } else { errorRt.anchoredPosition = fieldRt.anchoredPosition + new Vector2(-5 - fieldRt.rect.width, 0); } var newLines = error.Count(c => c == '\n'); errorRt.sizeDelta = new Vector2(fieldRt.sizeDelta.x, fieldRt.sizeDelta.y * (newLines + 1)); } } } }