Observable value in validation controlsfx - javafx

I have the following code
public ValidationResult notNull(Control control, String content) {
boolean condition = content.length() <=0;
return ValidationResult.fromMessageIf(control, "Field is empty!", Severity.WARNING, condition);
}
it checks if there is any character in text field,
the i call it like this
validator = new ValidationSupport();
validator.registerValidator(itemIdTf,vals::notNull);
and finally is do this
validator.invalidProperty().addListener((observable, oldValue, newValue) -> {
itemIdTf.pseudoClassStateChanged(PseudoClass.getPseudoClass("negative"), oldValue);});
And this works, it sets the pseudo class for certain controll, how ever when i have few text field controls on same validatior it must wait for all of them to be validated before changing the pseudoclass.
so i thought to perhaps do it in the ValidationResult method, because i think using many validators is probably not good. How ever i dont know if that is possible, i need some listener that is unique to every control, not for validation result.

well i found something that works, but it still leaves some unanswered questions :
public ValidationResult notNull(Control control, String content) {
boolean condition = content.length() <=0;
control.pseudoClassStateChanged(positive,!condition);
return ValidationResult.fromMessageIf(control, "Field is empty!", Severity.ERROR, condition);
}
i am using css styling validator, so the things that come from this line do not work (at least not all of them)
return ValidationResult.fromMessageIf(control, "Field is empty!", Severity.ERROR, bp.getValue());
Here above there is string "Field is empty" which should be tooltip for control, however its never set, so i just created my own tooltip inside validation and i add it to control.
then the whole thing looks something like this :
PseudoClass positive = PseudoClass.getPseudoClass("positive");
final Tooltip notNullTooltip = new Tooltip("Must have some value");
public ValidationResult notNull(Control control, String content) {
boolean condition = content.length() <=0;
control.pseudoClassStateChanged(positive,!condition);
control.setTooltip(notNullTooltip);
return ValidationResult.fromMessageIf(control, "Field is empty!", Severity.ERROR, condition);
}
And it does work, if anyone has more elegant solution i would be thankfull.

Related

JavaFX: check whether a text property is blank (and not just empty)

