Sheet fires ajax events for cell selection, changes, sorting, filtering and row or column selection. Sheet is a spreadsheet-like in-cell editing component based on Handsontable by Handsoncode.
Sheet provides the ajax events cellSelect, change, sort, filter, columnSelect and rowSelect. Click a header once to select it, then you may move columns around when the hand icon appears.
When outsideClickDeselects is false, clicking outside the sheet keeps the current selection.
<style>
.positive {
color: Green !important;
}
.negative {
color: Red !important;
}
</style>
<div class="card">
<h5>Ajax Events</h5>
<p>Sheet provides the ajax events <i>cellSelect</i>, <i>change</i>, <i>sort</i>, <i>filter</i>, <i>columnSelect</i> and <i>rowSelect</i>. Click a header once to select it, then you may move columns around when the hand icon appears.</p>
<h:form id="formAjaxEvents">
<p:messages id="messages" showSummary="true" showDetail="true"/>
<pe:sheet id="sheet" widgetVar="sheetEventsWidget" value="#{sheetAjaxController.assets}"
var="row" height="400" rowKey="#{row.assetId}" showRowHeaders="true"
sortBy="#{row.assetId}" sortOrder="ascending" width="1000"
stretchH="all" movableCols="true" movableRows="true"
filteredValue="#{sheetAjaxController.filteredAssets}">
<p:ajax event="change" listener="#{sheetAjaxController.cellChangeEvent}" update="messages"/>
<p:ajax event="columnSelect" listener="#{sheetAjaxController.columnSelectEvent}" update="messages"/>
<p:ajax event="rowSelect" listener="#{sheetAjaxController.rowSelectEvent}" update="detailPanel messages"/>
<f:facet name="header">
<h:outputText value="Assets"/>
</f:facet>
<f:facet name="footer">
<h:outputText value="#{sheetAjaxController.filteredAssets.size()} Records"/>
</f:facet>
<pe:sheetcolumn headerText="Id (readOnly)" readOnly="true" value="#{row.assetId}" colWidth="150"/>
<pe:sheetcolumn headerText="Type (readOnly)" value="#{row.assetType}" readOnly="true" colWidth="100"/>
<pe:sheetcolumn headerText="Platform (readOnly)" value="#{row.platform}" readOnly="true" colWidth="100"/>
<pe:sheetcolumn headerText="Arch (readOnly)" value="#{row.platformArch}" readOnly="true" colWidth="100"/>
<pe:sheetcolumn headerText="Editable" value="#{row.value1}" colWidth="100" styleClass="htRight" colType="numeric"/>
</pe:sheet>
<p:panel id="detailPanel" header="Selected Row Details" style="margin-top:1rem">
<h:panelGrid columns="2" rendered="#{sheetAjaxController.selectedAsset != null}">
<h:outputText value="ID:"/>
<h:outputText value="#{sheetAjaxController.selectedAsset.assetId}"/>
<h:outputText value="Host Name:"/>
<h:outputText value="#{sheetAjaxController.selectedAsset.hostName}"/>
<h:outputText value="Platform:"/>
<h:outputText value="#{sheetAjaxController.selectedAsset.platform}"/>
<h:outputText value="Type:"/>
<h:outputText value="#{sheetAjaxController.selectedAsset.assetType}"/>
<h:outputText value="Arch:"/>
<h:outputText value="#{sheetAjaxController.selectedAsset.platformArch}"/>
<h:outputText value="Purchase Price:"/>
<h:outputText value="#{sheetAjaxController.selectedAsset.purchasePrice}"/>
<h:outputText value="Active:"/>
<h:outputText value="#{sheetAjaxController.selectedAsset.active}"/>
</h:panelGrid>
<h:outputText value="Click a row to see details." rendered="#{sheetAjaxController.selectedAsset == null}"
style="color:#888"/>
</p:panel>
</h:form>
</div>
<div class="card">
<h5>Outside Click Deselects</h5>
<p>When <code>outsideClickDeselects</code> is <code>false</code>, clicking outside the sheet keeps the current selection.</p>
<h:form id="formOutsideClickDeselects">
<p:importEnum type="org.primefaces.extensions.showcase.model.sheet.AssetType" var="AssetType"/>
<p:importEnum type="org.primefaces.extensions.showcase.model.sheet.PlatformType" var="PlatformType"/>
<h:panelGroup style="display:block; margin-bottom: 1rem;">
<p:outputLabel for="platformFilter" value="Filter Platform: " style="margin-right: 0.5rem;"/>
<p:selectOneMenu id="platformFilter" filter="true" style="width: 200px;">
<f:selectItem itemLabel="All" itemValue=""/>
<f:selectItems value="#{PlatformType.ALL_VALUES}" var="pt" itemLabel="#{pt}" itemValue="#{pt}"/>
</p:selectOneMenu>
</h:panelGroup>
<pe:sheet id="sheet" widgetVar="sheetOutsideWidget" value="#{sheetController.assets}" var="row" height="400"
rowKey="#{row.assetId}" showRowHeaders="true" sortBy="#{row.assetId}" sortOrder="ascending"
outsideClickDeselects="false" filteredValue="#{sheetController.filteredAssets}">
<f:facet name="header">
<h:outputText value="Assets"/>
</f:facet>
<f:facet name="footer">
<h:outputText 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="jakarta.faces.BigDecimal"/>
</pe:sheetcolumn>
</pe:sheet>
</h:form>
</div>
package org.primefaces.extensions.showcase.controller.sheet;
import java.io.Serial;
import java.util.List;
import jakarta.faces.application.FacesMessage;
import jakarta.faces.component.UIComponent;
import jakarta.faces.context.FacesContext;
import jakarta.faces.validator.ValidatorException;
import jakarta.faces.view.ViewScoped;
import jakarta.inject.Named;
import org.apache.commons.collections4.IterableUtils;
import org.primefaces.extensions.component.sheet.Sheet;
import org.primefaces.extensions.event.SheetEvent;
import org.primefaces.extensions.model.sheet.SheetUpdate;
import org.primefaces.extensions.showcase.model.sheet.Asset;
/**
* {@link Sheet} Ajax Controller.
*
* @author Melloware [email protected]
*/
@Named
@ViewScoped
public class SheetAjaxController extends SheetController {
@Serial
private static final long serialVersionUID = 20120224L;
private Asset selectedAsset;
/**
* Ajax callback from the Sheet component when a cell value is changed.
*/
@Override
public void cellChangeEvent(final SheetEvent event) {
final Sheet sheet = event.getSheet();
final List<SheetUpdate> updates = sheet.getUpdates();
// only show 1 update
SheetUpdate sheetUpdate = IterableUtils.first(updates);
final Long id = (Long) sheetUpdate.getRowKey();
final Object oldValue = sheetUpdate.getOldValue();
final Object newValue = sheetUpdate.getNewValue();
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Update Success",
String.format("Asset %s updated. Old Value = %s, New Value = %s", id, oldValue, newValue)));
sheet.commitUpdates();
}
/**
* Ajax callback from the Sheet component when a column is selected.
*/
public static void columnSelectEvent(final SheetEvent event) {
final Sheet sheet = event.getSheet();
final int column = sheet.getSelectedColumn() + 1;
FacesContext.getCurrentInstance().addMessage(null,
new FacesMessage("Column Selected", String.format("Column %d selected.", column)));
}
/**
* Ajax callback from the Sheet component when a row is selected.
*/
public void rowSelectEvent(final SheetEvent event) {
selectedAsset = (Asset) event.getRowData();
final int row = event.getRowIndex() + 1;
final String hostName = (selectedAsset != null) ? selectedAsset.getHostName() : "N/A";
FacesContext.getCurrentInstance().addMessage(null,
new FacesMessage("Row Selected",
String.format("Row %d selected. Host: %s", row, hostName)));
}
public Asset getSelectedAsset() {
return selectedAsset;
}
public static void validateExactly5(final FacesContext context, final UIComponent comp, final Object value) {
if (context == null || comp == null) {
return;
}
final Integer integer = (Integer) value;
if (integer.intValue() != 5) {
final FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Error",
"Value must only be 5 exactly!");
throw new ValidatorException(message);
}
}
}
package org.primefaces.extensions.showcase.model.sheet;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
public class Asset implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
private Long assetId;
private Date purchaseDate;
private Date purchaseTime;
private String hostName;
private String description;
private String icon;
private BigDecimal purchasePrice;
private PlatformType platform;
private PlatformArchType platformArch;
private AssetType assetType;
private Boolean active;
private String comment;
private String password;
private Date lastUpdated;
private String patchLevel;
private String allocatedTo;
private String location;
private Integer value1;
private Integer value2;
private Integer value3;
private Integer value4;
private Integer value5;
private Integer value6;
private Integer value7;
private Integer value8;
private Integer value9;
private Integer value10;
public Long getAssetId() {
return assetId;
}
public void setAssetId(final Long assetId) {
this.assetId = assetId;
}
public Date getPurchaseDate() {
return purchaseDate;
}
public void setPurchaseDate(final Date purchaseDate) {
this.purchaseDate = purchaseDate;
}
public String getHostName() {
return hostName;
}
public void setHostName(final String hostName) {
this.hostName = hostName;
}
public String getDescription() {
return description;
}
public void setDescription(final String description) {
this.description = description;
}
public String getIcon() {
return icon;
}
public void setIcon(final String icon) {
this.icon = icon;
}
public BigDecimal getPurchasePrice() {
return purchasePrice;
}
public void setPurchasePrice(final BigDecimal purchasePrice) {
this.purchasePrice = purchasePrice;
}
public PlatformType getPlatform() {
return platform;
}
public void setPlatform(final PlatformType platform) {
this.platform = platform;
}
public PlatformArchType getPlatformArch() {
return platformArch;
}
public void setPlatformArch(final PlatformArchType platformArch) {
this.platformArch = platformArch;
}
public AssetType getAssetType() {
return assetType;
}
public void setAssetType(final AssetType assetType) {
this.assetType = assetType;
}
public String getComment() {
return comment;
}
public void setComment(final String comment) {
this.comment = comment;
}
public Date getLastUpdated() {
return lastUpdated;
}
public void setLastUpdated(final Date lastUpdated) {
this.lastUpdated = lastUpdated;
}
public String getPatchLevel() {
return patchLevel;
}
public void setPatchLevel(final String patchLevel) {
this.patchLevel = patchLevel;
}
public String getAllocatedTo() {
return allocatedTo;
}
public void setAllocatedTo(final String allocatedTo) {
this.allocatedTo = allocatedTo;
}
public String getLocation() {
return location;
}
public void setLocation(final String location) {
this.location = location;
}
public Integer getValue1() {
return value1;
}
public void setValue1(final Integer value1) {
this.value1 = value1;
}
public Integer getValue2() {
return value2;
}
public void setValue2(final Integer value2) {
this.value2 = value2;
}
public Integer getValue3() {
return value3;
}
public void setValue3(final Integer value3) {
this.value3 = value3;
}
public Integer getValue4() {
return value4;
}
public void setValue4(final Integer value4) {
this.value4 = value4;
}
public Integer getValue5() {
return value5;
}
public void setValue5(final Integer value5) {
this.value5 = value5;
}
public Integer getValue6() {
return value6;
}
public void setValue6(final Integer value6) {
this.value6 = value6;
}
public Integer getValue7() {
return value7;
}
public void setValue7(final Integer value7) {
this.value7 = value7;
}
public Integer getValue8() {
return value8;
}
public void setValue8(final Integer value8) {
this.value8 = value8;
}
public Integer getValue9() {
return value9;
}
public void setValue9(final Integer value9) {
this.value9 = value9;
}
public Integer getValue10() {
return value10;
}
public void setValue10(final Integer value10) {
this.value10 = value10;
}
public String getPassword() {
return password;
}
public void setPassword(final String password) {
this.password = password;
}
public Boolean getActive() {
return active;
}
public void setActive(final Boolean active) {
this.active = active;
}
public Date getPurchaseTime() {
return purchaseTime;
}
public void setPurchaseTime(final Date purchaseTime) {
this.purchaseTime = purchaseTime;
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (assetId == null ? 0 : assetId.hashCode());
result = prime * result + (hostName == null ? 0 : hostName.hashCode());
return result;
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Asset other = (Asset) obj;
if (assetId == null) {
if (other.assetId != null) {
return false;
}
}
else if (!assetId.equals(other.assetId)) {
return false;
}
if (hostName == null) {
return other.hostName == null;
}
else {
return hostName.equals(other.hostName);
}
}
public Integer getTotal() {
return (value1 == null ? 0 : value1) +
(value2 == null ? 0 : value2) +
(value3 == null ? 0 : value3) +
(value4 == null ? 0 : value4) +
(value5 == null ? 0 : value5) +
(value6 == null ? 0 : value6) +
(value7 == null ? 0 : value7) +
(value8 == null ? 0 : value8) +
(value9 == null ? 0 : value9) +
(value10 == null ? 0 : value10);
}
}
package org.primefaces.extensions.showcase.model.sheet;
public enum PlatformType {
Windows, Linux, Mac
}
package org.primefaces.extensions.showcase.model.sheet;
public enum PlatformArchType {
I386, AMD64, OTHER
}
package org.primefaces.extensions.showcase.model.sheet;
public enum AssetType {
DESKTOP, SERVER, LAPTOP, PRINTER, OTHER
}
package org.primefaces.extensions.showcase.controller.sheet;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import jakarta.faces.application.FacesMessage;
import jakarta.faces.context.FacesContext;
import jakarta.faces.view.ViewScoped;
import jakarta.inject.Named;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.RandomUtils;
import org.primefaces.extensions.component.sheet.Sheet;
import org.primefaces.extensions.event.SheetEvent;
import org.primefaces.extensions.model.sheet.SheetUpdate;
import org.primefaces.extensions.showcase.model.sheet.Asset;
import org.primefaces.extensions.showcase.model.sheet.AssetType;
import org.primefaces.extensions.showcase.model.sheet.PlatformArchType;
import org.primefaces.extensions.showcase.model.sheet.PlatformType;
/**
* {@link Sheet} Controller.
*
* @author Melloware [email protected]
*/
@Named
@ViewScoped
public class SheetController implements Serializable {
@Serial
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.secure().randomLong());
asset.setActive(RandomUtils.secure().randomBoolean());
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.secure().randomInt(1, 1000));
asset.setPassword(RandomStringUtils.secure().nextAlphabetic(6));
asset.setPurchasePrice(
BigDecimal.valueOf(RandomUtils.secure().randomDouble(1.11, 999.99) * (RandomUtils.secure().randomBoolean() ? -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;
}
}
package org.primefaces.extensions.showcase.controller.sheet;
import java.util.*;
import jakarta.annotation.*;
import jakarta.enterprise.context.*;
import jakarta.faces.model.*;
import jakarta.inject.*;
import org.primefaces.extensions.showcase.model.sheet.*;
@Named
@ApplicationScoped
public class SheetOptionProducer {
private List<SelectItem> assetTypes;
private List<SelectItem> platformTypes;
private List<SelectItem> archTypes;
@PostConstruct
private void init() {
assetTypes = createEnumList(AssetType.values());
platformTypes = createEnumList(PlatformType.values());
archTypes = createEnumList(PlatformArchType.values());
}
private static <T extends Enum<?>> List<SelectItem> createEnumList(final T[] values) {
final List<SelectItem> result = new ArrayList<>();
result.add(new SelectItem("", "All"));
for (final T value : values) {
result.add(new SelectItem(value, value.name()));
}
return result;
}
public List<SelectItem> getAssetTypes() {
return assetTypes;
}
public List<SelectItem> getPlatformTypes() {
return platformTypes;
}
public List<SelectItem> getArchTypes() {
return archTypes;
}
}