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.
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);
I should have asked this question way earlier but now I am really tired of dodging around this problem:
I have a normal datatable like
<p:dataTable id="dt1" var="tVar" value="#{mrBean.queriedElements}" filteredValue="#{mrBean.filteredElements}" ...
Now in addition to the primefaces filters, I made my own panelGrid in which you can apply filters to the data base which work before any PF action.
The following lists exist: queriedElements which holds all the data that is returned after my personal filter applied and filteredElements which is needed for primefaces datatable filtering. In addition, I am not exactly sure whether I need an element list that represents all the data from the database. If no personal filter is applied, queriedElements = allElements.
The datatable displays a lot of information on the objects contained and you can change these objects via a dialog. I want the following:
When saved, update all changes made to the selectedElement
When cancelled, revert all changes in the datatable (I dont use a temporary object that is edited but the very object from the list)
When closing the dialog, remember all filters and paginator position
What is the best practise to do so and how can I avoid redundant code for queriedElements and filteredElements (in case I must iterate through it to change it explicitly in addition to database merges)? I found the first attribute for pagination, but I'm not really sure how to use it properly combined with my other requirements. My main problem is that the datatable almost never displays the right values if I don't refetch from database.
PF 4.0
I don't know why PF 4.0 doesn't do this by itself, but something that worked for me can be found at http://www.brainhemorage.com/?p=258
Although I had to replaced the lines
ValueExpression filterBy = column.getValueExpression("filterBy");
String filterField = null;
if (filterBy != null)
filterField = table.resolveStaticField(filterBy);
String filterId = column.getContainerClientId(context) + separator + "filter";
String filterValue = params.containsKey(filterId) && !table.isReset() ? params.get(filterId) :
table.getFilters().get(filterField); // <-- and here, was ""
String filterStyleClass = column.getFilterStyleClass();
by
String filterId = column.getContainerClientId(context) + separator + "filter";
String filterValue = params.containsKey(filterId) && !table.isReset() ? params.get(filterId) :
table.getFilters().get(column.getFilterBy());
because getValueExpression always returned null.
Although this doesn't answer my question about the BP, this will surely help others with the filteredValue problem.
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));
This question already has answers here:
how to find source component that generated a DocumentEvent
(3 answers)
Closed 9 years ago.
I have my class and I have implemented DocumentListener
public void removeUpdate( DocumentEvent arg0 ) {
System.out.println( arg0.getDocument());
}
It would print javax.swing.text.PlainDocument#49ea903c
Is there any possible way I would get the object so I can get the value of the changed textfield? At the moment I have only one field so I do not need a check, but what if I use two or more, how do I know which JTextField has notified the listener?
I'm not sure it's possible to get the swing component from a Document. But the issue is easily solved: just add a different instance of the listener to every text field, and store the text field in the listener itself.
textField1.getDocument().addDocumentListener(new MyDocumentListener(textField1));
textField2.getDocument().addDocumentListener(new MyDocumentListener(textField2));
textField3.getDocument().addDocumentListener(new MyDocumentListener(textField3));
One option is to use an inner class, which will provide you an opportunity to reference the text field.
final JTextField field = new JTextField();
field.getDocument().addDocumentListener(new DocumentListener() {
// Here you can reference 'field' in your methods
});
If you need to perform the same action for each text field, JB Nizet's solution will be neater.
You actually are getting the PlainDocument object. Just store it in a variable instead of printing it.
For more info see docs.
What you need to do is adding document listners to each component you need.If you really need to know which text fields text has changed then you can have a property in the DocumentListner as textFieldName or something and you can set it when you create the document listner for them.But I think you better change your approaching to situation.It doesn't sound good.
You may update the document without knowing the context.
Consult the documentation of javax.swing.event.DocumentEvent
javax.swing.event.DocumentEvent.getOffset() Returns the offset within the document of the start of the change.
javax.swing.event.DocumentEvent.getLength() Returns the length of the change.
These methods combined with the document retrieved by javax.swing.event.DocumentEvent.getDocument() allow you to update the document in a proper way. Later you may add other text field without any change.
To simply get the text of a JTextField after change:
JTextField myTf = new ...; // maybe an attribute definition
...
myTf.addActionListener(
new ActionListener(){#Override public void actionPerformed( ActionEvent e ) {
JTextField tf = (JTextField)e.getSource();
System.out.println( tf.getText());
if( tf == myTf ) { // == for reference comparison
... // do something dedicated to myTf
}
}});
I am trying to bind the visibility of a JLabel to whether the text of a JTextField is empty or not.
I want to do this because I want to hide the JLabel with a red asterisk, which denotes that filling in a text field is compulsory, so it should hide when it is filled in.
The following does however not work (with ${text.isEmpty}):
binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(
org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE,
estimatedCostTextField,
org.jdesktop.beansbinding.ELProperty.create("${text.isEmpty}"),
estimatedCostAsterisk,
org.jdesktop.beansbinding.BeanProperty.create("visible"));
bindingGroup.addBinding(binding);
Can anybody help me with this?
I found the answer. You should use ${empty text}
So the code becomes:
binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(
org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ,
estimatedCostTextField,
org.jdesktop.beansbinding.ELProperty.create("${empty text}"),
estimatedCostAsterisk,
org.jdesktop.beansbinding.BeanProperty.create("visible"));
bindingGroup.addBinding(binding);