I want to have a button enabled or disabled based on whether a text field contains anything, and I want to implement this by using property binding.
So at first I used the isEmpty() method on the text field's text property to create a boolean binding for the button's disabled property:
startSearchButton.disableProperty().bind(searchField.textProperty().isEmpty());
While the binding works, my definition of "text field contains anything" is different to what the isEmpty() method does, namely just checking if the text's length is > 0. However, I'm interested in whether there is "real" text, i.e. whether the text field is blank (not just not empty, but actually not only whitespace).
Unfortunately there is no method isBlank(), and I also couldn't find anything appropriate in the Bindings utility class. Now I saw that you can implement any custom boolean property you like via the Bindings.createBooleanProperty method, but I'm not yet familiar with the concept of defining custom bindings. How would I have to implement such a boolean property for my case?
You can create a custom binding using (among many methods) Bindings.createBooleanBinding(...). The first argument is a function that computes the value of the binding (you can trim whitespace from the text with trim() and then check if the result is empty); the remaining arguments are a list of observables that trigger recomputation of the binding. You want to recompute the binding when the text in the text field changes, so just specify the text property:
startSearchButton.disableProperty().bind(Bindings.createBooleanBinding(() ->
searchField.getText().trim().isEmpty(),
searchField.textProperty());
As for 2022
public static BooleanBinding isBlank(StringProperty stringProperty) {
return Bindings.createBooleanBinding(() -> stringProperty.get().isBlank(), stringProperty);
}
public static BooleanBinding isNotBlank(StringProperty stringProperty) {
return isBlank(stringProperty).not();
}
(idk why didn't nobody suggest String::isBlank, perhaps it was added in Java later)
You can do this too:
With Apache StringUtils.isBlank()
startSearchButton.disableProperty().bind(Bindings.createBooleanBinding(() ->
StringUtils.isBlank(searchField.getText()),
searchField.textProperty());
Create you own method
public static boolean IsNullOrWhitespace(String s) {
if(s == null) {
return true;
}
for(int i = 0; i < s.length(); ++i) {
if(!Character.isWhitespace(s.charAt(i))) {
return false;
}
}
return true;
}
and then:
startSearchButton.disableProperty().bind(Bindings.createBooleanBinding(() ->
IsNullOrWhitespace(searchField.getText()),
searchField.textProperty());

Where to place validation code

I've created a simple form with an enum field on a grid, dragged from the DataSource CompanyImage:
Table CompanyImage has an Index on this field named Brand in my example and AllowDuplicates is set to No :
And here is the form:
I've overridden the close() method of the form like this:
public void close()
{
CompanyImage_ds.write();
super();
}
An error is displayed when I close it saying that
"Cannot create a record in CompanyImage(CompanyImage). Legal entities: Example1.
The record already exists."
That's fine but I would like a way to stop closing the window when this happens. A validateWrite() would be nice but I am not really able to figure out where and what to write in order to accomplish this behavior.
I mean, how to check that new row is added and it contains a field that already exists in the table ?
You shouldn't have to force the write() method. Closing the form should already do it.
If you wish to check something to allow the form to be closed, the close() method is too late in execution. You should leverage the canClose() method.
You could override the validate method of the grid column. You would need to write some validation logic in that method but that would prevent the column from saving at all if validation failed.
public boolean validate()
{
boolean ret;
// write your own validation logic
if (validation logic is true)
{
ret = true;
}
return ret;
}

Replace default client side validation by custom one in ASP.NET MVC

I have a view model containing a DateTime property, for which I want to provide a text box using using a custom format (only month and year, "MM.YYYY"):
public class MyModel {
public DateTime? DateField {get; set;}
}
Formatting the value for the TextBox is easy (using the format string). I also have implemented a custom model binder to do the conversion and this works fine.
I still have a problem with client side validation: I can implement a custom validator deriving from ValidationAttribute which implements IClientValidatable and set up the corresponding jquery.validate adapters etc.
But MVC still adds the "default" validation attribute data-val-date (in addition to my custom validation attribute data-val-monthyeardate) to the input field, so the default check still applies and the input "MM.YYYY" is rejected.
Is there any way to suppress the default client side validation for a data type and replace it with a custom one (instead of "adding" the custom one)?
Since the framework doesn't let you override the real type with some custom attribute or even override the ModelMetadataProvider for specific types, you'll have to register your own global ModelMetadataProvider that fools the validator to think it's actually a string.
Something like:
public class MyModelMetadataProvider : DataAnnotationsModelMetadataProvider
{
protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
{
if (attributes.OfType<ExcludeCharAttribute>().Any())
modelType = typeof (String);
return base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName); ;
}
}
Register it using:
ModelMetadataProviders.Current = new MyModelMetadataProvider();
Still, since the core problem is a client-side problem, I would deal with it purely with client-side code. In your monthyeardate adapter you can force removal of the date validation (I can provide an example if you'll share your monthyeardate code).
See MSDN
This is hacky but one simple thing you could do is add:
$(function () {
$.validator.methods.date = function () { return true; };
});
So that the default data-val-date always returns true along with firing your custom date validation.
I know that this is a little older, and just in case someone doesn't think about it (and since I cannot leave comments yet), to enhance #AlexC response, you can add validation to to that statement.
For instance, I use moment.js for date validation (moment.js), and this allows you to add your own validation rules.
if (moment(a, "M/YYYY").isValid() || moment(a).isValid())
{
return true;
}
This will check to see if it is a regular date, and also in this case, if the date is in "M/YYYY" format. If one of these are true, it accepts the validation.

How to get value from System.Web.UI.Control (base class)?

Is there a way to access a control's value/text using the base class?
Looking at TextBox and HiddenField, System.Web.UI.Control is the most specific base class they share. Seems like a good candidate, but I don't see an obvious way to access the text/value.
I do see that both class definitions use the ControlValuePropertyAttribute to identify the property that holds their text/value... e.g. HiddenField is set to "Value", TextBox is set to "Text". Not sure how to use that info to retrieve the data, though.
Background
I have an extension method that takes the text of some web control (e.g., a TextBox) and converts it to a given type:
<Extension()> _
Public Function GetTextValue(Of resultType)(ByVal textControl As ITextControl, ByVal defaultValue As resultType) As resultType
Return ConvertValue(Of resultType)(textControl .Text, defaultValue)
End Function
Now, I need the value of a HiddenField (which does not implement the ITextControl interface). It would be easy enough to overload the function, but it would be awesome to just handle the base class and not have to deal with writing overloads anymore.
Edit - additional info
Here is an example of how the extension method is used
Dim myThing as Decimal = tbSomeWebControl.GetTextValue(Of Decimal)(0) ' Converts the textbox data to a decimal
Dim yourThang as Date = hdnSomeSecret.GetTextValue(Of Date)(Date.MinValue) ' Converts the hiddenfield data to a Date
Currently, this requires two overloads because data is accessed using the Value property in hiddenfields and the Text property in textboxes.
What's wrong with overloads? Nothing, except i'm writing nearly the same code over and over (just modifying the 1st parameter of the definition and the 1st argument of its call).
You cant use object-oriented approach to get the value, because they don't have common ancestor in inheritance tree from which you can fetch data. Solutions are so inelegant that you should just pass WebControl and check type dynamically.
Pardon my C#:
(note that this is notepad code, I didn't run any of it so you may need some tweaks)
Solution 1: Get data directly from Request
Downside: Not very ASP.NET-ish. Does the job however.
public string GetTextValue<ResultType>(WebControl control, ResultType defaultValue)
{
// If you only wish data from form, and not questy string or cookies
// use Request.Form[control.UniqueId] instead
string value = Request[control.UniqueId];
return ConvertValue<ResultType>(value, defaultValue);
}
Solution 2: Check type dynamically
Downside: you have to provide handling for multiple control types
public string GetTextValue<ResultType>(WebControl control, ResultType defaultValue)
{
if (control is ITextControl)
return ConvertValue<ResultType>((ITextControl)control.Text, defaultValue);
else if (control is HiddenField)
return ConvertValue<ResultType>((HiddenField)control.Value, defaultValue);
else if (anothertype)
return ConvertValue<ResultType>(another_retrieval_method, defaultValue);
}
Solution 3: Use reflection
Downside: reflection can be tricky, doesn't look elegant, and can be slow on many invocations
public string GetTextValue<ResultType>(WebControl control, ResultType defaultValue)
{
// Get actual control type
Type controlType = control.GetType();
// Get the attribute which gives away value property
Attribute attr = controlType.GetCustomAttribute<ControlValuePropertyAttribute>();
if (attr == null)
throw new InvalidOperationException("Control must be decorated with ControlValuePropertyAttribute");
ControlValuePropertyAttribute valueAttr = (ControlValuePropertyAttribute)attr;
// Get property name holding the value
string propertyName = valueAttr.Name;
// Get PropertyInfo describing the property
PropertyInfo propertyInfo = controlType.GetProperty(propertyName);
// Get actual value from the control object
object val = propertyInfo.GetValue(control);
if (val == null)
val = "";
return ConvertValue<ResultType>(val, defaultValue);
}
I think DataItemContainer will be the closest thing you're looking for.

Label in ListViewDataItem setting property affects all labels

I have a listview called "lvQuestions" which has a label (called lblMissingField) on it and a couple of other fields. When the user tries to postback the page, I call a routine that first sets ALL lvlMissingField.Text = string.empty and then validates that "the other" fields in the ListViewDataItem have been filled in. If they haven't been filled I set the particular listview's lvlMissingField.Text = "*" to visually mark them with a "*" (Note: standard asp.net validators are not used).
So, what happens? Every lblMissingField.Visible is displayed, what am I missing here?
foreach(ListViewDataItem question in unansweredQuestions)
{
((Label)question.FindControl("lblMissingField")).Text = "*";
}
I've made that the unansweredQuestions are only those ListViewDataItems that I want, and still am suprised to see the output HTML sets all of them to "*"...?
I've also tried Setting Visible = true instead of setting the Text to '*' (which was my original preference) without any luck.
Going a little mad here...
When I have needed to do functionality similar to what you are doing and the updates / checks were done server side, I have implemented a DataBinding method for each control I want to customize. When something changes, I rebind to my model with the bits and pieces updated that need to be changed.
So for example in your case assume I am binding to an list of objects (could be DataTable or whatever) that has the following details:
public class MyData
{
public string Question { get; set; }
public string Answer { get; set; }
}
public List<MyData> listOfMyData = GetListOfMyData();
Then my databinding event on the label would be implemented
<asp:Label ID="lblMissingField" runat="server" OnDataBinding="lblMissingField_DataBinding" />
Then the implementation of the databinding:
protected void lblMissingField_DataBinding(object sender, System.EventArgs e)
{
Label lbl = (Label)(sender);
lbl.Text = (Eval("Answer").ToString() == string.Empty) ? "*" : string.Empty;
}
This gives you a lot of control on every single item in your template. Instead of dealing with all your controls after your data is loaded, this lets you have the data modified as the data is being bound.
I know it's not a solution to your exact question but maybe just a suggestion of another way to approach the problem.

Resources