From e711dfb2bc5d7a55bb6bad32fc94c4a06dab5cde Mon Sep 17 00:00:00 2001 From: nmihajlovski Date: Wed, 4 May 2016 21:27:27 +0200 Subject: [PATCH] Redesigned the paging in the Grid widget. --- .../main/java/org/rapidoid/commons/Coll.java | 2 +- .../src/main/java/org/rapidoid/util/Msc.java | 28 ++++-- .../src/main/java/org/rapidoid/gui/GUI.java | 4 +- .../src/main/java/org/rapidoid/gui/Grid.java | 99 ++++++++++++------- .../src/main/java/org/rapidoid/gui/Pager.java | 2 +- .../org/rapidoid/gui/base/AbstractWidget.java | 16 ++- ...bleWidgetTest.java => GridWidgetTest.java} | 10 +- .../testTableWidget/map-grid | 0 .../testTableWidget/persons-grid | 0 .../rapidoid/goodies/ClasspathHandler.java | 4 +- .../src/main/java/org/rapidoid/goodies/X.java | 9 +- 11 files changed, 105 insertions(+), 69 deletions(-) rename rapidoid-gui/src/test/java/org/rapidoid/widget/{TableWidgetTest.java => GridWidgetTest.java} (83%) rename rapidoid-gui/src/test/resources/test-results/{TableWidgetTest => GridWidgetTest}/testTableWidget/map-grid (100%) rename rapidoid-gui/src/test/resources/test-results/{TableWidgetTest => GridWidgetTest}/testTableWidget/persons-grid (100%) diff --git a/rapidoid-commons/src/main/java/org/rapidoid/commons/Coll.java b/rapidoid-commons/src/main/java/org/rapidoid/commons/Coll.java index f5724622a8..5b56849030 100644 --- a/rapidoid-commons/src/main/java/org/rapidoid/commons/Coll.java +++ b/rapidoid-commons/src/main/java/org/rapidoid/commons/Coll.java @@ -286,7 +286,7 @@ public Set map(K src) throws Exception { }); } - public static Iterable range(Iterable items, int from, int to) { + public static List range(Iterable items, int from, int to) { U.must(from <= to, "'from' must be <= 'to'!"); if (from == to) { diff --git a/rapidoid-commons/src/main/java/org/rapidoid/util/Msc.java b/rapidoid-commons/src/main/java/org/rapidoid/util/Msc.java index 6d324567e4..cd82b5c670 100644 --- a/rapidoid-commons/src/main/java/org/rapidoid/util/Msc.java +++ b/rapidoid-commons/src/main/java/org/rapidoid/util/Msc.java @@ -21,6 +21,7 @@ import org.rapidoid.log.Log; import org.rapidoid.u.U; import org.rapidoid.validation.InvalidData; +import org.rapidoid.wrap.BoolWrap; import javax.validation.ConstraintViolation; import javax.validation.ConstraintViolationException; @@ -820,19 +821,26 @@ public static ErrCodeAndMsg getErrorCodeAndMsg(Throwable err) { return new ErrCodeAndMsg(code, msg); } - public static List range(Iterable items, int fromIndex, int toIndex) { - // TODO more efficient implementation - List list = U.list(items); + public static List page(Iterable items, int page, int pageSize) { + return Coll.range(items, (page - 1) * pageSize, page * pageSize); + } - fromIndex = U.limit(0, fromIndex, list.size()); - toIndex = U.limit(fromIndex, toIndex, list.size()); + public static List getPage(Iterable items, int page, int pageSize, Integer size, BoolWrap isLastPage) { + int pageFrom = Math.max((page - 1) * pageSize, 0); + int pageTo = (page) * pageSize + 1; - return U.list(list.subList(fromIndex, toIndex)); - } + if (size != null) { + pageTo = Math.min(pageTo, size); + } - public static List page(Iterable items, int page, int pageSize) { - return range(items, (page - 1) * pageSize, page * pageSize); - } + List range = Coll.range(items, pageFrom, pageTo); + isLastPage.value = range.size() < pageSize + 1; + + if (!isLastPage.value && !range.isEmpty()) { + range.remove(range.size() - 1); + } + return range; // 1 item extra, to test if there are more results + } } diff --git a/rapidoid-gui/src/main/java/org/rapidoid/gui/GUI.java b/rapidoid-gui/src/main/java/org/rapidoid/gui/GUI.java index ae2000b858..88139fa1a1 100644 --- a/rapidoid-gui/src/main/java/org/rapidoid/gui/GUI.java +++ b/rapidoid-gui/src/main/java/org/rapidoid/gui/GUI.java @@ -401,7 +401,7 @@ public static HtmlPage page(Object... contents) { return Cls.customizable(HtmlPage.class, new Object[]{AnyObj.flat(contents)}); } - public static Grid grid(Iterable items) { + public static Grid grid(Iterable items) { return Cls.customizable(Grid.class).items(items); } @@ -954,7 +954,7 @@ public static String uriFor(String baseUri, Object target) { } Object id = getIdentifier(target); - return id != null ? Msc.uri(baseUri, id + "", "view") : ""; + return id != null ? Msc.uri(baseUri, id + "") : ""; } public static String typeUri(Class entityType) { diff --git a/rapidoid-gui/src/main/java/org/rapidoid/gui/Grid.java b/rapidoid-gui/src/main/java/org/rapidoid/gui/Grid.java index b800aa0c9d..d9c098b83e 100644 --- a/rapidoid-gui/src/main/java/org/rapidoid/gui/Grid.java +++ b/rapidoid-gui/src/main/java/org/rapidoid/gui/Grid.java @@ -2,6 +2,7 @@ import org.rapidoid.annotation.Authors; import org.rapidoid.annotation.Since; +import org.rapidoid.commons.Coll; import org.rapidoid.gui.base.AbstractWidget; import org.rapidoid.html.Tag; import org.rapidoid.html.tag.TdTag; @@ -12,7 +13,9 @@ import org.rapidoid.model.Models; import org.rapidoid.model.Property; import org.rapidoid.u.U; +import org.rapidoid.util.Msc; import org.rapidoid.var.Var; +import org.rapidoid.wrap.BoolWrap; import java.util.Iterator; import java.util.List; @@ -52,51 +55,76 @@ public class Grid extends AbstractWidget { private volatile Mapper toUri; @Override - protected Tag render() { + protected Object render() { - Iterator it = items.iterator(); - Class type = it.hasNext() ? it.next().getClass() : Object.class; + Pager pager = noPager(); + boolean paging = pageSize > 0; + Iterable rows; + + BoolWrap isLastPage = new BoolWrap(); + + if (paging) { + String pageParam = "_p" + seq("pager"); + pager = GUI.pager(pageParam).min(1); + + Integer size = Coll.getSizeOrNull(items); + + if (size != null) { + int pages = (int) Math.ceil(size / (double) pageSize); + pager.max(pages); + } + + rows = Msc.getPage(items, pager.pageNumber(), pageSize, size, isLastPage); - Items itemsModel; - if (items instanceof Items) { - itemsModel = (Items) items; } else { - itemsModel = Models.beanItems(type, U.array(items)); + rows = items; } - final List props = itemsModel.properties(columns); + return renderGridPage(pager, rows, isLastPage.value); + } - int total = itemsModel.size(); - int pages = (int) Math.ceil(total / (double) pageSize); + private Object renderGridPage(Pager pager, Iterable rows, boolean isLastPage) { + Iterator it = rows.iterator(); + boolean hasData = it.hasNext(); - boolean ordered = !U.isEmpty(orderBy); - Var order = null; + if (isLastPage || !hasData) { + pager.max(pager.pageNumber()); + } - Items slice = itemsModel; + if (!hasData) { + return U.list(noDataAvailable(), pager); // no data + } - String currentOrder = orderBy; + Class type = it.next().getClass(); - if (ordered) { - order = GUI.local("_order_" + widgetId(), orderBy); - currentOrder = order.get(); - slice = slice.orderedBy(currentOrder); + Items itemsModel; + if (rows instanceof Items) { + itemsModel = (Items) rows; + } else { + itemsModel = Models.beanItems(type, U.array(rows)); } - boolean paging = pageSize > 0; - Var pageNumber = null; + final List props = itemsModel.properties(columns); + boolean ordered = !U.isEmpty(orderBy); + Var order = null; + String currentOrder = orderBy; - if (paging) { - pageNumber = GUI.local("_page_" + widgetId(), 1, 1, pages); - slice = getPage(slice, pageNumber.get()); + if (ordered) { + order = GUI.local("_o" + seq("order"), orderBy); + currentOrder = order.get(); + itemsModel = itemsModel.orderedBy(currentOrder); } Tag header = tableHeader(props, order); - Tag body = tableBody(props, slice); - Pager pager = paging ? GUI.pager(pageNumber.name()).min(1).max(pages) : noPager(); + Tag body = tableBody(props, itemsModel); return fullTable(header, body, pager); } + protected Tag noDataAvailable() { + return GUI.h4("No data available!"); + } + protected Pager noPager() { return null; } @@ -105,15 +133,6 @@ protected Tag fullTable(Tag header, Tag body, Pager pager) { return GUI.row(GUI.table_(GUI.thead(header), body), pager); } - protected Items getPage(Items items, Integer pageN) { - Items pageOrAll; - int pageFrom = Math.max((pageN - 1) * pageSize, 0); - int pageTo = Math.min((pageN) * pageSize, items.size()); - - pageOrAll = items.range(pageFrom, pageTo); - return pageOrAll; - } - protected Tag tableBody(final List props, Items pageOrAll) { Tag body = GUI.tbody(); @@ -181,7 +200,16 @@ protected Tag itemRow(List properties, Item item) { } protected String onClickScript(Item item) { - String uri = toUri != null ? Lmbd.eval(toUri, item.value()) : GUI.uriFor(item.value()); + String uri; + if (toUri != null) { + uri = Lmbd.eval(toUri, item.value()); + } else { + uri = GUI.uriFor(item.value()); + if (U.notEmpty(uri)) { + uri = Msc.uri(uri, "view"); + } + } + return U.notEmpty(uri) ? U.frmt("Rapidoid.goAt('%s');", uri) : null; } @@ -194,6 +222,7 @@ public Iterable items() { } public Grid items(Iterable items) { + U.must(!(items instanceof Items)); this.items = items; return this; } diff --git a/rapidoid-gui/src/main/java/org/rapidoid/gui/Pager.java b/rapidoid-gui/src/main/java/org/rapidoid/gui/Pager.java index 2bb50b3c30..ef1e196763 100644 --- a/rapidoid-gui/src/main/java/org/rapidoid/gui/Pager.java +++ b/rapidoid-gui/src/main/java/org/rapidoid/gui/Pager.java @@ -62,7 +62,7 @@ protected String pageUri(int pageN) { } protected boolean shouldDisplay() { - return U.neq(min, max); + return U.neq(min, max) || max == null || min == null; } protected Tag pagination() { diff --git a/rapidoid-gui/src/main/java/org/rapidoid/gui/base/AbstractWidget.java b/rapidoid-gui/src/main/java/org/rapidoid/gui/base/AbstractWidget.java index 71a48a7830..dfa0a28e19 100644 --- a/rapidoid-gui/src/main/java/org/rapidoid/gui/base/AbstractWidget.java +++ b/rapidoid-gui/src/main/java/org/rapidoid/gui/base/AbstractWidget.java @@ -12,10 +12,9 @@ import org.rapidoid.html.tag.ATag; import org.rapidoid.html.tag.TdTag; import org.rapidoid.html.tag.ThTag; +import org.rapidoid.u.U; import org.rapidoid.util.Constants; -import java.util.UUID; - /* * #%L * rapidoid-gui @@ -62,7 +61,18 @@ public final Object render(Object extra) { } public String widgetId() { - return getClass().getSimpleName() + "-" + UUID.randomUUID(); + String type = getClass().getSimpleName().toLowerCase(); + return type + seq(type); + } + + public String seq(String seqName) { + String seq = "__seq_" + seqName; + int seqNum = (Integer) U.or(req().attrs().get(seq), 0); + + seqNum++; + req().attrs().put(seq, seqNum); + + return "" + seqNum; } public boolean visible() { diff --git a/rapidoid-gui/src/test/java/org/rapidoid/widget/TableWidgetTest.java b/rapidoid-gui/src/test/java/org/rapidoid/widget/GridWidgetTest.java similarity index 83% rename from rapidoid-gui/src/test/java/org/rapidoid/widget/TableWidgetTest.java rename to rapidoid-gui/src/test/java/org/rapidoid/widget/GridWidgetTest.java index 40030a9b05..08fd53423d 100644 --- a/rapidoid-gui/src/test/java/org/rapidoid/widget/TableWidgetTest.java +++ b/rapidoid-gui/src/test/java/org/rapidoid/widget/GridWidgetTest.java @@ -25,13 +25,11 @@ import org.rapidoid.annotation.Since; import org.rapidoid.gui.GUI; import org.rapidoid.gui.Grid; -import org.rapidoid.model.Items; -import org.rapidoid.model.Models; import org.rapidoid.u.U; @Authors("Nikolche Mihajlovski") -@Since("2.0.0") -public class TableWidgetTest extends WidgetTestCommons { +@Since("5.1.0") +public class GridWidgetTest extends WidgetTestCommons { @Test public void testTableWidget() { @@ -41,9 +39,7 @@ public void testTableWidget() { Person rambo = new Person("Rambo", 50); rambo.id = 2; - Items items = Models.beanItemsInfer(john, rambo); - - Grid table = GUI.grid(items).pageSize(10); + Grid table = GUI.grid(U.list(john, rambo)).pageSize(10); verifyGUI("persons-grid", table); verifyGUI("map-grid", GUI.grid(U.map("name", "Foo", "year", "2016"))); diff --git a/rapidoid-gui/src/test/resources/test-results/TableWidgetTest/testTableWidget/map-grid b/rapidoid-gui/src/test/resources/test-results/GridWidgetTest/testTableWidget/map-grid similarity index 100% rename from rapidoid-gui/src/test/resources/test-results/TableWidgetTest/testTableWidget/map-grid rename to rapidoid-gui/src/test/resources/test-results/GridWidgetTest/testTableWidget/map-grid diff --git a/rapidoid-gui/src/test/resources/test-results/TableWidgetTest/testTableWidget/persons-grid b/rapidoid-gui/src/test/resources/test-results/GridWidgetTest/testTableWidget/persons-grid similarity index 100% rename from rapidoid-gui/src/test/resources/test-results/TableWidgetTest/testTableWidget/persons-grid rename to rapidoid-gui/src/test/resources/test-results/GridWidgetTest/testTableWidget/persons-grid diff --git a/rapidoid-web/src/main/java/org/rapidoid/goodies/ClasspathHandler.java b/rapidoid-web/src/main/java/org/rapidoid/goodies/ClasspathHandler.java index 6e700f7276..21cd107eb0 100644 --- a/rapidoid-web/src/main/java/org/rapidoid/goodies/ClasspathHandler.java +++ b/rapidoid-web/src/main/java/org/rapidoid/goodies/ClasspathHandler.java @@ -47,11 +47,11 @@ public Object call() throws Exception { info.add(h3("Classpath folders:")); - info.add(grid(ClasspathUtil.getClasspathFolders()).columns("trim").headers("Classpath entries (folders)")); + info.add(grid(ClasspathUtil.getClasspathFolders()).columns("trim").headers("Classpath entries (folders)").pageSize(0)); info.add(h3("Classpath JARs:")); - info.add(grid(ClasspathUtil.getClasspathJars()).columns("trim").headers("Classpath entries (JARs)")); + info.add(grid(ClasspathUtil.getClasspathJars()).columns("trim").headers("Classpath entries (JARs)").pageSize(0)); return info; } diff --git a/rapidoid-web/src/main/java/org/rapidoid/goodies/X.java b/rapidoid-web/src/main/java/org/rapidoid/goodies/X.java index 33d8a423ed..59e8566af5 100644 --- a/rapidoid-web/src/main/java/org/rapidoid/goodies/X.java +++ b/rapidoid-web/src/main/java/org/rapidoid/goodies/X.java @@ -13,12 +13,10 @@ import org.rapidoid.http.ReqRespHandler; import org.rapidoid.http.Resp; import org.rapidoid.jpa.JPA; -import org.rapidoid.lambda.Mapper; import org.rapidoid.setup.On; import org.rapidoid.setup.Setup; import org.rapidoid.sql.JDBC; import org.rapidoid.u.U; -import org.rapidoid.util.Msc; import javax.persistence.metamodel.EntityType; import java.util.List; @@ -136,12 +134,7 @@ public Object execute(Req req, Resp resp) throws Exception { IRange range = Range.of((page - 1) * pageSize, pageSize); List records = JPA.of(entityType).page(range.start(), range.length()); - Grid grid = GUI.grid(records).toUri(new Mapper() { - @Override - public String map(Object target) throws Exception { - return Msc.uri(GUI.uriFor(baseUri, target), "view"); - } - }); + Grid grid = GUI.grid(records); Btn add = GUI.btn("Add " + name(entityType)).primary().go(baseUri + "/add");