I've created simple implementation of Primefaces' LazyDataModel. Now paginator works right, but when I set sortBy="..." attribute on some Datatable's column and try to sort table by clicking the column header on my JSF page, in LazyDataModel#load method I'm still getting sortField set to null and sortOrder set to ASCENDING. The ajax request is sent, containing sort field and order, however, this data are not passed to the load() method.
My backing bean providing the data model to JSF is #Named #ViewScoped, I'm using Wildfly 8 (JSF 2.2.5), Primefaces 4. Any tips?
#Override
public List<T> load(int first, int pageSize, String sortField,
SortOrder sortOrder, Map<String, String> filters) {
System.out.println("FIELD: " + sortField + ", ORDER: " + sortOrder);
.......
<p:dataTable value="#{logsBacking.appLogModel}"
paginator="true" rows="10" rowsPerPageTemplate="10"
var="appLog" lazy="true" rowKey="#{appLog.id}">
<p:column headerText="test" sortBy="id}">
<h:outputText value="id" />
</p:column>
............
try to override the other load(), getRowKey() and getRowData() too.
this is mine, and it is working:
public class EntityDataModel<T extends AbstractEntity> extends LazyDataModel<T>
{
private static final long serialVersionUID = 1L;
protected final Class<T> entityClass;
protected final EntityConverter converter;
protected final PersistenceService service;
public EntityDataModel(Class<T> entityClass)
{
super();
this.entityClass = entityClass;
this.converter = new EntityConverter();
try
{
this.service = PersistenceService.lookup();
}
catch(NamingException e)
{
throw new RuntimeException(e.getMessage(), e);
}
setPageSize(1);
setRowCount(-1);
}
#Override
public List<T> load(int first, int pageSize, List<SortMeta> multiSortMeta, Map<String, String> filterMap)
{
try
{
List<T> resultList = service.queryAll(entityClass, first, pageSize, filterMap, multiSortMeta);
setRowCount(service.count(entityClass, filterMap).intValue());
return resultList;
}
catch(Exception e)
{
throw new RuntimeException(e.getMessage(), e);
}
}
#Override
public List<T> load(int first, int pageSize, String sortField, SortOrder sortOrder, Map<String, String> filterMap)
{
return load(first, pageSize, Lists.newArrayList(new SortMeta(null, sortField, sortOrder, null)), filterMap);
}
#Override
public Object getRowKey(T entity)
{
return converter.getAsString(null, null, entity);
}
#Override
public T getRowData(String rowKey)
{
return (T) converter.getAsObject(null, null, rowKey);
}
#Override
public void setRowIndex(int rowIndex)
{
super.setRowIndex(getPageSize() == 0 ? -1 : rowIndex);
}
}
#PhilHDN
The PersistenceService.queryAll() makes use of many internal objects to create a dynamic query with dynamic order.
Here is a tailored version:
#TransactionAttribute(TransactionAttributeType.SUPPORTS)
public <T> List<T> queryAll(Class<T> entityClass, int first, int max, Map<String, ?> filter, List<SortMeta> sortList)
{
return queryAll(entityClass, first, max, createRestriction(filter), createSort(sortList));
}
#TransactionAttribute(TransactionAttributeType.SUPPORTS)
public <T> List<T> queryAll(Class<T> entityClass, int first, int max, Restriction<? super T> restriction, Sort<? super T> sort)
{
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<T> cq = builder.createQuery(entityClass);
Root<T> root = cq.from(entityClass);
cq.select(root).distinct(true);
if(restriction != null)
{
cq.where(restriction.apply(builder, root));
}
if(sort != null)
{
cq.orderBy(sort.apply(builder, root));
}
TypedQuery<T> query = em.createQuery(cq);
if(first > -1)
{
query.setFirstResult(first);
}
if(max > -1)
{
query.setMaxResults(max);
}
return query.getResultList();
}
private <T> Sort<T> createSort(List<SortMeta> sortList)
{
return (builder, root) ->
{
List<Order> ret = new LinkedList<>();
if(sortList != null)
{
for(SortMeta sortMeta : sortList)
{
String field = sortMeta.getSortField();
SortOrder sortOrder = sortMeta.getSortOrder();
if(field == null || field.isEmpty())
{
continue;
}
Expression<?> expr = root.get(field);
if(sortOrder == SortOrder.ASCENDING)
{
ret.add(builder.asc(expr));
}
else if(sortOrder == SortOrder.DESCENDING)
{
ret.add(builder.desc(expr));
}
}
}
return ret;
};
}
public interface Sort<T> extends BiFunction<CriteriaBuilder, Root<? extends T>, List<Order>>
{
// empty
}
p:dataTable MUST be inside a h:form so that filters work properly.
Related
I am upgrading primefaces 3 to 8. Already working datatable filters in 3 started not to work properly in 8. While entering an existing value to filter returning no data, "load" method seems is not triggered.
However, sorting column is triggering load method.
I compared with working example in primefaces showcase for lazy example of datatable, its already the same. Checked logs, inspected page for any error, but can't find anything.
Why it is not working? How can I debug it?
<ui:define name="content">
<h:form id="logsform">
<p:panel>
<p:dataTable id="tbl" var="log"
value="#{logBean.operationLogDataModel}" lazy="true" rows="10"
paginator="true" emptyMessage="#{messages['common.datatable.emptymessage']}">
<p:column sortBy="#{log.custNo}" filterBy="#{log.custNo}">
<f:facet name="header">
<h:outputText value="CustNo" />
</f:facet>
<h:outputText value="#{log.custNo}" />
</p:column>
</p:dataTable>
</p:panel>
</h:form>
My logBean is likely:
#ViewScoped
#FacesConfig(version = FacesConfig.Version.JSF_2_3)
#Named("logBean")
public class LogBean implements Serializable {
#Inject
#ManagedProperty("#{logBo}")
private LogBo logBo;
OperationLogDataModel operationLogDataModel;
AuthUser authUser = (AuthUser) SecurityContextHolder.getContext().getAuthentication();
public LogBean() {
}
#PostConstruct
public void initModel() {
operationLogDataModel = new OperationLogDataModel(logBo, authUser);
documentLogList = new ArrayList<DocumentLog>();
}
.
.
}
My lazyDataModel is as follows:
public class OperationLogDataModel extends LazyDataModel<OperationLogs> {
private static final Logger LOG = Logger.getLogger(OperationLogDataModel.class);
private LogBo logBo;
private String currentSortField;
private SortOrder currentSortOrder;
private Map<String, FilterMeta> currentFilters;
AuthUser authUser;
private Date startDate;
private Date finishDate;
public OperationLogDataModel() {
}
public OperationLogDataModel(LogBo logBo, AuthUser authUser) {
this.authUser = authUser;
this.logBo = logBo;
super.setPageSize(Configurations.PAGE_SIZE);
}
public OperationLogDataModel(LogBo logBo, AuthUser authUser, Date startDate, Date finishDate) {
this.logBo = logBo;
this.authUser = authUser;
super.setPageSize(Configurations.PAGE_SIZE);
this.startDate = startDate;
this.finishDate = finishDate;
}
#Override
public List<OperationLogs> load(int first, int pageSize, String sortField, SortOrder sortOrder, Map<String, FilterMeta> filters) {
currentSortField = sortField;
currentSortOrder = sortOrder;
currentFilters = filters;
if (sortField == null) {
sortField = "createTime";
sortOrder = SortOrder.DESCENDING;
}
Criterion sqlRestriction = Restrictions.sqlRestriction("1=1");
if (startDate != null)
sqlRestriction = Restrictions.and(sqlRestriction, Restrictions.ge("createTime", startDate));
if (finishDate != null)
sqlRestriction = Restrictions.and(sqlRestriction, Restrictions.le("createTime", finishDate));
List<OperationLogs> logs = new ArrayList<OperationLogs>();
try {
if (authUser.getAuthorityName().equals(Role.ROLE_ORDINARY.getName()))
sqlRestriction = Restrictions.and(sqlRestriction, Restrictions.eq("username", authUser.getName()));
super.setRowCount(logBo.countLogs(-1, -1, null, sortOrder.name(), filters, null, sqlRestriction).intValue());
System.out.println("load called!");
logs = logBo.listLogs(first, pageSize, sortField, sortOrder.name(), filters, null, sqlRestriction);
} catch (Exception e) {
LOG.error(e, e);
}
return logs;
}
#Override
public String getRowKey(OperationLogs logs) {
return logs.getId() + "";
}
#Override
public OperationLogs getRowData(String rowKey) {
try {
return logBo.getLogById(Long.parseLong(rowKey));
} catch (Exception e) {
return null;
}
}
#Override
public void setPageSize(int pageSize) {
super.setPageSize(pageSize);
}
public void onFilter(AjaxBehaviorEvent event) {
}
public String getCurrentSortField() {
return currentSortField;
}
public void setCurrentSortField(String currentSortField) {
this.currentSortField = currentSortField;
}
public SortOrder getCurrentSortOrder() {
return currentSortOrder;
}
public void setCurrentSortOrder(SortOrder currentSortOrder) {
this.currentSortOrder = currentSortOrder;
}
public Map<String, FilterMeta> getCurrentFilters() {
return currentFilters;
}
public void setCurrentFilters(Map<String, FilterMeta> currentFilters) {
this.currentFilters = currentFilters;
}
}
logBo.listLogs in load method above is just calling findEntities method below. But I don't think execution reaches here as load method above is never called when filtering despite being called and working properly in sort. I think so because the printly before logBo.listLogs
#Override
public List<T> findEntities(int first, int pageSize, String sortField,
String sortOrder, Map<String, FilterMeta> filters,
Map<String, String> aliases, Criterion extraCriterion)
throws SecurityException, NoSuchFieldException {
Criteria crit = sessionFactory.getCurrentSession().createCriteria(
getPersistentClass());
crit = prepareCriteria(first, pageSize, sortField, sortOrder, filters,
aliases, extraCriterion, crit);
crit.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
return crit.list();
}
private Criteria prepareCriteria(int first, int pageSize, String sortField,
String sortOrder, Map<String, FilterMeta> filters,
Map<String, String> aliases, Criterion extraCriterion, Criteria crit)
throws NoSuchFieldException {
if (aliases != null && !aliases.isEmpty()) {
Iterator<Entry<String, String>> iterator = aliases.entrySet()
.iterator();
while (iterator.hasNext()) {
Entry<String, String> entry = iterator.next();
crit.createAlias(entry.getKey(), entry.getValue(),
Criteria.LEFT_JOIN);
}
}
if (extraCriterion != null) {
crit.add(extraCriterion);
}
if (sortField != null && !sortField.isEmpty()) {
if (!sortOrder.equalsIgnoreCase("UNSORTED")) {
if (sortOrder.equalsIgnoreCase("ASCENDING")) {
crit = crit.addOrder(Order.asc(sortField));
} else {
crit = crit.addOrder(Order.desc(sortField));
}
}
}
if (filters != null && !filters.isEmpty()) {
Iterator<Entry<String, FilterMeta>> iterator = filters.entrySet()
.iterator();
while (iterator.hasNext()) {
Entry<String, FilterMeta> entry = iterator.next();
Class<?> type = getPersistentClass().getDeclaredField(
entry.getKey()).getType();
try {
if (type.isEnum() || Number.class.isAssignableFrom(type)
|| Double.class.isAssignableFrom(type)) {
crit = crit.add(Restrictions.eq(entry.getKey(), type
.getDeclaredMethod("valueOf", String.class)
.invoke(null, entry.getValue())));
} else {
crit = crit.add(Restrictions.like(entry.getKey(),
String.valueOf(entry.getValue().getFilterValue()), MatchMode.START));
}
} catch (Exception ex) {
}
}
}
if (first != -1) {
crit = crit.setFirstResult(first);
}
if (pageSize != -1) {
crit = crit.setMaxResults(pageSize);
}
return crit;
}
PS Update: Seems I am mistaken, load is called during filter. Possibly I need to check my custom generic filtering code.
I found out that once I enter some value to column A filterBy field, the other columns filterBy fields which I left intentionally empty enters the custom query as:
CriteriaImpl(com.xxx.model.OperationLogs:this[][1=1, custNo like 11111%, refNo like null%, username like null%])
So adding a control while query generation:
if(entry.getValue().getFilterValue() == null) {
continue;
}
just after:
if (filters != null && !filters.isEmpty()) {
Iterator<Entry<String, FilterMeta>> iterator = filters.entrySet()
.iterator();
while (iterator.hasNext()) {
solved the problem.
I was doing a total upgrade including Primefaces, JSF, Spring, Hibernate, etc
I think I have missed something in hibernate upgrade, as it was working in previous version successfully.
The point is i have a Custom JPA Repository which is load using "#EnableJpaRepositories", but inside of this Custom JPA repository i do autowire another Spring Bean annotated with #Component, but it never comes filled, always bringing a null reference...
I read that JPA Repository does join and share the same Spring Application Context and so it cannot see those Beans loaded by the common Application Context... Is it really true? If so, is there any way to glue them and make Custom Repositories to inject my componentes properly???
Down here the relevant code:
public class DefaultCrudRepository<T extends IdentifiableEntity> extends QuerydslJpaRepository<T, BigInteger>
implements CrudRepository<T> {
private static final EntityPathResolver DEFAULT_ENTITY_PATH_RESOLVER = SimpleEntityPathResolver.INSTANCE;
private JpaEntityInformation<T, BigInteger> jpaEntityInformation;
private EntityManager entityManager;
private EntityPath<T> path;
private PathBuilder<T> builder;
private Querydsl querydsl;
#Autowired
private SortComponent sortComponent;
#Autowired
private PageComponent pageComponent;
#Autowired
private FilterComponent filterComponent;
#Autowired
private ExpandComponent expandComponent;
public DefaultCrudRepository(JpaEntityInformation<T, BigInteger> jpaEntityInformation, EntityManager entityManager,
EntityPathResolver resolver) {
super(jpaEntityInformation, entityManager, resolver);
this.jpaEntityInformation = jpaEntityInformation;
this.entityManager = entityManager;
this.path = resolver.createPath(jpaEntityInformation.getJavaType());
this.builder = new PathBuilder<T>(path.getType(), path.getMetadata());
this.querydsl = new Querydsl(entityManager, builder);
this.expandComponent = new DefaultExpandComponent(entityManager);
this.sortComponent = new DefaultSortComponent();
this.filterComponent = new DefaultFilterComponent();
this.pageComponent = new DefaultPageComponent();
init();
}
public DefaultCrudRepository(JpaEntityInformation<T, BigInteger> jpaEntityInformation,
EntityManager entityManager) {
this(jpaEntityInformation, entityManager, DEFAULT_ENTITY_PATH_RESOLVER);
this.jpaEntityInformation = jpaEntityInformation;
this.entityManager = entityManager;
}
/*
* private Class<?> getDomainClass(Class<?> clazz) { Type type =
* clazz.getGenericSuperclass(); if (type instanceof ParameterizedType) {
* ParameterizedType parameterizedType = (ParameterizedType) type; return
* (Class<?>) parameterizedType.getActualTypeArguments()[0]; } else { return
* getDomainClass(clazz.getSuperclass()); } }
*/
#PostConstruct
private void init() {
this.filterComponent.init(this.jpaEntityInformation.getJavaType());
this.expandComponent.init(this.jpaEntityInformation.getJavaType());
}
#Override
public <S extends T> List<S> save(Iterable<S> entities) {
List<S> savedEntities = super.save(entities);
super.flush();
this.entityManager.refresh(savedEntities);
return savedEntities;
}
#Override
public <S extends T> S save(S entity) {
S savedEntity = super.save(entity);
super.flush();
if (!this.jpaEntityInformation.isNew(entity)) {
this.entityManager.refresh(savedEntity);
}
return savedEntity;
}
protected JPQLQuery<T> createQuery(final Predicate predicate, final EntityGraph<?> entityGraph) {
JPQLQuery<?> query = createQuery(predicate);
if (entityGraph != null) {
((AbstractJPAQuery<?, ?>) query).setHint(EntityGraphType.LOAD.getKey(), entityGraph);
}
return query.select(path);
}
protected Page<T> findAll(final Pageable pageable, final Predicate predicate, final EntityGraph<?> entityGraph) {
final JPQLQuery<?> countQuery = createCountQuery(predicate);
JPQLQuery<T> query = querydsl.applyPagination(pageable, createQuery(predicate, entityGraph));
return PageableExecutionUtils.getPage(query.fetch(), pageable, new LongSupplier() {
#Override
public long getAsLong() {
return countQuery.fetchCount();
}
});
}
#Override
public Page<T> findAll(Integer pageNumber, Integer pageSize, BooleanExpression booleanExpression,
String filterExpression, String sortExpression, String expandExpression)
throws InvalidFilterExpressionException, InvalidSortExpressionException,
InvalidExpandExpressionException {
Sort sort = null;
if (sortExpression != null && !sortExpression.isEmpty()) {
sort = this.sortComponent.getSort(sortExpression);
}
Pageable pageable = this.pageComponent.getPage(pageNumber, pageSize, sort);
BooleanExpression filterBooleanExpression = null;
if (filterExpression != null) {
filterBooleanExpression = this.filterComponent.getBooleanExpression(filterExpression);
}
BooleanExpression mergedBooleanExpression = null;
if (booleanExpression != null && filterBooleanExpression != null) {
mergedBooleanExpression = booleanExpression.and(filterBooleanExpression);
} else if (booleanExpression != null && filterBooleanExpression == null) {
mergedBooleanExpression = booleanExpression;
} else if (booleanExpression == null && filterBooleanExpression != null) {
mergedBooleanExpression = filterBooleanExpression;
}
EntityGraph<?> entityGraph = null;
if (expandExpression != null && !expandExpression.isEmpty()) {
entityGraph = this.expandComponent.getEntityGraph(expandExpression);
}
return this.findAll(pageable, mergedBooleanExpression, entityGraph);
}
protected Predicate getPredicate(final BigInteger identifier, final Predicate predicate) {
Class<?> clazz = this.jpaEntityInformation.getJavaType();
String name = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL, clazz.getSimpleName());
Path<?> rootPath = Expressions.path(this.jpaEntityInformation.getJavaType(), name);
Class<?> idType = this.jpaEntityInformation.getIdType();
String idAttributeName = this.jpaEntityInformation.getIdAttribute().getName();
Path<?> leftPath = Expressions.path(idType, rootPath, idAttributeName);
Expression<?> rightExpression = Expressions.constant(identifier);
BooleanExpression booleanExpression = Expressions.predicate(Ops.EQ, leftPath, rightExpression);
BooleanBuilder booleanBuilder = new BooleanBuilder(booleanExpression);
booleanBuilder.and(predicate);
return booleanBuilder.getValue();
}
protected T findOne(final BigInteger identifier, final BooleanExpression booleanExpression,
final EntityGraph<?> entityGraph) {
Assert.notNull(identifier, "The given id must not be null!");
T object = null;
if (booleanExpression != null) {
Predicate mergedPredicate = getPredicate(identifier, booleanExpression);
JPQLQuery<T> query = createQuery(mergedPredicate, entityGraph);
object = query.fetchOne();
} else {
Map<String, Object> hints = new HashMap<String, Object>();
if (entityGraph != null) {
hints.put("javax.persistence.loadgraph", entityGraph);
}
object = this.entityManager.find(this.jpaEntityInformation.getJavaType(), identifier, hints);
}
return object;
}
#Override
public T findOne(final BigInteger identifier, final BooleanExpression booleanExpression,
final String expandExpression) throws InvalidExpandExpressionException {
EntityGraph<?> entityGraph = null;
if (booleanExpression != null) {
entityGraph = this.expandComponent.getEntityGraph(expandExpression);
}
return this.findOne(identifier, booleanExpression, entityGraph);
}
#Override
public Map<Number, T> findAllRevisions(final BigInteger identifier) {
Assert.notNull(identifier, "The given id must not be null!");
AuditReader auditReader = AuditReaderFactory.get(this.entityManager);
List<Number> revisionList = auditReader.getRevisions(this.jpaEntityInformation.getJavaType(), identifier);
if (revisionList == null || revisionList.isEmpty()) {
return null;
}
Set<Number> revisionSet = new LinkedHashSet<Number>(revisionList);
return auditReader.findRevisions(this.jpaEntityInformation.getJavaType(), revisionSet);
}
#Override
public void delete(Iterable<? extends T> entities) {
super.delete(entities);
super.flush();
}
#Override
public void delete(T entity) {
super.delete(entity);
super.flush();
}
}
public class AbstractCrudService<T extends IdentifiableEntity> implements CrudService<T> {
#Autowired(required=false)
private CrudRepository<T> repository;
#Autowired(required=false)
private NotificationComponent<T> notificationComponent;
private NotificationContext<T> geNotificationContext(String action, List<T> payload) {
DefaultNotificationContext<T> defaultNotificationContext = new DefaultNotificationContext<T>();
/*defaultNotificationContext.setAction(action);
defaultNotificationContext.setObject(this.domainClazz.getSimpleName());
defaultNotificationContext.setInstant(Instant.now());
defaultNotificationContext.setResponsibleId(null);
defaultNotificationContext.setPayload(payload);*/
return defaultNotificationContext;
}
private NotificationContext<T> geNotificationContext(String action, Page<T> payload) {
return geNotificationContext(action, payload.getContent());
}
private NotificationContext<T> geNotificationContext(String action, T payload) {
List<T> payloadList = new ArrayList<T>();
payloadList.add(payload);
return geNotificationContext(action, payloadList);
}
#Override
#Transactional(dontRollbackOn = LongTermRunningException.class)
#TypeTaskCriteria(pre = PreSaveTask.class, post = PostSaveTask.class, referenceGenericType = AbstractCrudService.class)
public List<T> save(List<T> objects)
throws ConcurrentModificationException, UnexpectedException {
List<T> savedObjectList = this.repository.save(objects);
if (this.notificationComponent != null) {
this.notificationComponent.notify(geNotificationContext(NotificationContext.SAVE, savedObjectList));
}
return savedObjectList;
}
#Override
#Transactional(dontRollbackOn = LongTermRunningException.class)
#TypeTaskCriteria(pre = PreSaveTask.class, post = PostSaveTask.class, referenceGenericType = AbstractCrudService.class)
public T save(T object) throws ConcurrentModificationException, UnexpectedException {
T savedObject = this.repository.save(object);
if (this.notificationComponent != null) {
this.notificationComponent.notify(geNotificationContext(NotificationContext.SAVE, savedObject));
}
return savedObject;
}
#Override
#TypeTaskCriteria(pre = PreRetrieveTask.class, post = PostRetrieveTask.class, referenceGenericType = AbstractCrudService.class)
public Page<T> retrieve(
#P(PAGE_NUMBER) final Integer pageNumber,
#P(PAGE_SIZE) final Integer pageSize,
#P(FILTER_EXPRESSION) final String filterExpression,
#P(SORT_EXPRESSION) final String sortExpression,
#P(EXPAND_EXPRESSION) final String expandExpression,
#P(PARAMETERS) final Map<String, String> parameters) throws InvalidParameterException, UnexpectedException {
DefaultRetrieveTaskContext context = TaskContextHolder.getContext();
BooleanExpression booleanExpression = context.getBooleanExpression();
Page<T> page = null;
try {
page = new Page<T>(this.repository.findAll(pageNumber, pageSize, booleanExpression, filterExpression, sortExpression, expandExpression));
} catch (InvalidFilterExpressionException | InvalidSortExpressionException
| InvalidExpandExpressionException e) {
throw new UnexpectedException(e);
}
if (this.notificationComponent != null) {
this.notificationComponent.notify(geNotificationContext(NotificationContext.RETRIEVE, page));
}
return page;
}
#Override
#TypeTaskCriteria(pre = PreRetrieveTask.class, post = PostRetrieveTask.class, referenceGenericType = AbstractCrudService.class)
public T retrieve(BigInteger identifyer, String expandExpression) throws NotFoundException, UnexpectedException {
RetrieveTaskContext context = TaskContextHolder.getContext();
BooleanExpression booleanExpression = context.getBooleanExpression();
T object = null;
try {
object = this.repository.findOne(identifyer, booleanExpression, expandExpression);
} catch (InvalidExpandExpressionException invalidExpandExpressionException) {
throw new UnexpectedException(invalidExpandExpressionException);
}
if (this.notificationComponent != null) {
this.notificationComponent.notify(geNotificationContext(NotificationContext.RETRIEVE, object));
}
return object;
}
#Override
#Transactional(dontRollbackOn = LongTermRunningException.class)
#TypeTaskCriteria(pre = PreDeleteTask.class, post = PostDeleteTask.class, referenceGenericType = AbstractCrudService.class)
public void delete(List<T> objects) throws ConcurrentModificationException, UnexpectedException {
this.repository.delete(objects);
if (this.notificationComponent != null) {
this.notificationComponent.notify(geNotificationContext(NotificationContext.DELETE, (List<T>) null));
}
}
#Override
#Transactional(dontRollbackOn = LongTermRunningException.class)
#TypeTaskCriteria(pre = PreDeleteTask.class, post = PostDeleteTask.class, referenceGenericType = AbstractCrudService.class)
public void delete(T object) throws ConcurrentModificationException, UnexpectedException {
this.repository.delete(object);
if (this.notificationComponent != null) {
this.notificationComponent.notify(geNotificationContext(NotificationContext.DELETE, (T) null));
}
}
}
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(value = "br.org.ccee", repositoryFactoryBeanClass = CrudRepositoryFactoryBean.class)
#EnableAspectJAutoProxy
public class ServiceConfiguration {
#Bean
//#Scope("request")
public ServiceContext serviceContext() {
DefaultServiceContext defaultServiceContext = new DefaultServiceContext();
defaultServiceContext.setInstant(Instant.now());
defaultServiceContext.setUserId(new BigInteger("33"));
defaultServiceContext.setTenantId(new BigInteger("69"));
return defaultServiceContext;
}
#Bean
public TenantEventListener tenantEventListener() {
return new TenantEventListener();
}
#Bean
public AuditEventListener auditEventListener() {
return new AuditEventListener();
}
#Bean
public EventListenerRegistry eventListenerRegistry(
LocalContainerEntityManagerFactoryBean entityManagerFactory,
TenantEventListener tenantEventListener,
AuditEventListener auditEventListener) {
SessionFactoryImpl sessionFactoryImpl = (SessionFactoryImpl) entityManagerFactory.getNativeEntityManagerFactory();
ServiceRegistryImplementor serviceRegistryImplementor = sessionFactoryImpl.getServiceRegistry();
EventListenerRegistry eventListenerRegistry = serviceRegistryImplementor.getService(EventListenerRegistry.class);
eventListenerRegistry.prependListeners(EventType.PRE_INSERT, auditEventListener);
eventListenerRegistry.prependListeners(EventType.PRE_INSERT, tenantEventListener);
eventListenerRegistry.prependListeners(EventType.PRE_UPDATE, auditEventListener);
return eventListenerRegistry;
}
}
public class DefaultCrudRepository<T extends IdentifiableEntity> extends QueryDslJpaRepository<T, BigInteger>
implements CrudRepository<T> {
private static final EntityPathResolver DEFAULT_ENTITY_PATH_RESOLVER = SimpleEntityPathResolver.INSTANCE;
private JpaEntityInformation<T, BigInteger> jpaEntityInformation;
private EntityManager entityManager;
private EntityPath<T> path;
private PathBuilder<T> builder;
private Querydsl querydsl;
#Autowired
private SortComponent sortComponent;
#Autowired
private PageComponent pageComponent;
#Autowired
private FilterComponent filterComponent;
#Autowired
private ExpandComponent expandComponent;
I have a recyclerview with a header achieved by using two different element types. In my header there is an edit text which I want to use for filtering the nonheader elements of the list. Below is my current implementation, I have one concern and one problem with it.
My concern is that what I am doing in publishResults with the notifyItemRangeRemoved and notifyItemInserted is the wrong way to update the recycler view. I originally was doing notifyDatasetChanged but his would cause the header row to be refreshed too and the edit text to lose focus. What I really want is a way to refresh only the item rows and leave the header row untouched.
My current problem is that with the existing code if I scroll down too much the edit text looses focus. I want the edit text to keep focus even if I scroll to the bottom of the list.
The code used to use a ListView with setHeaderView and that worked somehow so there must be someway of achieving the goal just not sure what the trick with a recycler view is. Any help is much appreciated.
public class SideListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements Filterable {
private static final int TYPE_HEADER = 0;
private static final int TYPE_ITEM = 1;
private final List<String> data;
public List<String> filteredData;
private HeaderActionListener headerActionListener;
public SideListAdapter(Context context, ArrayList<String> data, HeaderActionListener headerActionListener) {
this.data = data;
filteredData = new ArrayList<>(data);
this.context = context;
this.headerActionListener = headerActionListener;
}
#Override
public Filter getFilter() {
return new TestFilter();
}
static class SideListItem extends RecyclerView.ViewHolder {
LinearLayout baseLayout;
public SideListItem(View itemView) {
super(itemView);
baseLayout = (LinearLayout) itemView.findViewById(R.id.settings_defaultcolor);
}
}
class SideListHeader extends SideListHeader {
EditText sort;
public SideListHeaderLoggedIn(View itemView) {
super(itemView);
sort = (EditText) itemView.findViewById(R.id.sort);
}
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == TYPE_ITEM) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item, parent, false);
return new SideListItem(v);
} else if (viewType == SideListHeader) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.header, parent, false);
return new SideListHeader(v);
}
throw new RuntimeException("there is no type that matches the type " + viewType + " + make sure your using types correctly");
}
public interface HeaderActionListener {
boolean onSortEditorAction(TextView arg0, int arg1, KeyEvent arg2);
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
if (holder instanceof SideListHeader) {
final SideListHeader sideListHeader = (SideListHeader) holder;
sideListHeader.sort.setOnEditorActionListener(new TextView.OnEditorActionListener() {
#Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
}
});
sideListHeader.sort.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) {
}
#Override
public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {
}
#Override
public void afterTextChanged(Editable editable) {
String result = sideListHeader.sort.getText().toString().replaceAll(" ", "");
getFilter().filter(result);
}
});
}
if (holder instanceof SideListItem) {
// Inflate normal item //
}
}
// need to override this method
#Override
public int getItemViewType(int position) {
if (isPositionHeader(position)) {
return TYPE_HEADER;
}
return TYPE_ITEM;
}
private boolean isPositionHeader(int position) {
return position == 0;
}
//increasing getItemcount to 1. This will be the row of header.
#Override
public int getItemCount() {
return filteredData.size() + 1;
}
private class TestFilter extends Filter {
#Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
String prefix = constraint.toString().toLowerCase();
if (prefix.isEmpty()) {
ArrayList<String> list = new ArrayList<>(data);
results.values = list;
results.count = list.size();
} else {
final ArrayList<String> list = new ArrayList<>(data);
final ArrayList<String> nlist = new ArrayList<>();
for (int i = 0 ; i < list.size(); i++) {
String item = list.get(i);
if (item.contains(prefix)) {
nlist.add(item);
}
}
results.values = nlist;
results.count = nlist.size();
}
return results;
}
#SuppressWarnings("unchecked")
#Override
protected void publishResults(CharSequence constraint, FilterResults results) {
notifyItemRangeRemoved(1, getItemCount()-1);
filteredData.clear();
filteredData.addAll((List<String>)results.values);
for(int i = 1; i < getItemCount() - 1; i++){
notifyItemInserted(i);
}
}
}
}
I'm not sure how correct this way is, but in my code I implemented it like that
private var headerList: List<HeaderItem> = listOf(HeaderItem("Title"))
private fun searchItem(items: List<Items>, query: String) {
items.filterIsInstance<MainItem>().filter { filteredItems ->
filteredItems.header.lowercase().contains(query.lowercase())
}.let { searchedItems ->
rvAdapter.submitList(headerList + searchedItems)
}
}
This way I was able to preserve header element when I did my search
I have to turn my dataTable to lazy dataTable. How can I do it? Here is my sample code:
IndexBean.java
#Name("indexBean")
public class IndexBean implements Serializable {
private static final long serialVersionUID = 1L;
#Inject
UserService userService;
private List<User> userList;
#Inject
MessageService messageService;
private List<Message> messageList;
private Integer dataTableRendered;
public void getAllUser() {
setDataTableRendered(1);
userList = new ArrayList<User>();
userList = userService.getAllUser();
if (userList.size() > 0) {
addMessageSuccess("Success", "Success");
} else {
addMessageError("Failure", "Failure!");
}
}
public void getAllMessages() {
setDataTableRendered(2);
messageList = new ArrayList<Message>();
messageList = messageService.getAllMessages();
if (messageList.size() > 0) {
addMessageSuccess("Success", "Success");
} else {
addMessageError("Failure", "Failure!");
}
}
public void addMessageSuccess(String summary, String detail) {
FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_INFO, summary, detail);
FacesContext.getCurrentInstance().addMessage(null, message);
}
public void addMessageError(String summary, String detail) {
FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR, summary, detail);
FacesContext.getCurrentInstance().addMessage(null, message);
}
public List<User> getUserList() {
return userList;
}
public void setUserList(List<User> userList) {
this.userList = userList;
}
public List<Message> getMessageList() {
return messageList;
}
public void setMessageList(List<Message> messageList) {
this.messageList = messageList;
}
public Integer getDataTableRendered() {
return dataTableRendered;
}
public void setDataTableRendered(Integer dataTableRendered) {
this.dataTableRendered = dataTableRendered;
}
}
index.xhtml
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:p="http://primefaces.org/ui"
xmlns:h="http://java.sun.com/jsf/html">
<h:head></h:head>
<p:layout fullPage="true">
<!-- Status Waiting Dialog -->
<p:ajaxStatus onstart="PF('statusDialog').show()"
onsuccess="PF('statusDialog').hide()" />
<p:dialog widgetVar="statusDialog" modal="true" draggable="false"
closable="false" resizable="false" showHeader="true"
header="Please wait...">
<p:graphicImage value="/images/loader.gif"
style=" float: left;position: relative;left: 30%;" />
</p:dialog>
<!-- Left Layout -->
<p:layoutUnit position="west" id="west">
<p:growl autoUpdate="true" id="infoMessage" showDetail="true"
showSummary="true" sticky="false" />
<p:commandButton value="Get Users"
actionListener="#{indexBean.getAllUser}"
update="infoMessage,centerForm,centerForm:userTable" />
<p:commandButton value="Get Messages"
actionListener="#{indexBean.getAllMessages}"
update="infoMessage,centerForm,centerForm:messageRaporTable" />
</p:layoutUnit>
<!-- Center Layout -->
<p:layoutUnit position="center" id="center">
<h:form id="centerForm">
<!-- User dataTable -->
<p:dataTable id="userTable" var="user"
value="#{indexBean.userList }" paginator="true" rows="10"
lazy="true" rendered="#{indexBean.dataTableRendered ==1}"
paginatorTemplate="{CurrentPageReport} {FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink} {RowsPerPageDropdown}"
rowsPerPageTemplate="5,10,15,20,25,50,100">
<p:column headerText="userid" sortBy="#{user.userId}"
filterBy="#{user.userId}">
<h:outputText value="#{user.userId}"></h:outputText>
</p:column>
<p:column headerText="username" sortBy="#{user.userName}"
filterBy="#{user.userName}">
<h:outputText value="#{user.userName}"></h:outputText>
</p:column>
<p:column headerText="firstname" sortBy="#{user.userFirstName}"
filterBy="#{user.userFirstName}">
<h:outputText value="#{user.userFirstName}"></h:outputText>
</p:column>
</p:dataTable>
<p:column headerText="Message Id"
filterBy="#{message.messageId}"
sortBy="#{message.messageId}">
<h:outputText value="#{message.messageId}" />
</p:column>
<p:column headerText="Sender"
sortBy="#{message.messageSender}" filterBy="#{message.messageSender}">
<h:outputText value="#{message.messageSender}" />
</p:column>
<p:column headerText="Receiver"
sortBy="#{message.messageReceiver}"
filterBy="#{message.messageReceiver}">
<h:outputText value="#{message.messageReceiver}" />
</p:column>
<p:column headerText="Subject"
sortBy="#{message.messageSubject}"
filterBy="#{message.messageSubject}">
<h:outputText value="#{message.messageSubject}" />
</p:column>
<p:column headerText="Message"
sortBy="#{message.messageText}"
filterBy="#{message.messageText}">
<h:outputText value="#{message.messageText}" />
</p:column>
</p:dataTable>
</h:form>
</p:layoutUnit>
</p:layout>
UserService.java
public interface UserService {
public List<User> getAllUser();
}
UserServiceImpl.java
#Service("userService")
#Component
#Transactional
public class UserServiceImpl implements UserService {
#Autowired
private UserDAO userDAO;
#Override
public List<User> getAllUser() {
return userDAO.getAllUser();
}
}
UserDAO.java
public interface UserDAO {
public List<User> getAllUser();
}
UserDAOImpl.java - Actually this sql more complicated.
#Component
#Repository("userDAO")
public class UserDAOImpl extends HibernateDaoSupport implements UserDAO {
#SuppressWarnings("unchecked")
#Override
public List<User> getAllUser() {
List<User> userList = new ArrayList<User>();
String sql = "select * from SECRETDB.USER";
try {
Query query = getSession().createSQLQuery(sql).addScalar("userOID", StringType.INSTANCE)
.addScalar("userName", StringType.INSTANCE).addScalar("firstName", StringType.INSTANCE)
.setResultTransformer(Transformers.aliasToBean(User.class)).setCacheable(false)
.setCacheMode(CacheMode.IGNORE);
userList = query.list();
getSession().clear();
} catch (HibernateException e) {
getSession().clear();
e.printStackTrace();
}
return userList;
}
User.java
public class User {
private String userOID;
private String userName;
private String firstName;
public String getUserOID() {
return userOID;
}
public void setUserOID(String userOID) {
this.userOID = userOID;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
}
HibernateDaoSupport.java
public abstract class HibernateDaoSupport {
#Autowired
private SessionFactory sessionFactory;
protected Session getSession() {
return sessionFactory.getCurrentSession();
}
}
In your datatable in index.xhtml i see
value="#{indexBean.userList }" paginator="true" rows="10"
lazy="true"
where userList is pointing to
private List<User> userList;
This is wrong. It should be
private LazyUserDataModel<User> userList
Please read showcase and userguide pages 167-168 to implement LazyDataModel. Nobody here is going to teach you to implement it. An example of a generic lazydatamodel using hibernate and JPA is below.
public class GenericLazyDataModel<T> extends LazyDataModel<T> {
#Transient
protected static Logger debugLogger = Logger.getLogger("DebugLogger");
protected EntityManager entityManager;
protected String hqlQuery;
private String countQuery;
protected List<SortFieldInfoDTO> defaultSortFields;
protected List<T> currentObjectList;
protected List<Object> parameterValues;
protected List<String> parameterName;
protected List<Object> filterFieldValues;
protected List<String> filterFieldName;
public GenericLazyDataModel(EntityManager entityManager, String hqlQuery,
String countQuery, List<SortFieldInfoDTO> sortFieldInfos,
List<Object> parameterValues, List<String> parameterName) {
super();
this.entityManager = entityManager;
this.hqlQuery = hqlQuery;
this.countQuery = countQuery;
this.defaultSortFields = sortFieldInfos;
this.parameterValues = parameterValues;
this.parameterName = parameterName;
this.setRowCount(calculateRecordCount());
}
#Override
public List<T> load(int first, int pageSize, String sortField,
SortOrder sortOrder, Map<String, Object> filters) {
String sqlQuery = hqlQuery;
int filterCount = 0;
if(sortOrder==null){
sortOrder=SortOrder.ASCENDING;
}
if (filterFieldValues != null && filterFieldName != null) {
filterFieldValues.clear();
filterFieldName.clear();
}
for (Iterator<String> iterator = filters.keySet().iterator(); iterator
.hasNext();) {
String filterField = iterator.next();
if (debugLogger.isInfoEnabled()) {
debugLogger.info("Filter Field Name " + filterField);
}
// Check if the HQL Query already has a where clause
if (sqlQuery.toLowerCase().indexOf("where") != -1) {
sqlQuery = sqlQuery + " and " + filterField
+ " like :filterFieldValue" + filterCount;
} else {
sqlQuery = sqlQuery + " where " + filterField
+ " like :filterFieldValue" + filterCount;
}
if (filterFieldValues == null) {
filterFieldValues = new ArrayList<Object>();
}
if (filterFieldName == null) {
filterFieldName = new ArrayList<String>();
}
filterFieldValues.add(((String)filters.get(filterField)).toUpperCase() + "%");
filterFieldName.add("filterFieldValue" + filterCount);
filterCount++;
}
//************************
boolean isOrderFieldFound=Boolean.FALSE;
if(sortField !=null && !sortField.isEmpty()){
isOrderFieldFound=Boolean.TRUE;
sqlQuery = sqlQuery + " order by " + sortField+(sortOrder==SortOrder.ASCENDING?" ASC ":" DESC ");
}
if(isOrderFieldFound){
if(defaultSortFields !=null && !defaultSortFields.isEmpty()){
for(SortFieldInfoDTO fld:defaultSortFields){
if(!sqlQuery.contains(fld.getAliasedFieldName())) sqlQuery=sqlQuery+" , "+fld.getAliasedFieldName()+(fld.getSortOrder()==SortOrder.ASCENDING?" ASC ":" DESC ");
}
}
}else{
if(defaultSortFields !=null && !defaultSortFields.isEmpty()){
int index=0;
for(SortFieldInfoDTO fld:defaultSortFields){
if(index==0){
if(!sqlQuery.contains(fld.getAliasedFieldName())) sqlQuery = sqlQuery + " order by " + fld.getAliasedFieldName()+(fld.getSortOrder()==SortOrder.ASCENDING?" ASC ":" DESC ");
index++;
}else{
if(!sqlQuery.contains(fld.getAliasedFieldName())) sqlQuery=sqlQuery+" , "+fld.getAliasedFieldName()+(fld.getSortOrder()==SortOrder.ASCENDING?" ASC ":" DESC ");
}
}
}
}
//*********************************
if (debugLogger.isInfoEnabled())debugLogger.info("sqlQuery:"+sqlQuery);
Query query = entityManager.createQuery(sqlQuery);
int parameterCount = 0;
if (parameterValues != null) {
for (Iterator<Object> iterator = parameterValues.iterator(); iterator
.hasNext();) {
Object type = iterator.next();
query.setParameter(parameterName.get(parameterCount), type);
parameterCount++;
}
}
int parameterCount1 = 0;
if (filterFieldValues != null && !filterFieldValues.equals("")) {
for (Iterator<Object> iterator = filterFieldValues.iterator(); iterator
.hasNext();) {
Object type = iterator.next();
query.setParameter(filterFieldName.get(parameterCount1), type);
parameterCount1++;
}
}
if (debugLogger.isInfoEnabled())debugLogger.info("first page value " + first);
if (debugLogger.isInfoEnabled())debugLogger.info("pageSize page value " + pageSize);
query.setFirstResult(first);
query.setMaxResults(pageSize);
currentObjectList = query.getResultList();
return currentObjectList;
}
private int calculateRecordCount() {
Query query = entityManager.createQuery(countQuery);
int parameterCount = 0;
if (parameterValues != null) {
for (Iterator<Object> iterator = parameterValues.iterator(); iterator
.hasNext();) {
Object type = iterator.next();
query.setParameter(parameterName.get(parameterCount), type);
parameterCount++;
}
}
if (filterFieldValues != null) {
for (Iterator<Object> iterator = filterFieldValues.iterator(); iterator
.hasNext();) {
Object type = iterator.next();
query.setParameter(filterFieldName.get(parameterCount), type);
parameterCount++;
}
}
Long rowCount = (Long) query.getSingleResult();
if (debugLogger.isInfoEnabled()) {
debugLogger.info("rowCount Application Data Model" + rowCount);
}
return rowCount.intValue();
}
public List<T> getCurrentObjectList() {
return currentObjectList;
}
}
Using this and learning on your own figure this out and good luck.
this is how I am doing it.
public class ImagePaginator extends LazyDataModel<PhotoObject> {
private ISession session;
private int startId;
private int endId;
private User user;
private static final long serialVersionUID = 1L;
private SearchController searchController;
public ImagePaginator(User user, SearchController searchController) {
this.session = user.getSession();
this.user = user;
this.searchController = searchController;
}
#Override
public List<PhotoObject> load(int first, int pageSize, String sortField, SortOrder sortOrder,
Map<String, Object> filters) {
// collect paginated data from server
Collection<IObject> photos = searchController.getResultAccount().getPaginatedObjects(first, first + 3,
SystemConstants.PHOTO);
List<PhotoObject> data = new ArrayList<PhotoObject>();
for (IObject resultPhoto : photos) {
IPhoto photo = (IPhoto) resultPhoto;
data.add(new PhotoObject(photo));
}
this.setRowCount(data.size() + 1);
return data;
}
#Override
public Object getRowKey(PhotoObject photoObject) {
return photoObject.getId();
}
public ISession getSession() {
return session;
}
public void setSession(ISession session) {
this.session = session;
}
public int getStartId() {
return startId;
}
public void setStartId(int startId) {
this.startId = startId;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public SearchController getSearchController() {
return searchController;
}
public void setSearchController(SearchController searchController) {
this.searchController = searchController;
}
public int getEndId() {
return endId;
}
public void setEndId(int endId) {
this.endId = endId;
}
just like you I use a service to retrieve the results. The sql that I am sending (Depending in the situation) is:
public List<IEntity> retrievePaginatedById(String classType, String targetId, String status, int id, int offset,
int limit, boolean forContactRequestNotification) {
SessionFactory sessionFactory = createSessionFactory();
Session session = sessionFactory.openSession();
String hql = null;
if (forContactRequestNotification) {
hql = "FROM " + classType + " D WHERE " + targetId + "=" + "'" + id + "' AND D.status = '" + status
+ "' ORDER BY D.id DESC";
} else {
hql = "FROM " + classType + " D WHERE " + targetId + "=" + "'" + id + "'" + " ORDER BY D.id DESC";
}
Query query = session.createQuery(hql).setFirstResult(offset).setMaxResults(limit);
#SuppressWarnings("unchecked")
List<IEntity> results = query.list();
session.close();
sessionFactory.close();
return results;
}
I try to make Table cells editable. I managed to do this with two Collumns that have String values in it, but I cant make this with columns that represent Integer values.
Places with X is where compiler get the error:
The method setCellFactory(Callback<TableColumn<DataModel,Integer>,TableCell<DataModel,Integer>>) in the type TableColumn<DataModel,Integer> is not applicable for the arguments (Callback<TableColumn<DataModel,String>,TableCell<DataModel,String>>)
and places with XX is where compiler get the error:
The method setOnEditCommit(EventHandler<TableColumn.CellEditEvent<DataModel,Integer>>) in the type TableColumn<DataModel,Integer> is not applicable for the arguments ((CellEditEvent<DataModel, Integer> event) -> {})
Heres the code:
public void initialize(URL location, ResourceBundle resources) {
//Tworzymy sobie kolumny, które będą odpowiadać oraz przyjmować konretne dane
TableColumn<DataModel, String> nameColumn = new TableColumn<DataModel, String>("Name");
nameColumn.setMinWidth(100);
TableColumn<DataModel, String> surnameColumn = new TableColumn<DataModel, String>("Surname");
surnameColumn.setMinWidth(100);
TableColumn<DataModel, Integer> ageColumn = new TableColumn<DataModel, Integer>("Age");
ageColumn.setMinWidth(100);
TableColumn<DataModel, Integer> telNumberColumn = new TableColumn<DataModel, Integer>("Tel. Number");
telNumberColumn.setMinWidth(100);
//dodajemy kolumny do okna
tableView.getColumns().addAll(nameColumn,surnameColumn,ageColumn,telNumberColumn);
//podajemy nazwy zmiennych, których wartości mają się wyświetlać w poszczególnych kolumnach
nameColumn.setCellValueFactory(new PropertyValueFactory<>("sName"));
surnameColumn.setCellValueFactory(new PropertyValueFactory<>("sSurname"));
ageColumn.setCellValueFactory(new PropertyValueFactory<>("iAge"));
telNumberColumn.setCellValueFactory(new PropertyValueFactory<>("iPhoneNumber"));
//Sprawiamy że poszczególne kolumny stają się edytowalne
nameColumn.setCellFactory(TextFieldTableCell.<DataModel>forTableColumn());
nameColumn.setOnEditCommit((CellEditEvent<DataModel, String> event) -> {
((DataModel) event.getTableView().getItems(). get(event.getTablePosition().getRow())). setsName(event.getNewValue());
});
surnameColumn.setCellFactory(TextFieldTableCell.<DataModel>forTableColumn());
surnameColumn.setOnEditCommit((CellEditEvent<DataModel, String> event) -> {
((DataModel) event.getTableView().getItems(). get(event.getTablePosition().getRow())). setsSurname(event.getNewValue());
});
X ageColumn.setCellFactory(TextFieldTableCell.<DataModel>forTableColumn());
XX ageColumn.setOnEditCommit((CellEditEvent<DataModel, Integer> event) -> {
// ((DataModel) event.getTableView().getItems(). get(event.getTablePosition().getRow())). setiAge(Integer.valueOf(event.getNewValue()));
});
X telNumberColumn.setCellFactory(TextFieldTableCell.<DataModel>forTableColumn());
XX telNumberColumn.setOnEditCommit((CellEditEvent<DataModel, Integer> event) -> {
// ((DataModel) event.getTableView().getItems(). get(event.getTablePosition().getRow())). setiPhoneNumber(Integer.valueOf(event.getNewValue()));
});
tableView.setPlaceholder(new Label("Pust tabelka!"));//jaki element dodać jeśli tabelka nie jest wyświetlona
tableView.setEditable(true);
tableView.setItems(dataList); //wczytujemy dane do przygotowanej tabelki
buttAdd.setOnAction((ActionEvent e) -> {
buttAddAction(e);
});
}
Im taking oracle TableView tutorial, and its quite difficult. Help.
The issue is that TextFieldTableCell.forTableColumn() is typed to a String value. See the default implementation:
public static <S> Callback<TableColumn<S,String>, TableCell<S,String>> forTableColumn() {
return forTableColumn(new DefaultStringConverter());
}
What you need is the TextFieldTableCell with an IntegerStringConverter, for example:
ageColumn.setCellFactory(TextFieldTableCell.<DataModel, Integer>forTableColumn(new IntegerStringConverter()));
I searched through a lot of answers and I've borrowed/extended/merged to this solution. Edits are committed when focus moves from edited cell. I have a public class for each datatype that can be represented in a table: EditingTextCell, EditingIntegerCell etc. These public classes can be applied to any table provided that the data is represented as an observable list of a class that accesses the data to be displayed as properties. I publish this solution because I was faced with creating a class for each column of each table in my application. Currently, the double value and combobox cell versions are tied to specific columns of specific tables. I'll do a generalized version of these as time permits. Please forgive my not presenting the source links -- I forgot to bookmark them as I perused them.
Java documentation suggests that easier ways of doing this are forthcoming.
Example usage for Integer field:
TableColumn<Factor, Number> noLevelsCol =
new TableColumn<>("No. Levels");
noLevelsCol.setCellValueFactory(
new PropertyValueFactory("numberLevels"));
noLevelsCol.setMinWidth(40);
noLevelsCol.setCellFactory(col -> new EditingIntegerCell<>());
noLevelsCol.setOnEditCommit((CellEditEvent<Factor, Number> t) -> {
((Factor) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setNumberLevels(t.getNewValue().intValue());
});
Example usage for String field:
TableColumn<Factor, String> nameCol = new TableColumn<>("Name");
nameCol.setMinWidth(60);
nameCol.setCellValueFactory(
new PropertyValueFactory("factorName"));
nameCol.setCellFactory(cellFactory);
nameCol.setOnEditCommit((CellEditEvent<Factor, String> t) -> {
((Factor) t.getTableView().getItems().get(
t.getTablePosition().getRow())
).setFactorName(t.getNewValue());
});
Definition of Factor class:
public class Factor {
private final IntegerProperty factorID = new SimpleIntegerProperty();
public IntegerProperty getFactorID() { return factorID; }
private StringProperty factorName = new SimpleStringProperty();
public void setFactorName(String value) {
factorNameProperty().set(value); }
public String getFactorName() { return factorNameProperty().get(); }
public StringProperty factorNameProperty() {
if (factorName == null) factorName =
new SimpleStringProperty(this, "factorName");
return factorName;
}
private IntegerProperty numberLevels = new SimpleIntegerProperty();
public void setNumberLevels(int value) {
numberLevelsProperty().set(value); }
public IntegerProperty getNumberLevels() { return numberLevels; }
public IntegerProperty numberLevelsProperty() {
if (numberLevels == null) numberLevels =
new SimpleIntegerProperty(this, "numberLevels");
return numberLevels;
}
private StringProperty listOfLevels = new SimpleStringProperty();
public void setListOfLevels(String value) {
listOfLevelsProperty().set(value); }
public String getListOfLevels() { return listOfLevelsProperty().get(); }
public StringProperty listOfLevelsProperty() {
if (listOfLevels == null) listOfLevels =
new SimpleStringProperty(this, "listOfLevels");
return listOfLevels;
}
// Constructors
public Factor(int factorID, String factorName) {
this.factorID.set(factorID);
this.factorName.set(factorName);
this.numberLevels.set(1);
this.listOfLevels.set("-1, 1");
}
public Factor(int factorID, String factorName, int numberLevels,
String listOfLevels) {
this.factorID.set(factorID);
this.factorName.set(factorName);
this.numberLevels.set(numberLevels);
this.listOfLevels.set(listOfLevels);
}
#Override
public String toString() {
return "Factor{" + "factorName=" + factorName + '}';
}
public String[] getLevels() {
return listOfLevels.getValue().split(",");
}
}
Loading the data into the table
final ObservableList factorList =
FXCollections.observableArrayList(
new Factor(1, "Factor1", 2, "-1, 1")
);
factorTableView.setEditable(true);
factorTableView.getColumns().clear();
factorTableView.setItems(factorList);
boolean addAll;
addAll = factorTableView.getColumns().addAll(idCol,
nameCol, noLevelsCol, levelsCol);
The EditingIntegerCell class
public class EditingIntegerCell extends TableCell {
private TextField textField;
private final Pattern intPattern = Pattern.compile("-?\\d+");
public EditingIntegerCell() {
}
#Override
public void startEdit() {
if (!isEmpty()) {
super.startEdit();
createTextField();
setText(null);
setGraphic(textField);
textField.selectAll();
}
}
#Override
public void cancelEdit() {
super.cancelEdit();
setText((String) getItem().toString());
setGraphic(null);
}
#Override
public void updateItem(Number item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
if (isEditing()) {
if (textField != null) {
textField.setText(getString());
}
setText(null);
setGraphic(textField);
} else {
setText(getString());
setGraphic(null);
}
}
}
private void createTextField() {
textField = new TextField(getString());
textField.setMinWidth(this.getWidth() - this.getGraphicTextGap()* 2);
textField.focusedProperty().addListener(
(ObservableValue<? extends Boolean> arg0, Boolean arg1, Boolean arg2)
-> {
if (!arg2) {
processEdit();
}
});
}
private void processEdit() {
String text = textField.getText();
if (intPattern.matcher(text).matches()) {
commitEdit(Integer.parseInt(text));
} else {
cancelEdit();
}
}
private String getString() {
return getItem() == null ? "" : getItem().toString();
}
}
** The EditingTextCell class **
public class EditingTextCell extends TableCell {
private TextField textField;
public EditingTextCell() {
}
#Override
public void startEdit() {
if (!isEmpty()) {
super.startEdit();
createTextField();
setText(null);
setGraphic(textField);
textField.selectAll();
}
}
#Override
public void cancelEdit() {
super.cancelEdit();
setText((String) getItem());
setGraphic(null);
}
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
if (isEditing()) {
if (textField != null) {
textField.setText(getString());
}
setText(null);
setGraphic(textField);
} else {
setText(getString());
setGraphic(null);
}
}
}
private void createTextField() {
textField = new TextField(getString());
textField.setMinWidth(this.getWidth() - this.getGraphicTextGap()* 2);
textField.focusedProperty().addListener(
(ObservableValue<? extends Boolean> arg0, Boolean arg1, Boolean arg2)
-> {
if (!arg2) {
commitEdit(textField.getText());
}
});
}
private String getString() {
return getItem() == null ? "" : getItem();
}
}