Sheet provides a spreadsheet experience you are already familiar with. You can finally work with large volumes of data without worrying about performance issues.
Notice the fixed columns and rows, and the custom cell styling. Validation is per cell, try and enter "abc" into the Price column which is expecting a BigDecimal.
Source
<p:importEnum type="org.primefaces.extensions.showcase.model.sheet.AssetType" var="AssetType"/>
<p:importEnum type="org.primefaces.extensions.showcase.model.sheet.PlatformType" var="PlatformType"/>
<pe:sheet id="sheet" widgetVar="sheetWidget" value="#{sheetController.assets}" var="row"
height="400" rowKey="#{row.assetId}" fixedCols="2" fixedRows="3" showRowHeaders="true"
resizableCols="true" resizableRows="true" filteredValue="#{sheetController.filteredAssets}"
sortBy="#{row.assetId}" sortOrder="ascending" readOnly="false" rowStyleClass="#{row.purchasePrice gt 0 ? 'positive' : 'negative'}">
<p:ajax event="change" listener="#{sheetController.cellChangeEvent}"/>
<f:facet name="header">
<h:outputText value="Assets"/>
</f:facet>
<f:facet name="footer">
<h:outputText id="footer" value="#{sheetController.filteredAssets.size()} Records"/>
</f:facet>
<pe:sheetcolumn headerText="Id (read-only)"
readOnly="true"
value="#{row.assetId}"
sortBy="#{row.assetId}"
colWidth="150"
filterBy="#{row.assetId}"/>
<pe:sheetcolumn headerText="Icon"
readOnly="true"
styleClass="text-center"
value="#{row.icon}"
colWidth="80"/>
<pe:sheetcolumn headerText="Type" value="#{row.assetType}" colWidth="100"
sortBy="#{row.assetType}" filterBy="#{row.assetType}"
filterOptions="#{sheetOptionProducer.assetTypes}"
colType="dropdown"
selectItems="#{AssetType.ALL_VALUES}"/>
<pe:sheetcolumn headerText="Platform" value="#{row.platform}" colWidth="100"
sortBy="#{row.platform}" filterBy="#{row.platform}"
filterOptions="#{sheetOptionProducer.platformTypes}"
colType="autocomplete"
selectItems="#{PlatformType.ALL_VALUES}"
autoCompleteStrict="false"
autoCompleteVisibleRows="4"/>
<pe:sheetcolumn headerText="Arch" value="#{row.platformArch}" readOnly="true"
colWidth="100" sortBy="#{row.platformArch}"
filterBy="#{row.platformArch}"
filterOptions="#{sheetOptionProducer.archTypes}"/>
<pe:sheetcolumn headerText="Active" value="#{row.active}" colWidth="60" colType="checkbox"/>
<pe:sheetcolumn headerText="Price" value="#{row.purchasePrice}" colWidth="100"
styleClass="htRight #{row.purchasePrice gt 0 ? 'positive' : 'negative'}"
colType="numeric"
numericLocale="de-DE"
numericPattern="$0,0.00"
required="true"
requiredMessage="Price is required!">
<f:converter converterId="javax.faces.BigDecimal"/>
</pe:sheetcolumn>
<pe:sheetcolumn headerText="Password" value="#{row.password}" colWidth="100" styleClass="password"
colType="password"
passwordHashSymbol="*"
passwordHashLength="8"/>
<pe:sheetcolumn headerText="Date" value="#{row.purchaseDate}" colWidth="110"
sortBy="#{row.purchaseDate}"
styleClass="htCenter"
colType="date"
dateFormat="YYYY-MM-DD">
<f:convertDateTime type="date" pattern="yyyy-MM-dd"/>
</pe:sheetcolumn>
<pe:sheetcolumn headerText="Time" value="#{row.purchaseTime}" colWidth="110"
sortBy="#{row.purchaseTime}"
styleClass="htCenter"
colType="time"
timeFormat="h:mm:ss a">
<f:convertDateTime type="time" pattern="h:mm:ss a"/>
</pe:sheetcolumn>
<pe:sheetcolumn headerText="Value 1" value="#{row.value1}" colWidth="100" colType="numeric"/>
<pe:sheetcolumn headerText="Value 2" value="#{row.value2}" colWidth="100" colType="numeric"/>
<pe:sheetcolumn headerText="Value 3" value="#{row.value3}" colWidth="100" colType="numeric"/>
<pe:sheetcolumn headerText="Value 4" value="#{row.value4}" colWidth="100" colType="numeric"/>
<pe:sheetcolumn headerText="Value 5" value="#{row.value5}" colWidth="100" colType="numeric"/>
<pe:sheetcolumn headerText="Value 6" value="#{row.value6}" colWidth="100" colType="numeric"/>
<pe:sheetcolumn headerText="Value 7" value="#{row.value7}" colWidth="100" colType="numeric"/>
<pe:sheetcolumn headerText="Value 8" value="#{row.value8}" colWidth="100" colType="numeric"/>
<pe:sheetcolumn headerText="Value 9" value="#{row.value9}" colWidth="100" colType="numeric"/>
<pe:sheetcolumn headerText="Value 10" value="#{row.value10}" colWidth="100" colType="numeric"/>
</pe:sheet>
<style type="text/css">
.positive {
color: Green !important;
}
.negative {
color: Red !important;
}
.password {
color: DarkViolet !important
}
</style>
@Named
@ViewScoped
public class SheetController implements Serializable {
private static final long serialVersionUID = 20120224L;
private List<Asset> assets = new ArrayList<>();
private List<Asset> filteredAssets = new ArrayList<>();
public SheetController() {
addAssets(40, PlatformType.Linux, PlatformArchType.AMD64, AssetType.SERVER);
addAssets(50, PlatformType.Windows, PlatformArchType.I386, AssetType.DESKTOP);
addAssets(60, PlatformType.Mac, PlatformArchType.OTHER, AssetType.LAPTOP);
addAssets(5, null, null, AssetType.PRINTER);
}
public void cellChangeEvent(final SheetEvent event) {
final Sheet sheet = event.getSheet();
final List<SheetUpdate> updates = sheet.getUpdates();
// A SheetUpdate exists for each column updated, the same row may
// appear more than once. For this reason we will track those we already
// persisted
final HashSet<Asset> processed = new HashSet<Asset>();
int rowUpdates = 0;
for (final SheetUpdate update : updates) {
final Asset asset = (Asset) update.getRowData();
if (processed.contains(asset)) {
continue;
}
System.out.println("Asset " + asset.getAssetId() + " updated.");
rowUpdates++;
}
sheet.commitUpdates();
FacesContext.getCurrentInstance().addMessage(null,
new FacesMessage("Update Success", rowUpdates + " rows updated"));
}
private void addAssets(final int count, final PlatformType platform, final PlatformArchType arch,
final AssetType type) {
for (int i = 0; i < count; i++) {
final Asset asset = new Asset();
asset.setAssetId(RandomUtils.nextLong());
asset.setActive(RandomUtils.nextBoolean());
asset.setPlatform(platform);
asset.setPlatformArch(arch);
asset.setHostName(type.toString().toLowerCase() + i + ".example.lan");
asset.setAssetType(type);
asset.setIcon("<i class='fas fa-info-circle' style='color:cornflowerblue' />");
asset.setPurchaseDate(new Date());
asset.setPurchaseTime(new Date());
asset.setValue1(RandomUtils.nextInt(1, 1000));
asset.setPassword(RandomStringUtils.randomAlphabetic(6));
asset.setPurchasePrice(
BigDecimal.valueOf(RandomUtils.nextDouble(1.11, 999.99) * (RandomUtils.nextBoolean() ? -1 : 1))
.setScale(2, RoundingMode.HALF_UP));
getAssets().add(asset);
}
}
public List<Asset> getAssets() {
return assets;
}
public void setAssets(final List<Asset> assets) {
this.assets = assets;
}
public List<Asset> getFilteredAssets() {
return filteredAssets;
}
public void setFilteredAssets(final List<Asset> filteredAssets) {
this.filteredAssets = filteredAssets;
}
}