Skip to content

Instantly share code, notes, and snippets.

@jayrambhia
Last active October 18, 2020 18:25
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save jayrambhia/52c65afbb74e204a14de1c32b3fcefac to your computer and use it in GitHub Desktop.
Save jayrambhia/52c65afbb74e204a14de1c32b3fcefac to your computer and use it in GitHub Desktop.
Post about form validation using RxJava http://www.jayrambhia.com/blog/rx-form-validations
public interface AvailabilityChecker {
Observable<ValidationResult<String>> isEmailAvailable(@NonNull String email);
Observable<ValidationResult<String>> isUsernameAvailable(@NonNull String email);
ValidationResult<String> isEmailAvailableSync(@NonNull String email);
ValidationResult<String> isUsernameAvailableSync(@NonNull String email);
}
_subscription = Observable.combineLatest(usernameSubject, emailSubject, phoneObservable,
new Func3<Boolean, Boolean, Boolean, Boolean>() {
@Override
public Boolean call(Boolean validUsername, Boolean validEmail, Boolean validPhone) {
return validUsername && validEmail && validPhone;
}
}).subscribe(new Action1<Boolean>() {
@Override
public void call(Boolean enabled) {
submitButton.setEnabled(enabled);
}
});
Subscription _subscription = Observable.combineLatest(usernameObservable, emailObservable, phoneObservable,
new Func3<Boolean, Boolean, Boolean, Boolean>() {
@Override
public Boolean call(Boolean validUsername, Boolean validEmail, Boolean validPhone) {
return validUsername && validEmail && validPhone;
}
}).subscribe(new Action1<Boolean>() {
@Override
public void call(Boolean enabled) {
submitButton.setEnabled(enabled);
}
});
public class RandomAvailabilityChecker implements AvailabilityChecker {
private static final String TAG = "RandAvailabilityChecker";
private Random random;
public RandomAvailabilityChecker() {
random = new Random();
}
@Override
public Observable<ValidationResult<String>> isEmailAvailable(@NonNull final String email) {
return Observable.defer(new Func0<Observable<ValidationResult<String>>>() {
@Override
public Observable<ValidationResult<String>> call() {
return Observable.just(isEmailAvailableSync(email));
}
}).delay(1200, TimeUnit.MILLISECONDS);
}
@Override
public Observable<ValidationResult<String>> isUsernameAvailable(@NonNull final String username) {
return Observable.defer(new Func0<Observable<ValidationResult<String>>>() {
@Override
public Observable<ValidationResult<String>> call() {
return Observable.just(isUsernameAvailableSync(username));
}
}).delay(3000, TimeUnit.MILLISECONDS);
}
@Override
public ValidationResult<String> isEmailAvailableSync(@NonNull String email) {
int rand = random.nextInt(12);
if (rand > 4) {
return ValidationResult.success(email);
}
return ValidationResult.failure("Email is already taken", email);
}
@Override
public ValidationResult<String> isUsernameAvailableSync(@NonNull String username) {
int rand = random.nextInt(12);
if (rand > 4) {
return ValidationResult.success(username);
}
return ValidationResult.failure("Username is already taken", username);
}
}
public static Observable<String> getTextWatcherObservable(@NonNull final EditText editText) {
final PublishSubject<String> subject = PublishSubject.create();
editText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {}
@Override
public void afterTextChanged(Editable s) {
subject.onNext(s.toString());
}
});
return subject;
}
Observable<String> emailObservable = RxHelper.getTextWatcherObservable(emailView);
Observable<String> usernameObservable = RxHelper.getTextWatcherObservable(usernameView);
Observable<String> phoneObservable = RxHelper.getTextWatcherObservable(phoneView);
Subscription _subscription = Observable.combineLatest(emailObservable, usernameObservable,
phoneObservable, new Func3<String, String, String, Boolean>() {
@Override
public Boolean call(String email, String username, String phone) {
return false;
}
}).subscribe(new Action1<Boolean>() {
@Override
public void call(Boolean enabled) {
Log.i(TAG, "submit button enabled: " + enabled);
}
});
public class ValidationResult<T> {
private boolean valid;
private String reason;
private T data;
public static <T> ValidationResult<T> success(T t) {
return new ValidationResult<>(true, null, t);
}
public static <T> ValidationResult<T> failure(@Nullable String reason, T t) {
return new ValidationResult<>(false, reason, t);
}
private ValidationResult(boolean valid, @Nullable String reason, T t) {
this.valid = valid;
this.reason = reason;
this.data = t;
}
public boolean isValid() {
return valid;
}
@Nullable
public String getReason() {
return reason;
}
public T getData() {
return data;
}
}
Observable<Boolean> emailObservable = RxHelper.getTextWatcherObservable(emailView)
.debounce(800, TimeUnit.MILLISECONDS)
.subscribeOn(Schedulers.io())
.map(new Func1<String, ValidationResult<String>>() {
@Override
public ValidationResult<String> call(String s) {
return validateEmail(s);
}
}).map(new Func1<ValidationResult<String>, ValidationResult<String>>() {
@Override
public ValidationResult<String> call(ValidationResult<String> result) {
if (!result.isValid()) {
return result;
}
return availabilityChecker.isEmailAvailableSync(result.getData());
}
}).observeOn(AndroidSchedulers.mainThread())
.map(new Func1<ValidationResult<String>, Boolean>() {
@Override
public Boolean call(ValidationResult<String> result) {
emailView.setError(result.getReason());
return result.isValid();
}
});
Observable<Boolean> emailObservable = RxHelper.getTextWatcherObservable(emailView)
.debounce(800, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.map(new Func1<String, Boolean>() {
@Override
public Boolean call(String s) {
Log.i(TAG, "validate email: " + s);
ValidationResult result = validateEmail(s);
emailView.setError(result.getReason());
return result.isValid();
}
});
private void setupObservables() {
emailSubject = PublishSubject.create();
RxHelper.getTextWatcherObservable(emailView)
.debounce(800, TimeUnit.MILLISECONDS)
.map(new Func1<String, ValidationResult<String>>() {
@Override
public ValidationResult<String> call(String s) {
cancelEmailApiCall();
return validateEmail(s);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<ValidationResult<String>>() {
@Override
public void call(ValidationResult<String> result) {
if (!result.isValid()) {
emailView.setError(result.getReason());
emailSubject.onNext(false);
return;
}
callApiToValidateEmail(result.getData());
}
});
}
private void callApiToValidateEmail(@NonNull String email) {
cancelEmailApiCall();
emailApiSubscription = availabilityChecker.isEmailAvailable(email)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<ValidationResult<String>>() {
@Override
public void call(ValidationResult<String> result) {
emailView.setError(result.getReason());
emailSubject.onNext(result.isValid());
}
});
}
private void cancelEmailApiCall() {
if (emailApiSubscription != null && !emailApiSubscription.isUnsubscribed()) {
emailApiSubscription.unsubscribe();
emailApiSubscription = null;
}
}
RxHelper.getTextWatcherObservable(emailView)
.map(new Func1<String, String>() {
@Override
public String call(String s) {
cancelEmailApiCall();
return s;
}
})
.debounce(800, TimeUnit.MILLISECONDS)
....
Other stuff
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment