Java vaadin table fields customization - java

I have the following properties to display in the Vaadin Table
public class Item
{
String itemName;
String itemSource;
String itemStatus;
...
}
Next, I retrieve my data using the ItemContainer, and connect it to the table:
ItemContainer container = new ItemContainer(Item.class, app);
table.setContainerDataSource(container);
The table will display the data based on Item class.
The question: can I change "somehow" the datatype of the fields on the "fly" ? So I want change the "itemStatus" to be Label since display HTML tags in correct styles.
For example the text in "itemStatus" is: <b>Status is:</b><i>Completed</i>. I want to see in the table formatted string Status is:Completed

You can override a container property in Table by adding a generated column with the same id:
tablet.addGeneratedColumn("itemStatus", new ColumnGenerator() {
#Override
public Object generateCell(Table source, Object itemId, Object columnId) {
Label label = new Label("" + source.getContainerProperty(itemId, columnId).getValue());
label.setContentMode(ContentMode.HTML);
label.setSizeUndefined();
return label;
}
});

Related

Set Value to ComboBoxItem smartgwt

In drop down I get like this. find image attached. Actually in coulmn of "Name" field both 'Name' and 'Description' are displaying as comma(,) separated.
final ComboBoxItem comboBoxItem = new ComboBoxItem("attributeTypeId","Attr. Type");
ListGridField nameField = new ListGridField("name", "Name");
ListGridField descField = new ListGridField("description","Description");
descField.setShowHover(true);
comboBoxItem.setPickListFields(nameField, descField);
comboBoxItem.setPickListWidth(200);
comboBoxItem.setFilterLocally(true);
comboBoxItem.setColSpan(2);
comboBoxItem.setAddUnknownValues(false);
comboBoxItem.setValueField(FieldNames.ID_FIELD);
comboBoxItem.setDisplayField(FieldNames.NAME_FIELD);
comboBoxItem.setAutoFetchData(true);
OptionListDataSource attrTypeds = OptionListDataSource.getInstance(FieldNames.ATTRIBUTE_TYPE_FIELD);
attrTypeds.fetchData(null, new DSCallback() {
#Override
public void execute(final DSResponse response, final Object rawData, final DSRequest request) {
Record[] recList = response.getData();
LinkedHashMap<String, String[]> dataLinkMap = new inkedHashMap<String,String[]>(); //LinkedHashMap<String,
dataLinkMap.put("0", new String[]{"Select",""});
for (Record record : recList) {
String attrId = record.getAttribute(FieldNames.ID_FIELD);
String attrName = record.getAttribute(FieldNames.NAME_FIELD);
String attrDesc = record.getAttribute(FieldNames.DESCRIPTION_FIELD);
dataLinkMap.put(attrId, new String[]{attrName,attrDesc});
}
comboBoxItem.setValueMap(dataLinkMap);
}
});
Screen Shot
Here is some sample code to achieve what I understand you want to achieve:
public class TestCases implements EntryPoint {
public void onModuleLoad() {
DataSource logDS = DataSource.get("yourDSName");
final DynamicForm form = new DynamicForm();
form.setWidth(550);
form.setNumCols(2);
ListGridField nameField = new ListGridField(FieldNames.NAME_FIELD);
ListGridField descriptionField = new ListGridField(FieldNames.NAME_DESCRIPTION);
LinkedHashMap<String,String> hashMap = new LinkedHashMap<String,String>();
hashMap.put("-1", "Select");
ComboBoxItem myItem = new ComboBoxItem();
myItem.setTitle("ComboBox");
myItem.setOptionDataSource(logDS);
myItem.setDisplayField("category");
myItem.setValueField(FieldNames.ID_FIELD);
myItem.setSpecialValues(hashMap);
myItem.setPickListWidth(300);
myItem.setPickListFields(nameField, descriptionField);
form.setItems(myItem);
form.draw();
}
}
Notice:
In order to display various fields, you need to use setPickListFields with the reference to those fields.
You don't need to call fetch() on the DataSource itself. This is done automatically for you when you use DataBound components like ComoBoxItem.
You can add additional empty values using setSpecialValues() without modifying your DSResponse data (which is why you don't need to use fetch() directly).
EDIT
The problem you are having is that the ValueMap, which is just a Map (in other words, just a group of key/value pairs), that you are providing to the ComboBoxItem is not the same as the Record[] object provided directly by the DataSource, which in essence is just a List made of several Maps, each representing a field name and its value. This way, besides the value field, you can provide several fields to the ComboBoxItem for display purposes, like Name and Description, in your particular case.
From looking at the API, it looks to me that you can't provide a Record[] manually to the ComboBoxItem, so either you get the data via DMI (which for me is the easiest) or other method that allows you to modify and return the required response from the server automatically to the ComboBoxItem by using the data binding capabilities, or you stick to showing just the "values" (which is what you are getting right now, but off course you could format the data better).
What I mean with formatting is that if you choose to go with your original approach of using setValueMap(), you need to provide a Map where each entry in the Map is just a value on the ComboBoxItem and its respective display "text", which can be any String combining the values of several other fields, and formatted as desired using String concatenation (for instance, you could make it
nameField + ": " + descriptionField
But this is as good as it gets with this approach.
Now, via a DMI you would need to define the server class that would provide the properly-formatted data in you datasource descriptor (ds.xml file):
<operationBindings>
<operationBinding operationType="fetch" serverMethod="fetchComboBoxData">
<serverObject lookupStyle="new" className="com.myApp.ComboBoxDMI"/>
</operationBinding>
</operationBindings>
And then create the class and method to provide what you need:
public class ComboBoxDMI {
public DSResponse fetchComboBoxData(DSRequest dsRequest) throws Exception {
DSResponse response = dsRequest.execute();
if (response.statusIsSuccess()) {
#SuppressWarnings("unchecked")
List<Map<String, Object>> recList = response.getRecords();
List<Map<String, Object>> comboBoxList = new ArrayList<Map<String,Object>>();
// Add here the new record... for each field in your DataSource, you need to set a Map
// with the key being the field name and the value being the field value. So you need
// 1 Map entry per field. All your Map entries form 1 record, and that's what you add
// to your List of Maps
return constructDSResponse(comboBoxList);
}
return response;
}
private DSResponse constructDSResponse(List<Map<String, Object>> comboBoxList) {
DSResponse response = new DSResponse();
int totalRows = comboBoxList.size();
response.setStartRow(totalRows > 0 ? 1 : 0);
response.setEndRow(totalRows);
response.setTotalRows(totalRows);
response.setData(comboBoxList);
return response;
}
}
Finally, you can follow the original approach I suggest in my original answer, but now you don't need to use the setSpecialValues API, which your version doesn't support.

Vaadin table bound to SQLContainer, how to show field type as link

I use Vaadin's Table and set data using SQLContainer:
resultTable.setContainerDataSource(mySQLContainer);
resultTable.setColumnHeader("internalid", "Internal Id");
Now, I want to make this field as a Link in table column.
Since I am setting all the fields directly via SQLCOntainer, I am not sure how to change appearance for a single field from that.
Can anyone help?
The best way for showing column values as links like this is by registering a ColumnGenerator for that column by calling Table.addGeneratedColumn
The generator can itself access the backing container properties and return instances of com.vaadin.ui.Link. For the column ID, you can use the existing container id to "shadow" the container column.
You can use the TableFieldFactory to provide your own type mapping.
https://vaadin.com/api/com/vaadin/ui/TableFieldFactory.html
Just specify your own class on the table and it should be OK.
Look also here: Vaadin 7 : TableFieldFactory
A example with inline class:
// Set a custom field factory that overrides the default factory
table.setTableFieldFactory(new DefaultFieldFactory() {
private static final long serialVersionUID = 8585461394836108250L;
#Override
public Field createField(Container container, Object itemId,
Object propertyId, Component uiContext) {
// Create fields by their class
Class<?> cls = container.getType(propertyId);
// Create a DateField with year resolution for dates
if (cls.equals(Date.class)) {
DateField df = new DateField();
df.setResolution(DateField.RESOLUTION_YEAR);
return df;
}
// Create a CheckBox for Boolean fields
if (cls.equals(Boolean.class))
return new CheckBox();
// Otherwise use the default field factory
return super.createField(container, itemId, propertyId,
uiContext);
}
});

how to access two different java entities as parameters in backing bean method

Mojarra 2.1.5 / Java
I have two entities : i.e.
class Primary { String name; String age }
class Second { String dept; String hour }
...
In my managed bean I developed a function to generate PDF regarding my front-end prime-faces radio button (Primary or Second).
If I select in radio button the Primary option, the managed bean method will fire generatePDF() and inside the generatePDF I have :
Primary pr = new Primary();
pr.name = xxxxx;
pr.age = yyyyy;
...
...
But how can I do to re-utilize the same method generatePDF for both entities (Primary and Second ? I need to access both entity properties regarding my radio selection.
I need to instantiate the entities dynamically (Or I instantiate Primary or I intantiaty Second at a time)
What about do something like this.
interface Pdfeable{ String writeToPDF();}
class Primary implements Pdfeable { String name; String age }
class Second impleaments Pdfeable { String dept; String hour }
Just override with the statements you want to send data to the PDF.
class Primary implements Pdfeable {
String name; String age;
public String writeToPDF(){
return getName() + "" + getAge();
}
}
And write your code using the interface definition not concrete classes.
Assuming as per your question you need one class instantiation at a time as per radio button selection ,I would suggest you should create a valueChangeListener to your radio button.I have hardcoded the values of radio selectItem you can use any way either by bean-binding or hardcoded.
<h:selectOneRadio value="#{myBean.myRadioValue}" ... onclick="this.form.submit();" valueChangeListener="#{myBean.generatePDF}">
<f:selectItem itemValue="Radio1" itemLabel="Primary-radio" />
<f:selectItem itemValue="Radio2" itemLabel="Secondary-radio" />
</h:selectOneRadio>
This code will submit the form where the radio button are contained when the onclick Javascript event is detected. On the server side, the action generatePDF will be executed. In this method, you can do your requiste as on Submit action getter and setter will be called and you can check which radio is selected by comparing using if () and do your stuff:
public void generatePDF(ValueChangeEvent evt) {
if(getMyRadioValue.equals(Radio1)){
Primary pr = new Primary();
pr.name = xxxxx;
pr.age = yyyyy;
}
else if(getMyRadioValue.equals(Radio2)){
Secondary s = new Secondary();
s.dept = xxx;
s.hour = yyyy;}
...
...
}

Wicket - Generating Dynamic Table - ChildId warnings

I'm trying to create a dynamic table component in wicket to show a list of Objects. The component will receive a List of Objects and render it in a table form.
Assuming the following Product entity:
public class Product {
public static final long serialVersionUID = 1L;
//
private String id;
private String name;
private String description;
private Double price;
// getters and setters omitted for brevity
}
The following html/code pair will work:
(Note: Product entity will later be genericized so we can pass it a list of any POJO)
<table>
<tbody>
<tr wicket:id="row">
<td wicket:id="cell">
cell
</td>
</tr>
</tbody>
</table>
----------------------------------------------------------------
parameters:
final String[] fieldNames = new String[]{"id", "name", "description", "price"};
List<Product> productList = ....
----------------------------------------------------------------
ListView lvRows = new ListView("row", productList) {
#Override
protected void populateItem(ListItem item) {
Product product = (Product)item.getModelObject();
CompoundPropertyModel cpm = new CompoundPropertyModel(product);
//
RepeatingView cell = new RepeatingView("cell", cpm);
item.add(cell);
//
for (String fn : fieldNames) {
Label label = new Label(fn);
cell.add(label);
}
}
};
this.add(lvRows);
The problem is that the above code will result in a bunch of warnings (one for each Product in the list):
00:57:52.339 [http-apr-8080-exec-108] WARN
o.a.w.m.repeater.AbstractRepeater - Child component of repeater
org.apache.wicket.markup.repeater.RepeatingView:cell has a non-safe
child id of id. Safe child ids must be composed of digits only.
So my questions are:
am i doing it wrong?
if not, how do i get rid of the warnings?
Why does wicket require numeric child id in this instance? CompoundPropertyModel works fine in other situations, with the id linked to object attributes...
if i'm doing it wrong, what's the "proper" way to do it? Should i create the child ids uniquely, forfeit using CompoundPropertyModel, and feed the values directly via reflection? Not sure about the performance impact though, doing a reflection call for each cell can't be cheap. Something like this:
for(String fn:fieldNames} {
String s = ...; //find the value of Object O, property fn via Reflection
Label label = new Label(cell.newChildId(), s);
cell.add(label);
}
Thanks in advance.
You can avoid the warnings by adding an intermediate child in the RepeatingView, as explained by Igor Vaynberg in this post at the Wicket users list.
In other words, don't add the Labels with the non-numeric ids directly to the repeater, add a container with a numeric id instead, and add the Labels to the container:
RepeatingView cell = new RepeatingView("cell");
WebMarkupContainer container = new WebMarkupContainer(cell.newChildId(), cpm);
for (String fn : fieldNames) {
Label label = new Label(fn);
container.add(label);
}
cell.add(container);
item.add(cell);
The reasons for those warnings are also outlined in that post.
EDIT:
It seems that you'd need to model a ProductPanel and use it inside the RepeatingView instead of just a WebMarkupContainer. This means that the ProductPanel would probbaly have to explicitly enumerate properties (or wicket:ids) in its HTML.
You could also keep your current code and just do Label label = new Label(cell.newChildId(), new PropertyModel(product, fn)); and drop the CPM, if you want the HTML to be independent from the specific properties.

GWT DataGrid Headers, filterable and sortable

I am trying to extends GWT's DataGrid capabilities for my own project and would like to add the ability to filer columns. I have successfully rendered a filter box in the Header, but it is not responding to events.
Following is the relvant part of my code, which has been adapted from the code given here: CellTable with custom Header containing SearchBox and Focus Problem
The question above does not quite fit my needs, as it does not work if the columns are sortable.
Instead, I have developed a header consisted of 2 table rows (TR's), the top row containing filter boxes, the 2nd row containing column titles and responding to Sort events. The Sort events work OK, but the filter boxes to not respond to any events. Here's the code:
class HeaderBuilder extends AbstractHeaderOrFooterBuilder<Record> {
//HTML to render an Input Box
private InputBoxHTML inputBox = GWT.create(InputBoxHTML.class);
//List of columns in the table
private List<ListGridColumn<?>> columns = new ArrayList<ListGridColumn<?>>();
//Constructor. ListGrid is the outer class extending DataGrid
private HeaderBuilder() {
super(ListGrid.this, false);
}
#Override
protected boolean buildHeaderOrFooterImpl() {
TableRowBuilder tr = startRow();
tr.startTH().endTH(); //extra column
//Create top row of column headers - filter boxes for filterable columns, empty cells for non-filerable
for (ListGridColumn<?> column : this.columns) {
TableCellBuilder th = tr.startTH();
Header<String> header;
//If this column is filterable...
if (column.filter) {
//Create a new Cell containing an Input Box
AbstractCell<String> cell = new AbstractCell<String>("click","keydown","keyup") {
public void render(Context context, String value, SafeHtmlBuilder sb) {
sb.append(inputBox.input(""));
}
public void onBrowserEvent(Context context, Element parent, String value, NativeEvent event, ValueUpdater<String> valueUpdater) {
//These events never fire!
Window.alert("event");
}
};
header = new Header<String>(cell) {
public String getValue() {
return "value";
}
};
} else {
//Empty cell for non-filterable columns
header = new TextHeader("");
}
Context context = new Context(0, 0, header.getKey());
renderHeader(th, context, header);
th.endTH();
}
tr.endTR();
//Bottom row : header captions & sorting. This all works OK
tr = startRow();
tr.startTH().endTH(); //extra column
for (ListGridColumn<?> column : this.columns) {
TableCellBuilder th = tr.startTH();
enableColumnHandlers(th, column);
Header<String> header = new TextHeader(column.headerStr);
Context context = new Context(0, 0, header.getKey());
if (column.sortKey!=null) {
this.renderSortableHeader(th, context, header, true, true);
} else {
this.renderHeader(th, context, header);
}
th.endTH();
}
tr.endTR();
return true;
}
}
If you looks in the source code of insertColumn(int beforeIndex, Column col, Header header, Header footer) method in AbstractCellTable class (which is extended by DataGrid and CellTable), when a column is inserted (or added) all the events for the header (cell or footer) are sinked in order to propagate it from the table to the corresponding cell:
if (header != null) {
Set<String> headerEvents = header.getCell().getConsumedEvents();
if (headerEvents != null) {
consumedEvents.addAll(headerEvents);
}
}
...
CellBasedWidgetImpl.get().sinkEvents(this, consumedEvents);
You're declaring the Headers in the builder but not "registering" it, therefore events are not propagated to the header cell. You should find a way to register it. I don't see any clean solution because DataGrid can not be easily extended.
I can purpose you two dirty ones:
Create your version of DataGrid (you need to copy and paste the code and declared it in the same package of DataGrid) and modify it in order to register two header for column.
Create a new Header capable of propagate the events to the correct instance of the two headers in the column.
I will go for 2, creating an header with two cell inside, you can use these two cell in the builder.

Categories