This Question is Answered

14 "helpful" answers available (3 pts)
1 2 Previous Next
16 Replies Last post: May 14, 2008 6:28 AM by fukuta

Question about validation popup dialog

May 8, 2008 2:04 AM

Click to view fukuta's profile Level 6 fukuta 74 posts since
Oct 17, 2007
Hello,

I made a custom Validator and am using it on my applet.
My validator does something heavy calculation on its 'validate-value' method.
For example;

{define-class package MyValidator {inherits Validator}
{method public open {validate-value
controller:ValidationController,
target:ValueControl,
partial?:bool
}:#Visual
{sleep .1s} || something heavy calculation
{return {message my error!}}
}
}


I attach this validator with a TextField and put it with a CommandButton that shows a popup dialog.


{Dialog
{spaced-vbox
{TextField {validate-with {MyValidator}}},
{CommandButton
label = "OK",
{on Action do
{popup-message "OK"}
}
}
}
}


Edit a TextField's value (it's always marked as an error because of MyValidator) and
click a CommandButton without focusing out from the TextField.
Then two popup messages ("my error!" and "OK") are shown at the same time.

But in case attached validator has less validation time,
single message ("my error!") is shown and CommandButton's action isn't executed.
(please change the sleep time to 0.01s and try)

Shouldn't I rely on MyValidator to prevent firing CommandButton's action?
Are there any way to prevent it on errors exists?
Click to view friedger's profile MVP friedger 95 posts since
Jan 13, 2008
1. Re: Question about validation popup dialog May 8, 2008 2:54 AM

While processing you can disable the command button by

set cb.enabled? = false

You might want to name the command button "ok-button", then the use the command button name in the MyValidator like

{target.dialog.get-by-name "ok-button"}.enabled? = false

After validation finished unset the option.

Friedger

Click to view fukuta's profile Level 6 fukuta 74 posts since
Oct 17, 2007
2. Re: Question about validation popup dialog May 8, 2008 3:37 AM
in response to: friedger
Thanks friedger
MyValidator doesn't know where it is used,
so it cannot access command buttons that are on the same screen.
Click to view friedger's profile MVP friedger 95 posts since
Jan 13, 2008
3. Re: Question about validation popup dialog May 8, 2008 3:43 AM
in response to: fukuta

But the validator method "validate-value" gets the target (ValueControl), i.e. the TextField

The option "dialog" of target gives you the Dialog and target.dialog.get-by-name gives you a control in the dialog, e.g. the "ok-button".

You could provide the button's name in the constructor of MyValidator or use some kind of default names or a convention how to detect "ok-button"s.

Friedger

Click to view fukuta's profile Level 6 fukuta 74 posts since
Oct 17, 2007
4. Re: Question about validation popup dialog May 8, 2008 4:01 AM
in response to: friedger
In fact, my validator is used at many places with many buttons.
so I'm hesitating to do that approach.
And I wonder if it is a best practice or just workaround in the case like this.
Click to view friedger's profile MVP friedger 95 posts since
Jan 13, 2008
5. Re: Question about validation popup dialog May 8, 2008 4:13 AM
in response to: fukuta

You could also consider disabling the whole dialog and showing a wait cursor.

Maybe someone else could comment on best practice.

Friedger

Click to view fukuta's profile Level 6 fukuta 74 posts since
Oct 17, 2007
6. Re: Question about validation popup dialog May 8, 2008 4:51 AM
in response to: friedger
But disabling the dialog in the validate-value causes a value finished at the pending control,
so validation dialog is popuped at the moment the control is edited...
Click to view mgordon's profile Curl mgordon 36 posts since
Oct 17, 2007
7. Re: Question about validation popup dialog May 8, 2008 10:24 AM
The simplest "fix" is to change the Action handler on the CommandButton to validate the dialog before taking the action.
{on Action at cb:CommandButton do
    {if {validate-dialog {non-null cb.dialog}} then
        {popup-message "OK"}
    }
}


