I am looking for component for tree representation of table field.
What I am looking for is table with columns but with option with collapsing of cell like tree.
There is no built in Tree Table in Scout, but it is possible to make misuse the Table to make it look like a tree table. I just proposed an implementation in this Gist.
AbstractTreeTable
is a table template that adds 2 columns in the table:
KeyColumn
: a primary key for the tableParentKeyColumn
: the key of the parent row or null if the row is at the tree root.AbstractTreeTable
handles the collapsed state or not of each node row. It also decorates the first column (indentation and [+]
&[-]
marker on the nodes). It handle the row action (execRowAction(..)
).
If you use this in a table page, it is recommended to use a TablePageData and not Object[][]
. If you use this in a table fields, it only works if the table field is using a bean based table data.
My prototype could be improved:
@Order(10.0)
@FormData(value = AbstractTableFieldBeanData.class, sdkCommand = SdkCommand.USE, defaultSubtypeSdkCommand = DefaultSubtypeSdkCommand.CREATE)
public class CarsTableField extends AbstractTableField<CarsTableField.Table> {
@Override
protected int getConfiguredGridH() {
return 8;
}
@Override
protected int getConfiguredGridW() {
return 2;
}
@Override
protected String getConfiguredLabel() {
return TEXTS.get("Cars");
}
@Order(10.0)
public class Table extends AbstractTreeTable {
/**
* @return the PriceFromColumn
*/
public PriceFromColumn getPriceFromColumn() {
return getColumnSet().getColumnByClass(PriceFromColumn.class);
}
/**
* @return the NameColumn
*/
public NameColumn getNameColumn() {
return getColumnSet().getColumnByClass(NameColumn.class);
}
@Override
protected boolean execIsNode(ITableRow row) {
return getParentKeyColumn().getValue(row) == null;
}
@Override
protected void execDecorateRow(ITableRow row) throws ProcessingException {
if (execIsNode(row)) {
row.setFont(FontSpec.parse("BOLD"));
}
}
@Override
public void importFromTableBeanData(AbstractTableFieldBeanData source) throws ProcessingException {
super.importFromTableBeanData(source);
toggleExpandedState(getRows());
}
@Order(10.0)
public class NameColumn extends AbstractStringColumn {
@Override
protected String getConfiguredHeaderText() {
return TEXTS.get("CarModel");
}
}
@Order(20.0)
public class PriceFromColumn extends AbstractIntegerColumn {
@Override
protected String getConfiguredHeaderText() {
return TEXTS.get("PriceFrom");
}
}
}
}
@Order(10.0)
@FormData(value = AbstractTableFieldBeanData.class, sdkCommand = SdkCommand.USE, defaultSubtypeSdkCommand = DefaultSubtypeSdkCommand.CREATE)
public class FilesTableField extends AbstractTableField<FilesTableField.Table> {
@Override
protected int getConfiguredGridH() {
return 8;
}
@Override
protected int getConfiguredGridW() {
return 2;
}
@Override
protected String getConfiguredLabel() {
return TEXTS.get("Files");
}
@Order(10.0)
public class Table extends AbstractTreeTable {
@Override
protected boolean execIsNode(ITableRow row) {
Long type = getTypeColumn().getValue(row);
return FileTypeCodeType.FolderCode.ID.equals(type);
}
@Override
public void importFromTableBeanData(AbstractTableFieldBeanData source) throws ProcessingException {
super.importFromTableBeanData(source);
toggleExpandedState(getRows());
}
@Override
protected void execDecorateRow(ITableRow row) throws ProcessingException {
Long type = getTypeColumn().getValue(row);
if (FileTypeCodeType.FolderCode.ID.equals(type)) {
row.setIconId(Icons.FOLDER);
}
else if (FileTypeCodeType.FileCode.ID.equals(type)) {
row.setIconId(Icons.DOCUMENT);
}
else if (FileTypeCodeType.EmailCode.ID.equals(type)) {
row.setIconId(Icons.EMAIL);
}
else if (FileTypeCodeType.VCardCode.ID.equals(type)) {
row.setIconId(Icons.VCARD);
}
}
/**
* @return the TypeColumn
*/
public TypeColumn getTypeColumn() {
return getColumnSet().getColumnByClass(TypeColumn.class);
}
/**
* @return the ModifiedDateColumn
*/
public ModifiedDateColumn getModifiedDateColumn() {
return getColumnSet().getColumnByClass(ModifiedDateColumn.class);
}
/**
* @return the NameColumn
*/
public NameColumn getNameColumn() {
return getColumnSet().getColumnByClass(NameColumn.class);
}
@Order(20.0)
public class NameColumn extends AbstractStringColumn {
@Override
protected String getConfiguredHeaderText() {
return TEXTS.get("Name");
}
@Override
protected int getConfiguredWidth() {
return 400;
}
}
@Order(30.0)
public class ModifiedDateColumn extends AbstractDateColumn {
@Override
protected String getConfiguredFormat() {
return "dd.MM.yyyy hh:mm";
}
@Override
protected String getConfiguredHeaderText() {
return TEXTS.get("DateModified");
}
@Override
protected int getConfiguredWidth() {
return 200;
}
}
@Order(40.0)
public class TypeColumn extends AbstractSmartColumn<Long> {
@Override
protected Class<? extends ICodeType<?, Long>> getConfiguredCodeType() {
return FileTypeCodeType.class;
}
@Override
protected String getConfiguredHeaderText() {
return TEXTS.get("Type");
}
@Override
protected int getConfiguredWidth() {
return 200;
}
}
}
}
Of course, this is only a workaround for the real problem: scout is missing a representation for tree-tables at model level. If there was something like that, it would be possible to use real tree table widgets in the different UIs.