In my maven-wicket (6.10) application I have a TextField, which has an Integer type property model. I want to set a maximum length for numberts to type into this TextField. (for example the user should write maximum 2 characters to the "age" text field)
I have tried this code:
add(new TextField<>("age",new PropertyModel<(personModel,"age"))
.add(StringValidator.maximumLength(2)));
//age is an Integer value from a Person class, personModel is "IModel<Person>" type
but I got this exception:
java.lang.ClassCastException: java.lang.Long cannot be cast to java.lang.String
at org.apache.wicket.validation.validator.StringValidator.getValue(StringValidator.java:87)
at org.apache.wicket.validation.validator.StringValidator.getValue(StringValidator.java:59)
at org.apache.wicket.validation.validator.AbstractRangeValidator.validate(AbstractRangeValidator.java:107)
at org.apache.wicket.markup.html.form.FormComponent.validateValidators(FormComponent.java:1523)
So if the property model is not string type, I cannot use StringValidator. I have found examples, which use NumberValidator (validators), but I cannot resolve NumberValidator. I have only these validators in the source:
How could I use number validator? Or am I missing something, maybe form the pom.xml's dependencies for wicket?
The problem you are facing is related to the way Wicket works. First it converts input text into a model object and then it performs validation.
Thus, you have to use RangeValidator instead of StringValidator
IModel<Integer> model =
new PropertyModel<Integer>(personModel, "age");
Component ageField = new TextField<Integer>("age", model);
add(ageField).add(RangeValidator.<Integer>range(0, 99));
Note I've changes Long to Integer as I believe saving age as Long is not practical.
Also note that link to NumberValidator is for Wicket 1.4, while you are using Wicket 6. Wicket 6 is a large API change comparing to previous versions.
For future reference please have a look at NumberTextField<N>
In Op's case, you could do more easily like:
IModel<Integer> model = new PropertyModel<Integer>(personModel, "age");
add(new NumberTextField<Integer>("age", model).setMinimum(0).setMaximum(99));
Related
I migrating a Vaadin 8 project to Vaadin 14 and i try to show HTML in a grid column.
I figured out, that i have to use a TemplateRenderer, but how can i use it?
Here is the code from Vaadin 8:
grid.addColumn(e -> {
return ((Data) e).getValues()[index];
}).setCaption(myCaption).setRenderer(new HtmlRenderer());
In Vaadin 14 i did this:
gird.addColumn(e -> {
return TemplateRenderer.<Data>of((String) e.getValues()[index])
}).setHeader(myCaption);
e.getValues()[index] includes HTML, for example: <FONT SIZE = 4 COLOR = BLACK> ⚫</FONT>
In Vaadin 14 it always returns com.vaadin.flow.data.renderer.
Before we get to how to use a TemplateRenderer with Grid, I first need to point out that what you're trying to do is potentially dangerous because of the way it can lead to XSS vulnerabilities if the HTML strings that you want to show may be supplied by application users.
Using the Html component is indeed one potential solution to this problem, but it causes some overhead because there will be one component instance in memory for each row in the grid. There's also the same problem with potentially causing XSS vulnerabilities.
The first thing to notice with TemplateRenderer is that the renderer needs to be supplied directly as a parameter to addColumn. Wrapping it in a lambda will instead use that lambda as a value provider, which means that the toString() value of the renderer instance will be used with the default plain text renderer.
All rows should use the same renderer instance, configured with the same template string. The trick is that you can pass the data to show as a per-row property that the template will render for you. The last piece of the puzzle is that the template syntax tries to protect you against accidental XSS vulnerabilities, so you need to use a slightly contrived syntax to actually make it render the data as HTML.
Putting everything together, and also using JSoup to remove any dangerous stuff from your HTML strings, the working solution looks like this:
grid.addColumn(TemplateRenderer
.<Data> of("<div inner-h-t-m-l='[[item.html]]'></div>")
.withProperty("html", e -> {
String unsafeHtml = e.getValues()[index];
String safeHtml = Jsoup.clean(unsafeHtml, Whitelist.basic());
return safeHtml;
})).setHeader(myCaption);
I found a solution.
Instead of using the TemplateRenderer I used a ComponentRenderer.
The migration documentation recomented to use a TempleteRenderer or an ComponentRenderer instead of the htmlRenderer.
https://vaadin.com/docs/v14/flow/migration/8-migration-example.html#step-4-product-grid
Here is the code that worked for me:
grid.addColumn(new ComponentRenderer<>(e -> {
String value = (String) e.getValues()[index];
return new Html(value);
})).setHeader(String.valueOf(col + 1));
Comparing your attempts with TemplateRenderer and the documentation, I would assume it will have to look like this:
grid.addColumn(e ->
TemplateRenderer.<Data>of("[[item.customValue]]")
.withProperty("customValue", (String) e.getValues()[index])
).setHeader(myCaption);
While working on an in-line editing feature in Vaadin Grid (8.1.0), I have created the beans and use setItems method from the Grid to populate all rows.
But when I double clicked a row to edit it, an exception came up. I thought I have bound the bean's property type correctly with the TextField but it still throws exception.
The following is my manual binding code that finds a Boolean property to the Textfield I like to use for editing.
Binder<RegistrationRecord> needFancialFlagBinder = new Binder<>(RegistrationRecord.class);
needFancialFlagBinder .forField ( needFancialFlagField )
.withNullRepresentation( "" )
.withConverter (new StringToBooleanConverter("Need financial flag must be true or false!"))
.bind ( RegistrationRecord:: isNeedFancialFlag, RegistrationRecord:: setNeedFancialFlag);
The following code attaches the TextField with the column in the Grid.
registrationGrid.getColumn("needFancialFlag")
.setEditorComponent(needFancialFlagField)
.setExpandRatio(1);
Below is part of the exception. Does the StringToBooleanConverter only take care of converting from String to Boolean and not the other way around? What method should I be using for the other direction?
java.lang.ClassCastException: java.lang.Boolean cannot be cast to java.lang.String
at com.vaadin.ui.AbstractTextField.setValue(AbstractTextField.java:47) ~[vaadin-server-8.1.0.jar:8.1.0]
at com.vaadin.data.Binder$BindingImpl.initFieldValue(Binder.java:893) ~[vaadin-server-8.1.0.jar:8.1.0]
at com.vaadin.data.Binder$BindingImpl.access$100(Binder.java:766) ~[vaadin-server-8.1.0.jar:8.1.0]
at com.vaadin.data.Binder.lambda$readBean$2(Binder.java:1386) ~[vaadin-server-8.1.0.jar:8.1.0]
at java.lang.Iterable.forEach(Iterable.java:75) ~[na:1.8.0_121]
So, the question was asked inadequately. I wanted to use a TextField to edit a Boolean model member in a column of a Grid and somewhere in the process, a better idea was proposed. Instead of using a TextField, I should have used a CheckBox. So, the solution got turned around, and below is the correct code. Note this is Vaadin 8.1.0. (I found Vaadin has changed a lot of versions quickly.)
private void addBooleanPropertyColumn(Grid theGrid, String propertyName, String caption) {
CheckBox bBox = new CheckBox();
Column<RegistrationRecord, String> adultFlagColumn = theGrid.addColumn(record->
"<span class=\"v-checkbox v-widget\"><input type=\"checkbox\" id=\"my-uid-1\" " + returnChecked(propertyName, record) + " > <label for=\"my-uid-1\"></label> </span>",
new HtmlRenderer());
adultFlagColumn.setId(propertyName)
.setCaption(caption)
.setEditorComponent(bBox)
.setExpandRatio(1);
}
The concept is that a Column in a Grid supports the association of a Renderer, a Validator, and an Editor. Once you have learned that, it becomes straightforward.
I am not using a Validator since the model field is only a Boolean. And it is worth noting the editor component must have an associated property, hence the setID, a convenient setter.
I have a BDP function that looks like this.
BDP("Glen Ln Equity","NAME_CHINESE_SIMPLIFIED")
It is to update the Chinese name of a security.
I have to translate it to Java blpapi but I am not sure how.
Since this is a BDP function, I think I should use Reference Data Request but you can only specify the ticker and field mnemonic when creating a Reference Data Request. I also know I can use override but to use an override, based on my understanding, I will need a fieldID so that I can set that fieldID's value to be "NAME_CHINESE_SIMPLIFIED".
However, I am not sure what fieldID to use.
What fieldID should I use for the override?
Also, where can I find a list of fieldIDs that can set for overrides?
It should work fine with a reference data request - you don't need to override anything here:
Element security = request.getElement("securities");
security.appendValue("GLEN LN Equity");
Element field = request.getElement("fields");
field.appendValue("NAME_CHINESE_SIMPLIFIED ");
Was wondering why the constraints created for a form are not included in the input tag directly when created through the form helper?
Explanation (using Play 2.1):
Model:
public class Account {
#MaxLength(5)
private String id = "";
...
...
view:
#form(action = routes.Application.addAccount()) {
#inputText(accountForm("id"), '_label -> "Enter your id:")
}
renders automatically in html as:
Enter your id:
Maximum length: 5
Should it not render like this (actually constraining the form text field):
Enter your id:
Maximum length: 5
How can I get code that will automatically include constraints such as these in the form? It's just that I do not really think it is a good idea to have a maxlength defined in the form model and a separate one defined in the view.
Thanks
If I've understood you correctly, it sounds like you're looking to implement one of these features:
Highlight an input text field that is overlength before form submission
Clipping text in an input field so that it does not go overlength
Play's HTML templating engine doesn't natively provide this kind of client-side instant form validation. This functionality needs to be implemented via JavaScript, and JavaScript generation is not really a concern for Play.
If you want to progressively enhance your form and provide client-side validation, you'll have to write the JavaScript yourself. Of course there are libraries that you can use to help you with this task. For example, if you are already using jQuery you can use its validation plugin.
As you've mentioned in your question, it would be better to have a maximum length limit declared in one place only, rather than duplicated in your client-side JavaScript code and your server-side Java code. As a suggestion, you could keep the limit declared in Java code, but introduce a new action in your controller tier that returns a JSON response containing this limit. This action could then be called via AJAX when loading your form page.
EDIT
Didn't know about the maxlength attribute, thanks Saad. If you feed in your maximum length limit as an input parameter to your template, you can populate an input element's maxlength attribute as follows:
#(accountForm: Form[Account], maxLength: Int)
...
#form(action = routes.Application.addAccount()) {
...
#inputText(
field = accountForm("id"),
args = '_label -> "Enter your id:", 'maxlength -> maxLength
)
...
}
...
There may be a more elegant way to pass maxLength into your HTML template (e.g use the HTTP context map, or have it as a public field on your Account form object). The above code snippet just demonstrates how to correctly generate the input text field once you can access it in the template.
Wicket has a flexible internationalisation system that supports parameterising UI messages in many ways. There are examples e.g. in StringResourceModel javadocs, such as this:
WeatherStation ws = new WeatherStation();
add(new Label("weatherMessage", new StringResourceModel(
"weather.${currentStatus}", this, new Model<String>(ws)));
But I want something really simple, and couldn't find a good example of that.
Consider this kind of UI message in a .properties file:
msg=Value is {0}
Specifically, I wouldn't want to create a model object (with getters for the values to be replaced; like WeatherStation in the above example) only for this purpose. That's just overkill if I already have the values in local variables, and there is otherwise no need for such object.
Here's a stupid "brute force" way to replace the {0} with the right value:
String value = ... // contains the dynamic value to use
add(new Label("message", getString("msg").replaceAll("\\{0\\}", value)));
Is there a clean, more Wicket-y way to do this (that isn't awfully much longer than the above)?
Take a look at Example 4 in the StringResourceModel javadoc - you can pass a null model and explicit parameters:
add(new Label("message",
new StringResourceModel(
"msg", this, null, value)));
msg=Value is {0}
I think the most consistent WICKETY way could be accomplished by improving Jonik's answer with MessageFormat:
.properties:
msg=Saving record {0} with value {1}
.java:
add(new Label("label", MessageFormat.format(getString("msg"),obj1,obj2)));
//or
info(MessageFormat.format(getString("msg"),obj1,obj2));
Why I like it:
Clean, simple solution
Uses plain Java and nothing else
You can replace as many values as you want
Work with labels, info(), validation, etc.
It's not completely wickety but it is consistent with wicket so you may reuse these properties with StringResourceModel.
Notes:
if you want to use Models you simply need to create a simple model that override toString function of the model like this:
abstract class MyModel extends AbstractReadOnlyModel{
#Override
public String toString()
{
if(getObject()==null)return "";
return getObject().toString();
}
}
and pass it as MessageFormat argument.
I don't know why Wicket does not support Model in feedback message. but if it was supported there was no reason to use these solutions and you could use StringResourceModel everywhere.
There's a way, which although still involves creating a model, doesn't requires a bean with a getter.
given this message in a properties file:
msg=${} persons
Here's how to replace the placeholder with a value, be it a local variable, a field or a literal:
add(new Label("label", new StringResourceModel("msg", new Model<Serializable>(5))));
When faced with something like described in the question, I would now use:
.properties:
msg=Saving record %s with value %d
Java:
add(new Label("label", String.format(getString("msg"), record, value)));
Why I like it:
Clean, simple solution
Uses plain Java and nothing else
You can replace as many values as you want (unlike with the ${} trick). Edit: well, if you actually need to support many languages where the replaced values might be in different order, String.format() is no good. Instead, using MessageFormat is a similar approach that properly supports this.
Disclaimer: this is "too obvious", but it's simpler than the other solutions (and definitely nicer than my original replaceAll() hack). I originally sought for a "Wicket-y" way, while this kinda bypasses Wicket—then again, who cares? :-)
In case you have a Model in your Component which holds an object with values you want to access from your placeholders as substitutions, you can write:
new StringResourceModel("salutation.text", getModel());
Let's imagine getModel()'s return type is IModel<User> and User contains fields like firstName and lastName. In this case you can easily access firstName and lastName fields inside your property string:
salutation.text=Hej ${firstName} ${lastName}, have a nice day!
Further information you can find here: https://ci.apache.org/projects/wicket/apidocs/8.x/org/apache/wicket/model/StringResourceModel.html#StringResourceModel-java.lang.String-org.apache.wicket.model.IModel-
Creating a Model for your Label really is The Wicket Way. That said, you can make it easy on yourself with the occasional utility function. Here's one I use:
/**
* Creates a resource-based label with fixed arguments that will never change. Arguments are wrapped inside of a
* ConvertingModel to provide for automatic conversion and translation, if applicable.
*
* #param The component id
* #param resourceKey The StringResourceModel resource key to use
* #param component The component from which the resourceKey should be resolved
* #param args The values to use for StringResourceModel property substitutions ({0}, {1}, ...).
* #return the new static label
*/
public static Label staticResourceLabel(String id, String resourceKey, Component component, Serializable... args) {
#SuppressWarnings("unchecked")
ConvertingModel<Serializable>[] models = new ConvertingModel[args.length];
for ( int i = 0; i < args.length; i++ ) {
models[i] = new ConvertingModel<Serializable>( new Model<Serializable>( args[i] ), component );
}
return new CustomLabel( id, new StringResourceModel( resourceKey, component, null, models ) );
}
Details I'm glossing over here are:
I've created my own ConvertingModel which will automatically convert objects to their String representation based on the IConverters available to the given component
I've created my own CustomLabel that applies custom label text post-processing (as detailed in this answer)
With a custom IConverter for, say, a Temperature object, you could have something like:
Properties key:
temperature=The current temperature is ${0}.
Page.java code:
// Simpler version of method where wicket:id and resourceKey are the same
add( staticResourceLabel( "temperature", new Temperature(5, CELSIUS) ) );
Page.html:
<span wicket:id='temperature'>The current temperature is 5 degrees Celsius.</span>
The downside to this approach is that you no longer have direct access to the Label class, you can't subclass it to override isVisible() or things like that. But for my purposes it works 99% of the time.