Another solution is to use an EnablingValidator on the button. This has the advantage that the user knows that they can't click the button until the inputs are valid. If you do this, you don't need the call to validate-dialog. You do need to put names on the inputs, or you need to assign them to variables so they can be passed to the EnablingValidator.

In the code below I changed the MyValidator class so that it doesn't always give an error. If you input the string "ok" then it is OK. Also, it doesn't check the input if there is none, unless input is required (by saying required? = true on the call to validate-with).

{curl 6.0, 7.0 applet}

{define-class package MyValidator {inherits Validator}
  {method public open {validate-value
                          controller:ValidationController,
                          target:ValueControl,
                          partial?:bool
                      }:#Visual

    {type-switch target.value-as-any
     case sv:StringInterface do
        {if (controller.required? or sv.size > 0) then
            {sleep .5s} || something heavy calculation
            {if sv == "ok" then
                {return null}
            }
            {return {message my error!}}
        }
    }
  {return null}
  }
}


{Dialog
    margin = 9pt,
    {spaced-vbox
        {TextField 
            name = "entry",
            {validate-with {MyValidator}, required? = false}
        },
        {CommandButton
            label = "OK",
            {on Action at cb:CommandButton do
                {if {validate-dialog {non-null cb.dialog}} then
                    {popup-message "OK"}
                }
            },
            {validate-with {EnablingValidator "entry"}}
        }
    }
}

Hope that helps.
Click to view fukuta's profile Level 6 fukuta 74 posts since
Oct 17, 2007
8. Re: Question about validation popup dialog May 11, 2008 9:53 PM
in response to: mgordon
mgordon, thanks for your advice.

I tried validate-dialog and EnablingValidator, but two undesired things remain.

First, If I use validate-dialog without EnablingValidator,
an empty dialog(having no messages on it) is shown after clicking the button.
This may happen when I enter an invalid value to text field and click the button before focusing out from it.
(Of course, this is of no matter if EnablingValidator is applied or validator takes less time to validate value.)

Second (this is rather difficult for me),
Not all of the buttons need to validate before taking the action.
i.e. a button that just shows a new window regardless of the input values doesn't need to validate.
So I cannot apply validate-dialog or EnablingValidator to all the buttons.
Click to view mgordon's profile Curl mgordon 36 posts since
Oct 17, 2007
9. Re: Question about validation popup dialog May 12, 2008 9:49 AM
I'm sorry I didn't see that problem earlier. The following event handler will work better. If the dialog is already known to be invalid, it won't try to validate again:

            {on Action at cb:CommandButton do
                {if-non-null d = cb.dialog then
                    {if d.valid? then
                        {if {validate-dialog d} then
                            {popup-message "OK"}
                        }
                    }
                }
            }

As far as EnablingValidator goes, you are correct. If you have a Help button, for example it should always be enabled and you don't need to put an EnablingValidator on it. Of course if there is a bad entry in the input field when you click the Help button, you will get an error message. This happens because when the field loses focus it fires ValueFinished and the validation will then notice the bad value. If that's not what you want, you can use dialog-on-finished? = false like this:

