android - RxJava2 表单验证

标签 android validation rx-java2

我有一个表格,其中有 4 个可能的选项需要检查(根据情况也可能更少)。有 2 个编辑文本,一个用于电子邮件,一个用于创建订单时的引用字段。

根据条件(创建表单时可用),电子邮件和引用字段可能留空也可能不留空。此外,我们可能需要显示一个提醒对话框,告诉用户可能无法显示引用值(向订单的接收者),他们可能还需要同意条款和条件提醒对话框。

目前onConfirm检查是这样的,

void onCreateOrderConfirmed(@Nullable final String receiverEmail,
                            @Nullable final String reference,
                            @Nullable final Boolean noRefAgreed,
                            @Nullable final Boolean termsAndConditionsAgreed) {

    if (!reviewCompletionState.emailRequirementSatisfied()) {
        if (!isValidEmail(receiverEmail)) {
            view.showEmailError();
            return;
        }

        reviewCompletionState = reviewCompletionState.newBuilder()
                .receiverEmail(receiverEmail)
                .emailRequirementSatisfied(true)
                .build();
    }

    if (!reviewCompletionState.referenceRequirementSatisfied()) {
        if (isEmpty(reference)) {
            view.showReferenceError();
            return;
        }

        reviewCompletionState = reviewCompletionState.newBuilder()
                .reference(reference)
                .referenceRequirementSatisfied(true)
                .build();
    }

    if (!reviewCompletionState.noRefAgreed()) {
        if (noRefAgreed == null || !noRefAgreed) {
            view.showNoReferenceAlert();
            return;
        }

        reviewCompletionState = reviewCompletionState.newBuilder()
                .noRefAgreed(true)
                .build();
    }

    if (!reviewCompletionState.termsAndConditionsAgreed()) {
        if (termsAndConditionsAgreed == null || !termsAndConditionsAgreed) {
            view.showTermsDisclaimerAlert();
            return;
        }

        reviewCompletionState = reviewCompletionState.newBuilder()
                .termsAndConditionsAgreed(true)
                .build();
    }

    createOrder();
}

我很想知道是否有一种方法可以使用 RxJava2 简化此验证? (但目前还没有足够的知识来做到这一点)

TIA

最佳答案

