feat(StatusInput): add support for asynchronous validators

This adds support for asynchronous validators which are also just `StatusValidator`.

There are a few differences to syncronous validators though:

1. `validate` function doesn't return, it's only used to trigger the async
   validation. Ideally it would return an async object like Promise or Observable
   but since we have to rely on QML `Connections`, this won't work.
2. As mentioned, `Connections` are needed to listen to async events so the
  validation can be resolved.
3. Validation has to be resolved by calling `input.updateValidity` inside the validator.
   That API notifies `StatusInput` that validation is has been performed and expects
   the name of the validator as well as either a boolean (true), if validation was successful,
   or, if validation has failed, a boolean (false) or an error object.

Here's a `StatusENSValidator` as an example:

```qml
StatusValidator {

    id: root

    name: "ensValidator"
    errorMessage: "Couldn't resolve ENS name."

    readonly property string uuid: Utils.uuid()

    property int debounceTime: 600

    signal ensResolved(string address)

    validate: Backpressure.debounce(root, root.debounceTime, function (name) {
        name = name.startsWith("@") ? name.substring(1) : name
        walletModel.ensView.resolveENS(name, uuid)
    })

    Connections {
        target: walletModel.ensView
        onEnsWasResolved: {
            if (uuid !== root.uuid) {
                return
            }
            root.ensResolved(resolvedAddress)
            input.updateValidityAndPendingState(root.name, resolvedAddress !== "")
        }
    }
}
```

Closes #395
This commit is contained in:
Pascal Precht 2021-09-14 13:43:10 +02:00 committed by Eric Mastro
parent 899f4461bb
commit fe67997696
No known key found for this signature in database
GPG Key ID: 141E3048D95A4E63
3 changed files with 43 additions and 0 deletions

View File

@ -47,6 +47,7 @@ Item {
property bool valid: true
property bool pristine: true
property bool dirty: false
property bool pending: false
property bool leftIcon: true
property StatusIconSettings icon: StatusIconSettings {

View File

@ -24,6 +24,7 @@ Item {
property alias input: statusBaseInput
property alias valid: statusBaseInput.valid
property alias pending: statusBaseInput.pending
property alias text: statusBaseInput.text
property string label: ""
property string secondaryLabel: ""
@ -32,13 +33,18 @@ Item {
property real leftPadding: 16
property real rightPadding: 16
property list<StatusValidator> validators
property list<StatusValidator> asyncValidators
property int validationMode: StatusInput.ValidationMode.OnlyWhenDirty
property var pendingValidators: []
enum ValidationMode {
OnlyWhenDirty, // validates input only after it has become dirty
Always // validates input even before it has become dirty
}
property var errors: ({})
property var asyncErrors: ({})
function reset() {
statusBaseInput.valid = false
@ -78,6 +84,40 @@ Item {
}
}
}
if (asyncValidators.length && !Object.values(errors).length) {
root.pending = true
for (let idx in asyncValidators) {
let asyncValidator = asyncValidators[idx]
if (pendingValidators.indexOf(asyncValidator.name) == -1) {
asyncValidator.input = root
pendingValidators.push(asyncValidator.name)
asyncValidator.validate(statusBaseInput.text)
}
}
}
}
function updateValidity(validatorName, result) {
if (!asyncErrors) {
asyncErrors = {}
}
if (typeof result === "boolean" && result) {
if (asyncErrors[validatorName] !== undefined) {
delete asyncErrors[validatorName]
}
errorMessage.text = ""
} else {
asyncErrors[validatorName] = result
for (let idx in asyncValidators) {
errorMessage.text = asyncValidators[idx].errorMessage || root.errorMessage
break;
}
}
pendingValidators = pendingValidators.filter(v => v !== validatorName)
root.pending = pendingValidators.length > 0
root.valid = Object.values(asyncErrors).length == 0
}
Component.onCompleted: validate()

View File

@ -1,10 +1,12 @@
import QtQuick 2.13
import StatusQ.Controls 0.1
QtObject {
id: statusValidator
property string name: ""
property string errorMessage: "invalid input"
property StatusInput input
property var validate: function (value) {
return true