Form validation
A form card with a message under each field that needs fixing, a summary that counts them and a Save that only goes through when every field is valid.

- Version
- 1.0.0
- Updated
- Author
- Ynias Bensch
- Tags
Overview
A form in a white card that checks its fields the way people expect: it doesn't open covered in red, it tells them what to fix in words, and Save only goes through when everything is valid.
Each field has an error text right under it, and that text is the rule: it holds the message when the value is wrong and is empty when it is right. Save, the summary at the top and the screen reader label of each input all read those texts, so a rule is written once.
- Required fields stay quiet until someone tries to save. Then the summary at the top says how many fields need fixing ("Fix 2 fields before saving") and each one shows its message.
- Format errors show as soon as someone leaves a field with text that doesn't fit, such as an email address without
@or letters in a phone number.IsMatchwithMatch.Emailchecks the email; the phone uses a short pattern you can change. - Optional fields say so in their label ("Phone (optional)"), so nothing needs an asterisk.
Save stays enabled. A disabled Save button doesn't say what is missing; one that runs and then points at the fields does.
An input with a problem gets a red border (ValidationState), and required inputs tell screen readers so (Required). Fields are checked when people leave them (TriggerOutput.FocusOut), so an email address isn't flagged halfway through typing it. The card grows and shrinks with the messages, so it never shows a scrollbar.
When to use
- Forms built from loose inputs, where you write the
Patchyourself. - Any field with a format: email, phone, postcode, an ID like
INS-1042.
When not to use
- An edit form bound to a table: its cards already show required and type errors, and
SubmitFormchecks them. - Checks that need the server, such as "this email is already used"; do those in the save and show the result with a busy overlay or a notification.
Controls used
| Control | Version |
|---|---|
| GroupContainer | 1.5.0 |
| ModernText | 1.0.0 |
| ModernTextInput | 1.1.2 |
| ModernIcon | 1.1.1 |
| ModernButton | 1.0.0 |
YAML
Works as is, with example data inline. Paste it into a screen or container; the steps are underHow to paste.
Not yet verified in Power Apps Studio. The control versions are drafted from documentation. If Studio flags one, see How to paste for the fix.
- FormValidationRoot:
Control: GroupContainer@1.5.0
Variant: AutoLayout
Properties:
BorderColor: =ColorFade(App.Theme.Colors.Darker70, 88%)
BorderThickness: =1
DropShadow: =DropShadow.Light
Fill: =Color.White
# Hidden children take no space, so the summary only adds height when it shows.
Height: |
=FormValidationHeader.Height + If(FormValidationSummary.Visible, FormValidationSummary.Height + 16, 0) +
FormValidationNameField.Height + FormValidationEmailField.Height + FormValidationPhoneField.Height +
FormValidationFooter.Height + 4 * 16 + 48
LayoutDirection: =LayoutDirection.Vertical
LayoutGap: =16
PaddingBottom: =24
PaddingLeft: =24
PaddingRight: =24
PaddingTop: =24
RadiusBottomLeft: =16
RadiusBottomRight: =16
RadiusTopLeft: =16
RadiusTopRight: =16
Width: =Min(560, Parent.Width)
Children:
- FormValidationHeader:
Control: GroupContainer@1.5.0
Variant: AutoLayout
Properties:
AlignInContainer: =AlignInContainer.Stretch
DropShadow: =DropShadow.None
FillPortions: =0
Height: =50
LayoutDirection: =LayoutDirection.Vertical
LayoutGap: =4
LayoutMinHeight: =50
LayoutMinWidth: =120
Children:
- FormValidationTitle:
Control: ModernText@1.0.0
Properties:
AccessibleLabel: =Self.Text
AlignInContainer: =AlignInContainer.Stretch
FontWeight: =FontWeight.Semibold
Height: =26
LayoutMinHeight: =0
LayoutMinWidth: =80
PaddingBottom: =0
PaddingTop: =0
Size: =18
Text: ="Site contact"
Wrap: =false
- FormValidationDescription:
Control: ModernText@1.0.0
Properties:
AccessibleLabel: =Self.Text
AlignInContainer: =AlignInContainer.Stretch
Color: =ColorFade(App.Theme.Colors.Darker70, 45%)
Height: =20
LayoutMinHeight: =0
LayoutMinWidth: =80
PaddingBottom: =0
PaddingTop: =0
Size: =13
Text: ="Who should the inspector call on the day of the visit?"
Wrap: =false
- FormValidationSummary:
Control: GroupContainer@1.5.0
Variant: AutoLayout
Properties:
AlignInContainer: =AlignInContainer.Stretch
DropShadow: =DropShadow.None
Fill: =ColorFade(FormValidationSummaryIcon.BasePaletteColor, 92%)
FillPortions: =0
Height: =44
LayoutAlignItems: =LayoutAlignItems.Center
LayoutDirection: =LayoutDirection.Horizontal
LayoutGap: =8
LayoutMinHeight: =44
LayoutMinWidth: =120
PaddingLeft: =12
PaddingRight: =12
RadiusBottomLeft: =8
RadiusBottomRight: =8
RadiusTopLeft: =8
RadiusTopRight: =8
Visible: |
=locFormValidationSubmitted &&
Not(IsBlank(FormValidationNameError.Text) && IsBlank(FormValidationEmailError.Text) && IsBlank(FormValidationPhoneError.Text))
Children:
- FormValidationSummaryIcon:
Control: ModernIcon@1.1.1
Properties:
AccessibleLabel: ="Error"
# The error colour; the summary and every message read it from here.
BasePaletteColor: =RGBA(196, 43, 28, 1)
Height: =20
Icon: ="ErrorCircle"
IconColor: =Self.BasePaletteColor
LayoutMinHeight: =20
LayoutMinWidth: =20
Width: =20
- FormValidationSummaryText:
Control: ModernText@1.0.0
Properties:
AccessibleLabel: =Self.Text
Color: =FormValidationSummaryIcon.BasePaletteColor
FillPortions: =1
FontWeight: =FontWeight.Semibold
Height: =30
LayoutMinHeight: =0
LayoutMinWidth: =80
Size: =14
Text: |
=With(
{n: CountRows(Filter([FormValidationNameError.Text, FormValidationEmailError.Text, FormValidationPhoneError.Text], Not(IsBlank(Value))))},
If(n = 1, "Fix 1 field before saving.", "Fix " & n & " fields before saving.")
)
Wrap: =false
- FormValidationNameField:
Control: GroupContainer@1.5.0
Variant: AutoLayout
Properties:
AlignInContainer: =AlignInContainer.Stretch
DropShadow: =DropShadow.None
FillPortions: =0
Height: =FormValidationNameLabel.Height + 4 + FormValidationName.Height + If(FormValidationNameError.Visible, 4 + FormValidationNameError.Height, 0)
LayoutDirection: =LayoutDirection.Vertical
LayoutGap: =4
LayoutMinHeight: =64
LayoutMinWidth: =120
Children:
- FormValidationNameLabel:
Control: ModernText@1.0.0
Properties:
AccessibleLabel: =Self.Text
AlignInContainer: =AlignInContainer.Stretch
FontWeight: =FontWeight.Semibold
Height: =20
LayoutMinHeight: =0
LayoutMinWidth: =60
PaddingBottom: =0
PaddingTop: =0
Size: =13
Text: ="Contact name"
- FormValidationName:
Control: ModernTextInput@1.1.2
Properties:
AccessibleLabel: =FormValidationNameLabel.Text & If(FormValidationNameError.Visible, ", " & FormValidationNameError.Text, "")
AlignInContainer: =AlignInContainer.Stretch
Height: =40
LayoutMinHeight: =40
LayoutMinWidth: =100
Placeholder: ="Sara Peeters"
Size: =14
Required: =true
# Check a field when people leave it, not on every key.
TriggerOutput: =TriggerOutput.FocusOut
ValidationState: =If(FormValidationNameError.Visible, ValidationState.Error, ValidationState.None)
- FormValidationNameError:
Control: ModernText@1.0.0
Properties:
AccessibleLabel: =Self.Text
AlignInContainer: =AlignInContainer.Stretch
AutoHeight: =true
Color: =FormValidationSummaryIcon.BasePaletteColor
Height: =18
LayoutMinHeight: =0
LayoutMinWidth: =60
PaddingBottom: =0
PaddingTop: =0
Size: =12
Text: =If(IsBlank(Trim(FormValidationName.Text)), "Enter the name of the person to call.", "")
# Required: shows after a save attempt. Format errors show as soon as the field has text.
Visible: =Not(IsBlank(Self.Text)) && (locFormValidationSubmitted || Not(IsBlank(FormValidationName.Text)))
- FormValidationEmailField:
Control: GroupContainer@1.5.0
Variant: AutoLayout
Properties:
AlignInContainer: =AlignInContainer.Stretch
DropShadow: =DropShadow.None
FillPortions: =0
Height: =FormValidationEmailLabel.Height + 4 + FormValidationEmail.Height + If(FormValidationEmailError.Visible, 4 + FormValidationEmailError.Height, 0)
LayoutDirection: =LayoutDirection.Vertical
LayoutGap: =4
LayoutMinHeight: =64
LayoutMinWidth: =120
Children:
- FormValidationEmailLabel:
Control: ModernText@1.0.0
Properties:
AccessibleLabel: =Self.Text
AlignInContainer: =AlignInContainer.Stretch
FontWeight: =FontWeight.Semibold
Height: =20
LayoutMinHeight: =0
LayoutMinWidth: =60
PaddingBottom: =0
PaddingTop: =0
Size: =13
Text: ="Email"
- FormValidationEmail:
Control: ModernTextInput@1.1.2
Properties:
AccessibleLabel: =FormValidationEmailLabel.Text & If(FormValidationEmailError.Visible, ", " & FormValidationEmailError.Text, "")
AlignInContainer: =AlignInContainer.Stretch
Height: =40
LayoutMinHeight: =40
LayoutMinWidth: =100
Placeholder: ="sara.peeters@contoso.com"
Size: =14
Required: =true
# Check a field when people leave it, not on every key.
TriggerOutput: =TriggerOutput.FocusOut
ValidationState: =If(FormValidationEmailError.Visible, ValidationState.Error, ValidationState.None)
- FormValidationEmailError:
Control: ModernText@1.0.0
Properties:
AccessibleLabel: =Self.Text
AlignInContainer: =AlignInContainer.Stretch
AutoHeight: =true
Color: =FormValidationSummaryIcon.BasePaletteColor
Height: =18
LayoutMinHeight: =0
LayoutMinWidth: =60
PaddingBottom: =0
PaddingTop: =0
Size: =12
Text: |
=If(
IsBlank(Trim(FormValidationEmail.Text)), "Enter an email address.",
Not(IsMatch(Trim(FormValidationEmail.Text), Match.Email)), "Enter an email address like name@example.com.",
""
)
Visible: =Not(IsBlank(Self.Text)) && (locFormValidationSubmitted || Not(IsBlank(FormValidationEmail.Text)))
- FormValidationPhoneField:
Control: GroupContainer@1.5.0
Variant: AutoLayout
Properties:
AlignInContainer: =AlignInContainer.Stretch
DropShadow: =DropShadow.None
FillPortions: =0
Height: =FormValidationPhoneLabel.Height + 4 + FormValidationPhone.Height + If(FormValidationPhoneError.Visible, 4 + FormValidationPhoneError.Height, 0)
LayoutDirection: =LayoutDirection.Vertical
LayoutGap: =4
LayoutMinHeight: =64
LayoutMinWidth: =120
Children:
- FormValidationPhoneLabel:
Control: ModernText@1.0.0
Properties:
AccessibleLabel: =Self.Text
AlignInContainer: =AlignInContainer.Stretch
FontWeight: =FontWeight.Semibold
Height: =20
LayoutMinHeight: =0
LayoutMinWidth: =60
PaddingBottom: =0
PaddingTop: =0
Size: =13
Text: ="Phone (optional)"
- FormValidationPhone:
Control: ModernTextInput@1.1.2
Properties:
AccessibleLabel: =FormValidationPhoneLabel.Text & If(FormValidationPhoneError.Visible, ", " & FormValidationPhoneError.Text, "")
AlignInContainer: =AlignInContainer.Stretch
Height: =40
LayoutMinHeight: =40
LayoutMinWidth: =100
Placeholder: ="+32 470 12 34 56"
Size: =14
# Check a field when people leave it, not on every key.
TriggerOutput: =TriggerOutput.FocusOut
ValidationState: =If(FormValidationPhoneError.Visible, ValidationState.Error, ValidationState.None)
- FormValidationPhoneError:
Control: ModernText@1.0.0
Properties:
AccessibleLabel: =Self.Text
AlignInContainer: =AlignInContainer.Stretch
AutoHeight: =true
Color: =FormValidationSummaryIcon.BasePaletteColor
Height: =18
LayoutMinHeight: =0
LayoutMinWidth: =60
PaddingBottom: =0
PaddingTop: =0
Size: =12
Text: |
=If(
Not(IsBlank(FormValidationPhone.Text)) && Not(IsMatch(Trim(FormValidationPhone.Text), "\+?[0-9 ]{9,17}")),
"Use digits and spaces only, for example +32 470 12 34 56.",
""
)
Visible: =Not(IsBlank(Self.Text)) && (locFormValidationSubmitted || Not(IsBlank(FormValidationPhone.Text)))
- FormValidationFooter:
Control: GroupContainer@1.5.0
Variant: AutoLayout
Properties:
AlignInContainer: =AlignInContainer.Stretch
DropShadow: =DropShadow.None
FillPortions: =0
Height: =40
LayoutDirection: =LayoutDirection.Horizontal
LayoutGap: =8
LayoutJustifyContent: =LayoutJustifyContent.End
LayoutMinHeight: =40
LayoutMinWidth: =120
Children:
- FormValidationCancel:
Control: ModernButton@1.0.0
Properties:
AccessibleLabel: =Self.Text
Appearance: =ButtonAppearance.Secondary
Height: =40
LayoutMinHeight: =40
LayoutMinWidth: =96
OnSelect: |
=Reset(FormValidationName);
Reset(FormValidationEmail);
Reset(FormValidationPhone);
UpdateContext({locFormValidationSubmitted: false})
Size: =14
Text: ="Cancel"
Width: =96
- FormValidationSave:
Control: ModernButton@1.0.0
Properties:
AccessibleLabel: =Self.Text
Appearance: =ButtonAppearance.Primary
Height: =40
LayoutMinHeight: =40
LayoutMinWidth: =120
OnSelect: |
=UpdateContext({locFormValidationSubmitted: true});
If(
IsBlank(FormValidationNameError.Text) && IsBlank(FormValidationEmailError.Text) && IsBlank(FormValidationPhoneError.Text),
Notify("Saved " & FormValidationName.Text & " (example only).", NotificationType.Success);
Reset(FormValidationName);
Reset(FormValidationEmail);
Reset(FormValidationPhone);
UpdateContext({locFormValidationSubmitted: false})
)
Size: =14
Text: ="Save contact"
Width: =132- FormValidationRoot:
Control: GroupContainer@1.5.0
Variant: AutoLayout
Properties:
BorderColor: =ColorFade(App.Theme.Colors.Darker70, 88%)
BorderThickness: =1
DropShadow: =DropShadow.Light
Fill: =Color.White
# Hidden children take no space, so the summary only adds height when it shows.
Height: |
=FormValidationHeader.Height + If(FormValidationSummary.Visible, FormValidationSummary.Height + 16, 0) +
FormValidationNameField.Height + FormValidationEmailField.Height + FormValidationPhoneField.Height +
FormValidationFooter.Height + 4 * 16 + 48
LayoutDirection: =LayoutDirection.Vertical
LayoutGap: =16
PaddingBottom: =24
PaddingLeft: =24
PaddingRight: =24
PaddingTop: =24
RadiusBottomLeft: =16
RadiusBottomRight: =16
RadiusTopLeft: =16
RadiusTopRight: =16
Width: =Min(560, Parent.Width)
Children:
- FormValidationHeader:
Control: GroupContainer@1.5.0
Variant: AutoLayout
Properties:
AlignInContainer: =AlignInContainer.Stretch
DropShadow: =DropShadow.None
FillPortions: =0
Height: =50
LayoutDirection: =LayoutDirection.Vertical
LayoutGap: =4
LayoutMinHeight: =50
LayoutMinWidth: =120
Children:
- FormValidationTitle:
Control: ModernText@1.0.0
Properties:
AccessibleLabel: =Self.Text
AlignInContainer: =AlignInContainer.Stretch
FontWeight: =FontWeight.Semibold
Height: =26
LayoutMinHeight: =0
LayoutMinWidth: =80
PaddingBottom: =0
PaddingTop: =0
Size: =18
Text: ="Site contact"
Wrap: =false
- FormValidationDescription:
Control: ModernText@1.0.0
Properties:
AccessibleLabel: =Self.Text
AlignInContainer: =AlignInContainer.Stretch
Color: =ColorFade(App.Theme.Colors.Darker70, 45%)
Height: =20
LayoutMinHeight: =0
LayoutMinWidth: =80
PaddingBottom: =0
PaddingTop: =0
Size: =13
Text: ="Who should the inspector call on the day of the visit?"
Wrap: =false
- FormValidationSummary:
Control: GroupContainer@1.5.0
Variant: AutoLayout
Properties:
AlignInContainer: =AlignInContainer.Stretch
DropShadow: =DropShadow.None
Fill: =ColorFade(FormValidationSummaryIcon.BasePaletteColor, 92%)
FillPortions: =0
Height: =44
LayoutAlignItems: =LayoutAlignItems.Center
LayoutDirection: =LayoutDirection.Horizontal
LayoutGap: =8
LayoutMinHeight: =44
LayoutMinWidth: =120
PaddingLeft: =12
PaddingRight: =12
RadiusBottomLeft: =8
RadiusBottomRight: =8
RadiusTopLeft: =8
RadiusTopRight: =8
Visible: |
=locFormValidationSubmitted &&
Not(IsBlank(FormValidationNameError.Text) && IsBlank(FormValidationEmailError.Text) && IsBlank(FormValidationPhoneError.Text))
Children:
- FormValidationSummaryIcon:
Control: ModernIcon@1.1.1
Properties:
AccessibleLabel: ="Error"
# The error colour; the summary and every message read it from here.
BasePaletteColor: =RGBA(196, 43, 28, 1)
Height: =20
Icon: ="ErrorCircle"
IconColor: =Self.BasePaletteColor
LayoutMinHeight: =20
LayoutMinWidth: =20
Width: =20
- FormValidationSummaryText:
Control: ModernText@1.0.0
Properties:
AccessibleLabel: =Self.Text
Color: =FormValidationSummaryIcon.BasePaletteColor
FillPortions: =1
FontWeight: =FontWeight.Semibold
Height: =30
LayoutMinHeight: =0
LayoutMinWidth: =80
Size: =14
Text: |
=With(
{n: CountRows(Filter([FormValidationNameError.Text, FormValidationEmailError.Text, FormValidationPhoneError.Text], Not(IsBlank(Value))))},
If(n = 1, "Fix 1 field before saving.", "Fix " & n & " fields before saving.")
)
Wrap: =false
- FormValidationNameField:
Control: GroupContainer@1.5.0
Variant: AutoLayout
Properties:
AlignInContainer: =AlignInContainer.Stretch
DropShadow: =DropShadow.None
FillPortions: =0
Height: =FormValidationNameLabel.Height + 4 + FormValidationName.Height + If(FormValidationNameError.Visible, 4 + FormValidationNameError.Height, 0)
LayoutDirection: =LayoutDirection.Vertical
LayoutGap: =4
LayoutMinHeight: =64
LayoutMinWidth: =120
Children:
- FormValidationNameLabel:
Control: ModernText@1.0.0
Properties:
AccessibleLabel: =Self.Text
AlignInContainer: =AlignInContainer.Stretch
FontWeight: =FontWeight.Semibold
Height: =20
LayoutMinHeight: =0
LayoutMinWidth: =60
PaddingBottom: =0
PaddingTop: =0
Size: =13
Text: ="Contact name"
- FormValidationName:
Control: ModernTextInput@1.1.2
Properties:
AccessibleLabel: =FormValidationNameLabel.Text & If(FormValidationNameError.Visible, ", " & FormValidationNameError.Text, "")
AlignInContainer: =AlignInContainer.Stretch
Height: =40
LayoutMinHeight: =40
LayoutMinWidth: =100
Placeholder: ="Sara Peeters"
Size: =14
Required: =true
# Check a field when people leave it, not on every key.
TriggerOutput: =TriggerOutput.FocusOut
ValidationState: =If(FormValidationNameError.Visible, ValidationState.Error, ValidationState.None)
- FormValidationNameError:
Control: ModernText@1.0.0
Properties:
AccessibleLabel: =Self.Text
AlignInContainer: =AlignInContainer.Stretch
AutoHeight: =true
Color: =FormValidationSummaryIcon.BasePaletteColor
Height: =18
LayoutMinHeight: =0
LayoutMinWidth: =60
PaddingBottom: =0
PaddingTop: =0
Size: =12
Text: =If(IsBlank(Trim(FormValidationName.Text)), "Enter the name of the person to call.", "")
# Required: shows after a save attempt. Format errors show as soon as the field has text.
Visible: =Not(IsBlank(Self.Text)) && (locFormValidationSubmitted || Not(IsBlank(FormValidationName.Text)))
- FormValidationEmailField:
Control: GroupContainer@1.5.0
Variant: AutoLayout
Properties:
AlignInContainer: =AlignInContainer.Stretch
DropShadow: =DropShadow.None
FillPortions: =0
Height: =FormValidationEmailLabel.Height + 4 + FormValidationEmail.Height + If(FormValidationEmailError.Visible, 4 + FormValidationEmailError.Height, 0)
LayoutDirection: =LayoutDirection.Vertical
LayoutGap: =4
LayoutMinHeight: =64
LayoutMinWidth: =120
Children:
- FormValidationEmailLabel:
Control: ModernText@1.0.0
Properties:
AccessibleLabel: =Self.Text
AlignInContainer: =AlignInContainer.Stretch
FontWeight: =FontWeight.Semibold
Height: =20
LayoutMinHeight: =0
LayoutMinWidth: =60
PaddingBottom: =0
PaddingTop: =0
Size: =13
Text: ="Email"
- FormValidationEmail:
Control: ModernTextInput@1.1.2
Properties:
AccessibleLabel: =FormValidationEmailLabel.Text & If(FormValidationEmailError.Visible, ", " & FormValidationEmailError.Text, "")
AlignInContainer: =AlignInContainer.Stretch
Height: =40
LayoutMinHeight: =40
LayoutMinWidth: =100
Placeholder: ="sara.peeters@contoso.com"
Size: =14
Required: =true
# Check a field when people leave it, not on every key.
TriggerOutput: =TriggerOutput.FocusOut
ValidationState: =If(FormValidationEmailError.Visible, ValidationState.Error, ValidationState.None)
- FormValidationEmailError:
Control: ModernText@1.0.0
Properties:
AccessibleLabel: =Self.Text
AlignInContainer: =AlignInContainer.Stretch
AutoHeight: =true
Color: =FormValidationSummaryIcon.BasePaletteColor
Height: =18
LayoutMinHeight: =0
LayoutMinWidth: =60
PaddingBottom: =0
PaddingTop: =0
Size: =12
Text: |
=If(
IsBlank(Trim(FormValidationEmail.Text)), "Enter an email address.",
Not(IsMatch(Trim(FormValidationEmail.Text), Match.Email)), "Enter an email address like name@example.com.",
""
)
Visible: =Not(IsBlank(Self.Text)) && (locFormValidationSubmitted || Not(IsBlank(FormValidationEmail.Text)))
- FormValidationPhoneField:
Control: GroupContainer@1.5.0
Variant: AutoLayout
Properties:
AlignInContainer: =AlignInContainer.Stretch
DropShadow: =DropShadow.None
FillPortions: =0
Height: =FormValidationPhoneLabel.Height + 4 + FormValidationPhone.Height + If(FormValidationPhoneError.Visible, 4 + FormValidationPhoneError.Height, 0)
LayoutDirection: =LayoutDirection.Vertical
LayoutGap: =4
LayoutMinHeight: =64
LayoutMinWidth: =120
Children:
- FormValidationPhoneLabel:
Control: ModernText@1.0.0
Properties:
AccessibleLabel: =Self.Text
AlignInContainer: =AlignInContainer.Stretch
FontWeight: =FontWeight.Semibold
Height: =20
LayoutMinHeight: =0
LayoutMinWidth: =60
PaddingBottom: =0
PaddingTop: =0
Size: =13
Text: ="Phone (optional)"
- FormValidationPhone:
Control: ModernTextInput@1.1.2
Properties:
AccessibleLabel: =FormValidationPhoneLabel.Text & If(FormValidationPhoneError.Visible, ", " & FormValidationPhoneError.Text, "")
AlignInContainer: =AlignInContainer.Stretch
Height: =40
LayoutMinHeight: =40
LayoutMinWidth: =100
Placeholder: ="+32 470 12 34 56"
Size: =14
# Check a field when people leave it, not on every key.
TriggerOutput: =TriggerOutput.FocusOut
ValidationState: =If(FormValidationPhoneError.Visible, ValidationState.Error, ValidationState.None)
- FormValidationPhoneError:
Control: ModernText@1.0.0
Properties:
AccessibleLabel: =Self.Text
AlignInContainer: =AlignInContainer.Stretch
AutoHeight: =true
Color: =FormValidationSummaryIcon.BasePaletteColor
Height: =18
LayoutMinHeight: =0
LayoutMinWidth: =60
PaddingBottom: =0
PaddingTop: =0
Size: =12
Text: |
=If(
Not(IsBlank(FormValidationPhone.Text)) && Not(IsMatch(Trim(FormValidationPhone.Text), "\+?[0-9 ]{9,17}")),
"Use digits and spaces only, for example +32 470 12 34 56.",
""
)
Visible: =Not(IsBlank(Self.Text)) && (locFormValidationSubmitted || Not(IsBlank(FormValidationPhone.Text)))
- FormValidationFooter:
Control: GroupContainer@1.5.0
Variant: AutoLayout
Properties:
AlignInContainer: =AlignInContainer.Stretch
DropShadow: =DropShadow.None
FillPortions: =0
Height: =40
LayoutDirection: =LayoutDirection.Horizontal
LayoutGap: =8
LayoutJustifyContent: =LayoutJustifyContent.End
LayoutMinHeight: =40
LayoutMinWidth: =120
Children:
- FormValidationCancel:
Control: ModernButton@1.0.0
Properties:
AccessibleLabel: =Self.Text
Appearance: =ButtonAppearance.Secondary
Height: =40
LayoutMinHeight: =40
LayoutMinWidth: =96
OnSelect: |
=Reset(FormValidationName);
Reset(FormValidationEmail);
Reset(FormValidationPhone);
UpdateContext({locFormValidationSubmitted: false})
Size: =14
Text: ="Cancel"
Width: =96
- FormValidationSave:
Control: ModernButton@1.0.0
Properties:
AccessibleLabel: =Self.Text
Appearance: =ButtonAppearance.Primary
Height: =40
LayoutMinHeight: =40
LayoutMinWidth: =120
OnSelect: |
=UpdateContext({locFormValidationSubmitted: true});
If(
IsBlank(FormValidationNameError.Text) && IsBlank(FormValidationEmailError.Text) && IsBlank(FormValidationPhoneError.Text),
Notify("Saved " & FormValidationName.Text & " (example only).", NotificationType.Success);
Reset(FormValidationName);
Reset(FormValidationEmail);
Reset(FormValidationPhone);
UpdateContext({locFormValidationSubmitted: false})
)
Size: =14
Text: ="Save contact"
Width: =132
Make it yours
The example runs on sample data. Replace these properties to connect it to your app.
Text on FormValidationEmailError
The rule for this field, written as the message it shows; an empty text means the value is valid. Every field has an error text like this, and Save, the summary and the field's visibility all read it, so the rule lives in one place.
In the YAML
Text: |
=If(
IsBlank(Trim(FormValidationEmail.Text)), "Enter an email address.",
Not(IsMatch(Trim(FormValidationEmail.Text), Match.Email)), "Enter an email address like name@example.com.",
""
)Your version
Text: |
=If(
IsBlank(Trim(FormValidationEmail.Text)), "Enter an email address.",
Not(EndsWith(Lower(Trim(FormValidationEmail.Text)), "@contoso.com")), "Use a Contoso email address.",
""
)OnSelect on FormValidationSave
Save the values when every error text is empty. The example only shows a notification.
In the YAML
OnSelect: |
=UpdateContext({locFormValidationSubmitted: true});
If(
IsBlank(FormValidationNameError.Text) && IsBlank(FormValidationEmailError.Text) && IsBlank(FormValidationPhoneError.Text),
Notify("Saved " & FormValidationName.Text & " (example only).", NotificationType.Success);
Reset(FormValidationName);
Reset(FormValidationEmail);
Reset(FormValidationPhone);
UpdateContext({locFormValidationSubmitted: false})
)Your version
OnSelect: |
=UpdateContext({locFormValidationSubmitted: true});
If(
IsBlank(FormValidationNameError.Text) && IsBlank(FormValidationEmailError.Text) && IsBlank(FormValidationPhoneError.Text),
IfError(
Patch(Contacts, Defaults(Contacts), {Name: FormValidationName.Text, Email: FormValidationEmail.Text, Phone: FormValidationPhone.Text}),
Notify("The contact wasn't saved. " & FirstError.Message, NotificationType.Error),
Notify("Contact saved", NotificationType.Success);
Reset(FormValidationName);
Reset(FormValidationEmail);
Reset(FormValidationPhone);
UpdateContext({locFormValidationSubmitted: false})
)
)BasePaletteColor on FormValidationSummaryIcon
The error colour. The summary's tint and every error message take it from this icon. It is hard-coded because the theme has no red.
In the YAML
BasePaletteColor: =RGBA(196, 43, 28, 1)Your version
BasePaletteColor: =RGBA(164, 38, 44, 1)How to paste
- Open your app in Power Apps Studio.
- Check that modern controls are on: Settings → Updates → Modern controls and themes. In newer environments they are on by default.
- Copy the YAML with the Copy button above the code.
- In the Tree view, select the screen or container that should hold the component and press Ctrl+V. The whole component arrives as one container,
FormValidationRoot, with all its controls inside. - Work through Make it yours to swap the example data for your own.
Where your version of Studio has a code view for a control or screen, you can paste the same YAML there instead of into the tree view.
When Studio flags a control version
Each control in the YAML names its version, like Control: ModernButton@1.0.0. If that version is newer than what your environment knows, Studio falls back to the version it has or flags the control. All controls of one type in an app must also use the same version, so an app that already has an older ModernButton can reject the paste. To fix it, insert the same control from the Insert pane, copy it as code, note the version on its Control: line, update every matching line in the YAML and paste again.
Missing a component, or did this one not paste cleanly? Suggest a component or report an issue.