这可以很简单。代码会比较多,我先展示结果

    private ReviewValidator reviewValidator = new ReviewValidator();

    void onCreateOrderConfirmed(@Nullable final String receiverEmail,
                                @Nullable final String reference,
                                @Nullable final Boolean noRefAgreed,
                                @Nullable final Boolean termsAndConditionsAgreed) {
        ReviewState reviewState = new ReviewState(receiverEmail,
                reference,
                noRefAgreed,
                termsAndConditionsAgreed);//another model for simplicity

        reviewValidator.validate(reviewState)
                .flatMap(reviewState -> /* create order */)
                .subscribe(this::onOrderCreated, this::onOrderCreatingError);

    }

    void onOrderCreated(Object order) {//or what you need here
        //handle positive result
    }

    void onOrderCreatingError(Throwable throwable) {
        if (throwable instanceof ValidateException) {
            List<ValidateError> errors = ((ValidateException) throwable).getValidateErrors();
            for (ValidateError error: errors) {
                switch (error.getField()) {
                    case EMAIL: {
                        view.showEmailError();
                        return;//or break if you want show all errors
                    }
                    case REFERENCE: {
                        view.showReferenceError();
                        return;
                    }
                    //handle another errors....
                }
            }
        //handle another error cases...
    }

首先,为 reviewState 创建模型:

public class ReviewState {

    private String receiverEmail;
    private String reference;
    private Boolean noRefAgreed;
    private Boolean termsAndConditionsAgree;

    public ReviewState(String receiverEmail,
                       String reference,
                       Boolean noRefAgreed,
                       Boolean termsAndConditionsAgree) {
        this.receiverEmail = receiverEmail;
        this.reference = reference;
        this.noRefAgreed = noRefAgreed;
        this.termsAndConditionsAgree = termsAndConditionsAgree;
    }

    public String getReceiverEmail() {
        return receiverEmail;
    }

    public String getReference() {
        return reference;
    }

    public Boolean getNoRefAgreed() {
        return noRefAgreed;
    }

    public Boolean getTermsAndConditionsAgree() {
        return termsAndConditionsAgree;
    }
}

然后创建您自己的验证器。无需创建整个模型,您可以为每个字段创建验证器,并根据您的选择将它们与 flatMap() 链接。

public class ReviewValidator extends Validator<ReviewState> {

    @Override
    protected List<ValidateFunction> getValidateFunctions(ReviewState reviewState) {
        List<ValidateFunction> validateFunctions = new LinkedList<>();
        validateFunctions.add(() -> validateEmail(reviewState.getReceiverEmail()));
        validateFunctions.add(() -> validateReference(reviewState.getReference()));
        //another validation methods
        return validateFunctions;
    }

    private ValidateError validateEmail(String email) {
        if (TextUtils.isEmpty(email)) {
            return new ValidateError(Field.EMAIL);//Field.EMAIL - just enum
        }
        return null;
    }


    private ValidateError validateReference(String reference) {
        if (TextUtils.isEmpty(reference)) {
            return new ValidateError(Field.REFERENCE);
        }
        return null;
    }
    //....
    //another validation methods
}

验证器的抽象类:

public abstract class Validator<Model> {

    public Single<Model> validate(Model model) {
        return Single.just(model)
                .map(this::validateModel)
                .flatMap(this::processResult);
    }

    private Single<Model> processResult(ValidateResultModel<Model> validateResultModel) {
        return Single.create(subscriber -> {
            List<ValidateError> validateErrors = validateResultModel.getValidateErrors();
            if (validateErrors.isEmpty()) {
                subscriber.onSuccess(validateResultModel.getModel());
            } else {
                subscriber.onError(new ValidateException(validateErrors));
            }
        });
    }

    private ValidateResultModel<Model> validateModel(Model model) {
        List<ValidateError> errors = new LinkedList<>();
        for (ValidateFunction validateFunctions : getValidateFunctions(model)) {
            ValidateError error = validateFunctions.validate();
            if (error != null) {
                errors.add(error);
            }
        }
        return new ValidateResultModel<>(model, errors);
    }

    protected abstract List<ValidateFunction> getValidateFunctions(Model model);

    protected interface ValidateFunction {

        @Nullable
        ValidateError validate();
    }
}

验证器的帮助类...

public class ValidateError {

    private Field field;

    public ValidateError(Field field) {
        this.field = field;
    }

    public Field getField() {
        return field;
    }
}

class ValidateResultModel<T> {

    private T model;
    private List<ValidateError> validateErrors;

    ValidateResultModel(T model, List<ValidateError> validateErrors) {
        this.model = model;
        this.validateErrors = validateErrors;
    }

    T getModel() {
        return model;
    }

    List<ValidateError> getValidateErrors() {
        return validateErrors;
    }
}

public class ValidateException extends RuntimeException {

    private List<ValidateError> validateErrors;

    ValidateException(List<ValidateError> validateErrors) {
        this.validateErrors = validateErrors;
    }

    public List<ValidateError> getValidateErrors() {
        return validateErrors;
    }
}

最初,我的想法来自这里:https://github.com/matzuk/TestableCodeMobius/tree/master/app/src/main/java/com/matsyuk/testablecodemobius/business/transfer/validation

关于android - RxJava2 表单验证,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47219424/

相关文章:

c++ - 初学者 C++ 使用 cin.fail() 防止无效输入

unit-testing - 测试 RxJava .delay(20, SECONDS)

java - 我可以创建一个类作为每种 Activity 类型的父类吗?

jquery - Rails 中的客户端验证以及 jQuery slider 中的表单

java - 是否可以动态访问 R.drawable 变量?

javascript - 为什么我的表单验证不正确

android - 从 retrofit2 和 rxjava 中的错误中获取 url

kotlin - kotlin,在subscribe()中使用观察者对象实例时得到了 “Type mismatch. Required: Disposable? Found: Unit”

android - 动态改变android :title in preference

ANDROID:按下按钮后如何延迟?