Office Hours — Today, January 5

Saturday, January 2

Jan 5
7:20 PM
Mark M.
has entered the room
Mark M.
turned on guest access
7:30 PM
Anshul V.
has entered the room
Mark M.
hello, Anshul!
how can I help you today?
Anshul V.
Hi Mark, happy new year!
Mark M.
Happy New Year to you as well!
Anshul V.
I have a use case with validating an EditText field. Let me try to describe it as clearly as I can.
I have a list of validators (provided by server), once the user enters text in the EditText, onFocusChange(), I need to run the list of validators one after the other.
7:35 PM
Anshul V.
I was thinking of chaining of these validators using RxJava, theoretically, it could work, but I am getting confused between observer, subscriber and the how to chain the List<Validator> and update the error/clear error for each validator in the list
sequentially
Please let me know if I am making sense.
Mark M.
you are to a point, though I'm going to have some questions :-)
"I have a list of validators (provided by server)" -- at what point are you getting these? Is it something you are trying to do on the fly as the user is typing, or are you getting this list ahead of time?
Anshul V.
ahead of time, in the viewmodel, along with other EditText properties (hint, editable?) etc. and with livedata updating the edittext view
Mark M.
OK, that is what I was hoping you would say :-)
next question: are the validation rules themselves doing any sort of I/O? or are they simple in-memory stuff, like matching with regular expression patterns?
7:40 PM
Anshul V.
they are: validators can be client-side (simple in-memory stuff) or server-side (network request for validation -> response either error message or 200OK)
the "type" property is in each Validator decides what validator it is
Mark M.
OK, given the network I/O, a background thread is needed -- I was hoping that perhaps RxJava would be overkill
final(?) question: what is the end result of the validation? are you showing an error message in the UI, and clearing it if the validation passes?
and, if that is correct... what if there is more than one error? do you only show one?
Anshul V.
correct. textInputLayout.setError()
that's where the chaining comes in. if the list is { "server", "is_email_address" }, once the "server" response is OK, we validate for "is_email_address" on client side
7:45 PM
Mark M.
why would you need to wait to validate is_email_address? it would be faster (and probably simpler) to do them in parallel, particularly if there might be 2+ server calls
Anshul V.
the order of the list elements decides the priority of the validators. so if "server" is first, until that validation passes, don't run the next validator.
Mark M.
OK, give me a couple of minutes to think about this
Anshul V.
Sure :)
7:50 PM
Mark M.
If you only need the first error, then I think that one approach would be to use Single.concat(). You would have each Validator be wrapped(?) in a Single that emits something if the validation succeeds and emits an error if the validation fails. You would pass these in a priority-ordered list to concat(). If the resulting Flowable completes normally, then the overall validation succeeded. If the Flowable has an error, that would be from the first Validator that failed.
note: I am not an RxJava expert -- I know enough to get around and get stuff done, but there may be better solutions than this, even assuming that this solution meets your criteria
7:55 PM
Anshul V.
Oh, okay. Right, I am still getting grasps with the basics of RxJava, I thought it would help in this use case, but I wasn't sure about the "how" part
Mark M.
based on the marble diagram, once the error is received, all later Singles in the list are ignored, which should meet your need of sequentially trying validators until you get your first error, or you run out of validators
if the validators return useful data in the "passed" case, you could have the Singles emit that -- otherwise, what they emit in the "passed" case really does not matter, as you are really looking for whether the Flowable completes or results in an error
Anshul V.
Makes sense, so if Single<"Server"> is in concat(), it returns an error, the Flowable would error out and I can update the TextInputLayout error with the error returned. if it passes, it would go to next Single<"is_email_address"> and if correct I can clear the previous error (if any) and do TextInputLayout.error = null
Mark M.
assuming that those are the only two validation Singles in the list, then yes
Anshul V.
No, passed case doesn't return any useful data other than notifying the TextInputLayout to clear the error if any
8:00 PM
Mark M.
in that case, you probably will wind up using this three-parameter subscribe() on Flowable: http://reactivex.io/RxJava/javadoc/io/reactivex...
onNext would do nothing; onError would set your error on the TextInputLayout; onComplete would clear your error
Anshul V.
Makes sense. Now for what would be the observer, subscriber. Let me try to explain if I can. OnFocusChange() triggers the validation, so that becomes the Observable<EditText>, Observer would be in ViewModel which would run the Single.concat(), if error is returned (Livedata would update the textInputLayout.setError()) field.
Mark M.
are you using RxBinding or something that is tying RxJava to views? if so, I have not used that personally
8:05 PM
Mark M.
but, yes, the viewmodel presumably is where the Single.concat() would go
Anshul V.
That was going to be my next question, I haven't used RxBinding (nor do I want to). I was hoping there would be a LiveData <-> RxJava combination which could work with this use case
Mark M.
the only part that I was not understanding was "OnFocusChange() triggers the validation, so that becomes the Observable<EditText>"
IIRC, RxBinding is a Jake Wharton library that handles this sort of thing, allowing you to turn callbacks like onFocusChange() into RxJava chains
I have done that with coroutines, but not RxJava
Anshul V.
Oh, apologies, my bad. I wasn't sure what would observer/subscriber be, I was trying my best guess.
Mark M.
in the absence of RxBinding, you would just set up an ordinary OnFocusChangeListener that would call a method on your viewmodel to trigger the validation work
8:10 PM
Anshul V.
Ah, I see. so OnFocusChangeListener calls the viewmodel.validate(), any error from concat posts a Livedata value updating the error, once users fixes it, OnFocusChangeListener calls the viewmodel.validate() again, and on everything passing, livedata posts value clearing the error.
Mark M.
right
Anshul V.
Co-routines is such an alien-concept for me. Only yesterday I found out you have a whole book on that subject :D
Got it. One more question. Currently, I have a modelLiveData which updates everything (including TextInputLayout setError), would it make sense to separate the validation part out to a validatorLiveData which specifically updates the TextInputLayout.error field?
Mark M.
possibly -- you could do it all as one LiveData if you wanted, or split them out
I have used both approaches
8:15 PM
Mark M.
(not specifically for validation, but for similar sorts of things)
Anshul V.
View paste
Okay, so to summarize:
1. Convert List<Validator> => List<Single<Validator>>
2. feed the List<Single<Validator>> in the concat() function
3. If error, post with message
4. If success, post to clear the error (if any)
Mark M.
the Single would not be *emitting* a Validator... though I suppose it could, if you really wanted
it's more that the Single would *wrap* a Validator, knowing how to map whatever the Validator API is to what a Single needs
so, suppose that Validator had a single method, validate(), that returned a boolean
8:20 PM
Mark M.
the Single might be Single.create { emitter -> if (validate()) emitter.onSuccess(true) else emitter.onError(YourValidationException()) }
in reality, that's not quite right, in that you would need to get the error message into the exception, but it should be close
(oh, and I forgot to have the Validator be in scope for the vaildate() call)
(this is why I do not usually do live-coding in these chats... :-)
Anshul V.
Hehe, that's alright, it is still very helpful!
So each single that goes in the concat() would have the validation logic in it and passing/failing of that logic is what would be returned from the concat() as a Flowable.
Mark M.
each Single would perform one validation using a Validator, and the Flowable represents the aggregate results (all succeeded, or the first failure)
8:25 PM
Mark M.
so, going back to your list, other than Single<Validator> implying that the Single emits the Validator itself, that list seems fine
Anshul V.
Hmm, okay. Can I create a function for each validator Single and make a list of those List<Single> and combine them? Just trying to understand the conversion of List<Validator> to what List<Single> goes in the concat().
Mark M.
there are a handful of static methods on Single for creating Singles from stuff
I used create() above
there is also fromCallable()
one of those will probably fit your needs for wrapping your Validator in a Single
Anshul V.
I see, okay. Thank you, Mark. This is super helpful :D
Mark M.
happy to help!
8:30 PM
Mark M.
and that's a wrap for today's chat
the transcript will be posted on https://commonsware.com/office-hours/ shortly
the next chat is Thursday at 8:30am US Eastern
have a pleasant day!
Anshul V.
Okay, thank you. Have a good evening!
Anshul V.
has left the room
Mark M.
turned off guest access

Saturday, January 2

 

Office Hours

People in this transcript

  • Anshul Vyas
  • Mark Murphy