-
Notifications
You must be signed in to change notification settings - Fork 9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Multi-view validators #24
Comments
On Jun 13, 2012, at 12:35 AM, Gabriel Foust wrote:
Agreed. The above choice is problematic in many ways.
Is it undesirable? (I am not sure how priorities should behave in this kind of cases)
Order of which values?
Maybe instead we should think this way: if a validation function needs to validate two (or more) In any case, we should not think that view == widget. View can be any programmatic entity that communicates with the viewmodel according to the agreed upon protocol.
Not sure what this means exactly. If so, then how does one signal failed validation?
|
We had some discussion of this on issue 14. The conclusion I reached (and no one has disagreed so far) is that priorities should reflect the order of editing, not the order in which values are actually inserted into the model. So: edit → touch, ¬edit → ¬touch
I just meant that if the validator takes multiple values (i.e. multiple parameters) then we need to specify the views which those values are coming from and the order in which they are passed (i.e. parameter order). Small detail.
Yes, that would work; but it seems to introduce a lot of extra work -- in order to validate we have to create a whole new view.
Yes, that is what I meant.
The validation method would take the "always succeeds" variable as input and would output to two variables: the validated variable and the associated error variable. The error variables may be bound to some other part of the UI to display the message. |
Here's another scenario to consider: In the hotel example we had three inputs—start date, end date, and number of nights—connected with a constraint. One of the feedback remarks we got was that we were not validating because you could enter a negative value for number of nights. So let's consider how we might validate this. The first thing to note here is that it's not enough to simply put a validator on number of nights because it's possible that the negative value was not entered directly but came from invalid start and end dates. We could also place validators on start and end date (or use a multi-value validator as described above) so that start date must come before end date, but then we would loose some of the helpful behavior of the constraint. To see how the validators would interfere with the constraint, consider a scenario where the form initially has start date (6/13/2012), end date (6/15/2012), and nights (2). Now say I want to set nights (3) and start date (7/1/2012). After making both of those edits, start date comes after end date and so will fail to validate. But if only we would let it into the model, the constraint would go into effect fixing the problem. We could get really complicated and let the validator know about the priority order so it could decide whether the constraint will fix the problem or not, but I think a much better solution is to do some sort of validation after the constraint has been enforced. One way to do this would be with a precondition. A precondition does not interfere with the working of the model, it simply notes after-the-fact that there was a problem. So a precondition would allow nights to be negative but would flag it with an error. But suppose this motel example is part of a much larger travel budget form. Suppose the number of nights is multiplied by some nightly rate to get a total cost, and that total cost is used in several other calculations in the form. If a negative number of nights is entered we get a negative total cost, which just makes the rest of the form nonsense. It seems desirable to me to be able to be able to specify that if number of nights is negative then it should not be used in calculations for the rest of the form. In other words, I would like to be able to validate number of nights within the model - after it has been calculated by the start-date/end-date/number-of-nights constraint, but before it is used by any other methods. If validation is simply a constraint between two variables then this is easy to do. |
On Jun 13, 2012, at 4:57 PM, Gabriel Foust wrote:
It would be more work, but maybe it is worth while to think what this work would be.
I think the issue then becomes that there will be garbage values in the model, and the rest |
OK, sure. So, I'll give it a go: The tricky thing about binding to two variables is that most of the code we've written assumes there's only one value being worked with; for example, the read operation is supposed to take a view and return a value; what do we do if our view holds two values? So we'd probably have to make two separate read operations -- one for each variable -- and then subscribe them to editing events on both widgets. That means the validator function would be executed twice: once for each read. And it would still mean that when one was edited (successfully) the priority of both would be updated. Another option (suggested by John) would be to create a third variable which holds an object constructed from the other two values (by means of a constraint). Now we can just bind to that third variable and read/write both values at the same time. Admittedly, this is not technically binding to two variables, but I think it's a much neater solution and avoids many of the issues generated by the other solution. (We still have the issue of just having a single priority for all variables.) I guess perhaps this could be generalized into a generic composite view type which took multiple views (with their associated read/write operations) and bound to a variable which held an object. The read operation for the composite type would perform a read on the views an assemble the results into an object. The write operation would take the fields of the object and perform a write on the corresponding views. |
OK... So, my impression is that you feel these extra variables sort of clutter up the model and get in the way. I'd like to consider that for a moment. In terms of efficiency, I would think these extra variables and constraints are adding a minimal amount of extra work for the system. There are a few extra methods to be considered when solving, but the code in the methods would need to be executed at some point anyway. And the extra work results in improved functionality. In terms of programmer design, I can definitely see how the extra variables are undesirable: the programmer should be able to focus on just the data model and completely ignore any details about the user interface. But we can support that through modular design. It's perfectly possible to create the core model on its own, then come back later and add any variables necessary for validation. For example, you could write a function that takes the core model as a parameter and adds to it; or perhaps even have the model be a class and then make a subclass that has the binding variables added. Even in ADAM (if we ever go back to that) we can add language support for modular design so that the underlying data model can be defined on its own. The only other aspect I can think of is debugging. It's possible that debugging the model might be worse (since there's a little more work going on), but I don't think it would be much worse. (And we really don't have any support for debugging a model anyway.) Are there other ways in which having extra variables is going to interfere with the model? |
On Jun 14, 2012, at 1:39 AM, Gabriel Foust wrote:
Actually, I don't really consider any of the above concerns to be real concerns :) In general, I'm not opposing putting validators to the view model as a matter of principle, but |
I see where you are going with this. Indeed, one may easily end up duplicating Recapitulating the two options of dealing with erroneous values:
Both have their uses and should thus be supported. I think 1) is non-problematic and we already support it. On Jun 13, 2012, at 5:20 PM, Gabriel Foust wrote:
I don't yet have a clear picture how this solves the complex cases of 2) One possibility of dealing with 2) might be to utilize undo (or model copying).
|
After reading this discussion, I can see a few different times at which validation is desired:
Perhaps we should support all of these uses, and they may require different facilities. |
This does not actually address 2). The scenario I'm describing is this: We have our three variables—start-date, end-date, and number-of-nights—just as in the previous example. However, we have a second number-of-nights variable—number-of-nights-validated. We have a constraint which copies the value of number-of-nights to number-of-nights-validated, but only if number-of-nights is greater than zero; otherwise it just leaves number-of-nights-validated alone. This way start-date, end-date, and number-of-nights continue to work in the same intuitive fashion as before; however, if we come up with a negative value for number-of-nights, that value will not be propagated throughout the rest of the form. This is similar to, but not quite the same as, the concept of breaking a variable down to an input variable and output variable with a validation constraint in between. The difference is that I am not assuming one of these is used for input only and the other is used for output only; they are two separate variables, each with their own purpose, and its up to the programmer to make sure he uses the correct one. The point I was making earlier was that if validation was really just a helper function that created a method in the graph, we could use the same mechanism for this type of validation as for the binding validation. |
This will lead to problems. The validator is in a constraint that writes the validated variable. Any other constraint that writes the unvalidated variable must not read the validated variable, or else a cycle appears. Thus, any other constraint that reads the validated variable must never write either variable, or else a cycle appears. I don't think that will be generally useful. In general, I don't want to place a burden on the programmer to figure out which methods should use the unvalidated variable and which should use the validated variable. |
Regarding John's item #2 "Before the value is used by a method: The validator checks a method's precondition.": Ways to deal with this:
|
Special considerations arise when dealing with validators that make use of multiple values. A simple example would be two text inputs which are used to enter numbers, the first of which must be larger than the second. To clarify discussion I'll call them A and B.
As an aside here, I'll note that there are a couple of other options for dealing with this problem. For example, a constraint could be used so that when one of these was edited the other was automatically updated to enforce the relationship. Another option would be to use a precondition, which would allow the user to enter numbers which did not preserve the relationship, but would flag an error and prevent execution of commands. Validators would be used in the case when we did not want to allow values which did not preserve the relationship into the model at all.
I'll suggest three possibilities for validating such a relationship:
The text was updated successfully, but these errors were encountered: