2017-11-10 2 views
8

Ich habe ein Formular mit 4 möglichen Optionen, die überprüft werden müssen (je nach den Umständen auch weniger). Es gibt 2 Editexte, einen für E-Mail und einen für ein Referenzfeld, wenn Sie einen Auftrag erstellen.RxJava2 Formularvalidierung

Die E-Mail- und Referenzfelder können auf Grund der Bedingungen (die beim Erstellen des Formulars verfügbar sind) möglicherweise nicht leer gelassen werden. Darüber hinaus müssen wir möglicherweise einen Warndialog anzeigen, um dem Benutzer mitzuteilen, dass der Referenzwert möglicherweise nicht angezeigt werden kann (dem Empfänger der Bestellung) und dass er möglicherweise einem Dialogfeld für Bedingungen und Bedingungen zustimmen muss.

Derzeit werden die onConfirm Kontrollen ist in etwa so,

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(); 
} 

Ich würde gerne wissen, ob es einen Weg gibt, diese Validierung einfacher mit RxJava2 zu machen? (Aber nicht wissen, zur Zeit genug, um der Lage sein, dies zu tun)

TIA

Antwort

3

Ich glaube, Sie sollten die RxJava CombineLatest, so müssen Sie alle Form Eingang sind eine beobachtbare produzieren, dann sind Sie gerade kombinieren und passen Sie die Ansicht

als Referenz können Sie überprüfen:

https://medium.com/@etiennelawlor/rxjava-on-the-sign-in-screen-9ecb66b88572

Using RxJava for email login validation, an observable is emitting twice

========

Beispiel:

import android.support.v7.app.AppCompatActivity; 
import android.os.Bundle; 
import android.widget.Button; 
import android.widget.CheckBox; 
import android.widget.EditText; 
import android.widget.Toast; 

import com.jakewharton.rxbinding2.view.RxView; 
import com.jakewharton.rxbinding2.widget.RxCompoundButton; 
import com.jakewharton.rxbinding2.widget.RxTextView; 

import io.reactivex.Observable; 


public class MainActivity extends AppCompatActivity { 

@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_main); 

    EditText receiverText = findViewById(R.id.input_receiver); 
    EditText referenceText = findViewById(R.id.input_reference); 
    CheckBox checkRef = findViewById(R.id.check_ref); 
    CheckBox checkTerms = findViewById(R.id.check_terms); 
    Button buttonLogin = findViewById(R.id.button_login); 

    Observable<CharSequence> receiverObservable = RxTextView.textChanges(receiverText).skip(1); // can add more logic 
    Observable<CharSequence> referenceObservable = RxTextView.textChanges(referenceText).skip(1); // can add more logic 
    Observable<Boolean> refCheckObservable = RxCompoundButton.checkedChanges(checkRef); // can add more logic 
    Observable<Boolean> termsCheckObservable = RxCompoundButton.checkedChanges(checkTerms); // can add more logic 

    Observable<String> combineObservable = Observable.combineLatest(
      receiverObservable, 
      referenceObservable, 
      refCheckObservable, 
      termsCheckObservable, (receiverCharSequence, referenceCharSequence, refBoolean, termsBoolean) -> { 
       // add logic here for now it is only combine the input 
       return receiverCharSequence + " " + referenceCharSequence + " " + refBoolean + " " + termsBoolean ;} 
      ); 

    RxView.clicks(buttonLogin).flatMap(o -> { return combineObservable;}).distinctUntilChanged().subscribe(string -> { 
     Toast.makeText(this, string, Toast.LENGTH_LONG).show(); 
    }); 

    } 
} 
+0

Dank! Ich möchte nicht unbedingt, dass das automatische Zeugs mit dem Textwechsel usw. nur validiert werden soll, wenn der Benutzer auf die Bestätigungsschaltfläche klickt (weil ich nicht überprüfen möchte, ob ich die Warndialogfelder bis dahin öffnen soll) –

+0

Dann setzen Sie den Mähdrescher spätestens in die Bestätigungsschaltfläche –

+0

Könnten Sie bitte ein kurzes Snippet teilen, wie das aussehen würde (wo mindestens einer der Dialoge angezeigt werden muss)? –

4

Dies kann eine einfache sein. Es wird viel Code geben, ich werde das Ergebnis zuerst zeigen.

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... 
    } 

Zunächst erstellen Modell für 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; 
    } 
} 

Dann erstellen Sie Validator besitzen. Es ist nicht notwendig, ein ganzes Modell zu erstellen, Sie können für jedes Feld einen Validator erstellen und diese mit flatMap(), Ihrer Wahl, verknüpfen.

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 
} 

Abstrakte Klasse für Validator:

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(); 
    } 
} 

Helper-Klassen für Validator ...

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; 
    } 
} 

Zunächst ich die Idee von hier nahm: https://github.com/matzuk/TestableCodeMobius/tree/master/app/src/main/java/com/matsyuk/testablecodemobius/business/transfer/validation