|| No dialogs until you click OK
{Dialog
    margin = 9pt,
    {spaced-vbox
        {TextField 
            {validate-with {MyValidator}, required? = false,
                dialog-on-finished? = false
            }
        },
        {CommandButton
            label = "OK",
            {on Action at cb:CommandButton do
                {if-non-null d = cb.dialog then
                    {if {validate-dialog d} then
                        {popup-message "OK"}
                    }
                }
            }
        },
        {CommandButton
            label = "Help",
            {on Action at cb:CommandButton do
                {popup-message "Help!"}
            }
        }

    }

You still get a color change to show you there is a problem but there is no dialog until you call validate-dialog.

In general, I would say that if you don't want to use EnablingValidator then you should use dialog-on-finished?=false.

Writing validation code is complicated because different people want it to work in different ways. I hope that once you figure out how you like it to work, you can use the same techniques again and again.
Click to view fukuta's profile Level 6 fukuta 74 posts since
Oct 17, 2007
10. Re: Question about validation popup dialog May 12, 2008 9:45 PM
in response to: mgordon
Thanks for your kindly advice.

What I have understood is that if there is a button needing no validation, it's better to use validator with 'dialog-on-finished?=false'.
It is something like a manner we should follow when designing behavior of validation.
Is this correct?

btw,
I'm afraid if I check the valid? of a dialog before calling validate-dialog, nothing comes to happen on clicking a button when the dialog is invalid.
So error messages are shown only when an invalid control loses focus.

Click to view mgordon's profile Curl mgordon 36 posts since
Oct 17, 2007
11. Re: Question about validation popup dialog May 13, 2008 7:52 AM
in response to: fukuta
It sounds like the style you want is
1. Always enable all buttons
2. If there are errors, display them when you click the button

To get this behavior, use diaog-on-finished?=false on the input fields and call validate-dialog in the Action event handler (without calling d.valid?). If the inputs can be invalid when the dialog is first shown, and you want an error dialog in that case, put {validate-with {DialogValidator}} on the Dialog.

Another style is
1. Always enable all buttons
2. If the user makes a mistake, tell them when they finish editing

In this case, use diaog-on-finished?=true on the input fields and call validate-dialog in the Action event handler only if d.valid? is true. In this style, you only get an error if you change an input. Again, if the inputs can be invalid when the dialog is first shown, and you want an error dialog in that case, put {validate-with {DialogValidator}} on the Dialog.

The other style we've discussed is
1. Only enable a button if he inputs it requires are valid
2. If the user makes a mistake, tell them when they finish editing

Here you also need to use diaog-on-finished?=true on the input fields, and you can use an EnablingValidator to enable the button when the inputs are valid. Again, we only show the user a message if the user made a bad entry. Using DialogValidator is recommended in this case to ensure that the buttons are disabled when the dialog is first shown, if appropriate.

The code for the first case looks like this:
{let D1:Dialog = 
    {Dialog
        margin = 9pt,
        {validate-with {DialogValidator}},
        {spaced-vbox
            {text
                Buttons always enabled, give messages when you click button
                {br}dialog-on-finished?=false
                {br}don't check d.valid?
            },

            {TextField 
                value = "d",
                {validate-with {MyValidator}, required? = false,
                    dialog-on-finished? = false
                }
            },
            {TextField 
                value = "3",
                {validate-with {MyValidator}, required? = false,
                    dialog-on-finished? = false
                }
            },
            {CommandButton
                label = "&OK",
                {on Action at cb:CommandButton do
                    {if-non-null d = cb.dialog then
                        {if {validate-dialog d} then
                            {popup-message "OK"}
                        }
                    }
                }
            },
            {CommandButton
                label = "&Help",
                {on Action at cb:CommandButton do
                    {popup-message "Help!"}
                }
            }

        }
    }
}

{CommandButton
    label = "Show Dialog &1",
    {on Action do
        {D1.show title = "Validate on Button"}
    }
}
Click to view fukuta's profile Level 6 fukuta 74 posts since
Oct 17, 2007
12. Re: Question about validation popup dialog May 13, 2008 9:26 AM
in response to: mgordon
All of the following behaviors are the style I originally want.
1. Always enable all buttons
2. If the user makes a mistake, tell them when they finish editing
3. If there are errors, display them when you click the button

And I wish I could make it with the style of 'diaog-on-finished?=true'.
Click to view mgordon's profile Curl mgordon 36 posts since
Oct 17, 2007
13. Re: Question about validation popup dialog May 13, 2008 2:39 PM
OK, I think you're running into a problem with MessageDialog. Here is another version that includes a subclass that should work better in this case.

{curl 6.0, 7.0 applet}
{set-document-properties font-size = 10pt}

{title Validate Always}

{doc-next
    {purpose A {docref MessageDialog} that can show one or more
        messages.
    }
}
{define-class public open MultiMessageDialog {inherits MessageDialog}

  field private mdpending?:bool = false
  field private results:ValidationResults = {ValidationResults}

  {doc-next
    {purpose Construct a {docref MultiMessageDialog} object.
    }
    {parameter title, The title to be used when a dialog is shown.
        If this is null a default title is used.
    }
    {parameter ..., Rest arguments are used to initialize this object,
        which will be placed within a dialog when shown.
    }
  }
  {constructor public {default
                          title:#String = null,
                          ...
                      }
    {construct-super title = title, {splice ...}}
  }

  {method public open {clear}:void
    {if not self.mdpending? then
        {self.results.clear}
    }
  }

  {method public open {show-results
                          results:ValidationResults
                      }:void
    {if {self.has-message? results} and not self.mdpending? then
        set self.mdpending? = true
        {self.add-internal replace? = true, {self.render-results results}}
        {after 0s do
            {popup-message title = self.title, self}
            set self.mdpending? = false
        }
    }
  }

  {method public open {show 
                          vc:ValidationComplete, 
                          target:ActiveTraversor
                      }:void
    {self.show-colors target}
    {if not vc.current? then {return}}
    let display?:bool = 
        not vc.consumed? and vc.display? and not vc.partial? 
        and not self.mdpending?
    {vc.consume}

    {if-non-null result = target.validation-result then
        {if-non-null message = result.message then
            {if self.mdpending? then
                {self.results.append result}
             else
                {self.results.clear}
                {self.results.append result}
            }
            {if display? then
                set self.mdpending? = true
                set self.color = self.message-color
                {after 0s do
                    {if {self.has-message? self.results} then
                        {self.add-internal replace? = true, 
                            {self.render-results self.results}}
                        {popup-message title = self.title, self}
                    }
                    set self.mdpending? = false
                }
            }
        }
    }
  }
}


{define-class package MyValidator {inherits Validator}
  {method public open {validate-value
                          controller:ValidationController,
                          target:ValueControl,
                          partial?:bool
                      }:#Visual

    {type-switch target.value-as-any
     case sv:StringInterface do
        {if target.valid? then
        {if (controller.required? or sv.size > 0) then
            {sleep .2s} || something heavy calculation
            {if sv == "ok" then
                {return null}
            }
            {return "Invalid input: " & target.value-as-any}
        }
        }
    }
  {return null}
  }
}

{let D4:Dialog = 
    {Dialog
        margin = 9pt,
        message-display = {MultiMessageDialog},
        {validate-with {DialogValidator}},
        {spaced-vbox
            {text
                Buttons always enabled, give messages when you click button
                and when edit finished
                {br}dialog-on-finished?=true
                {br}don't check d.valid?
            },

            {TextField 
                value = "d",
                {validate-with {MyValidator}, required? = false,
                    dialog-on-finished? = true, refocus? = true
                }
            },
            {TextField 
                value = "3",
                {validate-with {MyValidator}, required? = false,
                    dialog-on-finished? = true, refocus? = true
                }
            },
            {CommandButton
                label = "&OK",
                {on Action at cb:CommandButton do
                    {if-non-null d = cb.dialog then
                        {if {validate-dialog d} then
                            {popup-message "OK"}
                        }
                    }
                }
            },
            {CommandButton
                label = "&Help",
                {on Action at cb:CommandButton do
                    {popup-message "Help!"}
                }
            }

        }
    }
}

{CommandButton
    label = "&4. Check Always",
    width = 1.5in,
    {on Action do
        {D4.show title = "Validate on Button"}
    }
} Check on button and on entry finished
Click to view fukuta's profile Level 6 fukuta 74 posts since
Oct 17, 2007
14. Re: Question about validation popup dialog May 13, 2008 10:07 PM
in response to: mgordon
Thanks so much.
I mostly satisfied with your example, but my last problem still remains.
Sorry for my poor explanation.

I'd like to avoid the situation that Error and Help dialog are shown at a time.
When I enter an invalid input to a field and click the Help button without focusing out, I don't want the help dialog to show.
But if I don't edit the fields and click the Help button, then it is appreciated that help is shown (and error is not) even if invalid fields exists.

However I'm starting to think that it is impossible behavior..
1 2 Previous Next