Paging leicht gemacht

By | May 25

Wer kennt das nicht? Tausende von Daten müssen seitenweise navigierbar zur Verfügung gestellt werden. z.b. Suchergebnisse, etc. Da dies ein häufiges Problem ist und in fast jedem Projekt mal vorkommt hab ich ein schönes Objekt dafür geschrieben welches die Implementierung solch einer Funktionalität erleichtern soll. Und vorallem: den Code leserlicher und übersichtlicher! Hier gleich mal ein Screenshot was am Ende rauskommen soll:

Pageable

Man sieht hier sehr schön die Anforderung an ein komfortables “Paging”. Hervorheben der aktuellen Seite, Navigation zur vorherigen und nächsten Seite sowie zur ersten und letzten. Ausserdem noch die Limitierung der Seiten auf eine bestimmte Anzahl, da es nicht recht sinnvoll wäre 1000 Seiten aufeinmal anzuzeigen. (im Screenshot deuten dies die Punkte an). All dies lösen wir nun mit der pageable-klasse welche Teil der gabLibrary ist. Da ich heut richtig in Schreibstimmung bin versuch ich ein umfangreiches Tutorial zu schreiben und einiges näher zu erläutern.

Wenn ich mir ein Tutorial zu Gemüte führ will ich gleich mal das Resultat selber testen ;) somit gibts hier die DEMO-Paging-Bar zum Testen. Aber nun zum Eigentlichen:

Gleich vorweg: das Objekt kann für alle möglichen Datenstrukturen werden. Ich demonstriere es in diesem Beispiel mit einem Array um die wesentliche Funktionalität herzuheben. Funktioniert aber genauso gut mit einem Recordset-objekt oder dictionary, etc.

1. Vorbereitung: Als erstes müssen wir die Klasse (pageable.asp) inkludieren und eine Basis für die Verwendung des Objekts schaffen (da dieses uns ja “nur” die Funktionalität kapselt). Bei einer paging-bar müssen wir uns irgendwie merken auf welcher Seite wir uns gerade befinden. Ich löse das mal über einen querystring-parameter namens page. Dafür hab ich die Funktion getCurrentPage() , geschrieben, welche lediglich überprüft ob dieser Wert vorhanden ist und gibt diesen weiter. Ist dieser nicht vorhanden (aller erster Aufruf der Seite) wird 1 zurückgegeben.

  1. <!--#include file="pageable.asp"-->
  2. 'liefert die aktuelle Seitennummer
  3. function getCurrentPage()
  4.   getCurrentPage = 1
  5.   if request("page") <> "" then
  6.     getCurrentPage = request("page")
  7.   end if
  8. end function

Ich hab mir gedacht die Navigation mittels “Buttons” zu realisieren und beim Klick per Javascript die Seite mit der gewünschten Seiternummer neu zu laden. Deshalb hier eine Funktion welche einen Button ausgibt. Recht allgemein gehalten, zeichnet einfach einen Button. Parameter wie disabled oder eine Css-klasse können übergeben werden. hab diese Funktion mal drawButton() genannt:

  1. 'zeichnet einen button
  2. sub drawButton(value, pageNr, cssClass, disabled)
  3.   with response
  4.     if disabled then dis = " disabled"
  5.     onClick = "document.location.href='paging.asp?page=" & pageNr
  6.     .write("<button type=button class=""btn " & cssClass & """ onclick=""" & onClick & "'""" & dis & ">")
  7.     .write(value)
  8.     .write("</button>")
  9.   end with
  10. end sub

Man erkennt hier schön die Wiederverwendung von CSS-Klassen. Jeder Button verwendet aufjedenfall die btn-klasse und zusätzlich eine individuelle welche von außen kommt. Dadruch können wir später gemeinsame Eigenschaften eines jeden Buttons konfigurieren.

Zu letzt benötigen wir noch eine Funktion, welche uns die gesamte Paging-Bar zeichnet. Also den Vor- und Zurück-Button, die einzelnen Seiten, etc. Dafür verwenden wir die zuvor erstellte Funktion drawButton(), da ja alle Funktionen mittels Buttons realisiert werden. Anbei die Funktion drawPagingBar():

  1. 'zeichnet unsere seiten-bar
  2. sub drawPagingBar()
  3.   drawButton "<<", paging.firstPage, "prevnext", paging.isOnFirstPage()
  4.   drawButton "<", paging.currentPage - 1, "prevnext", not paging.hasPreviousPage()
  5.   if paging.hasPreviousBlock() then response.write("....")
  6.   for i = 0 to uBound(paging.pages)
  7.     drawButton paging.page(i), paging.page(i), "common", paging.page(i) = paging.currentPage
  8.   next
  9.   if paging.hasNextBlock() then response.write("....")
  10.   drawButton ">", paging.currentPage + 1, "prevnext", not paging.hasNextPage()
  11.   drawButton ">>", paging.lastPage, "prevnext", paging.isOnLastPage()
  12. end sub

Mit Hilfe dieser Funktion wird die komplette Paging-Bar gezeichnet. Eigentlich keine Hexerei. Die verwendete Instanz paging vom Typ pageable bietet alle Funktionalitäten. So wird z.b. in der ersten Zeile der Button fürs “springen zur ersten Seiten” gezeichnet. Seitennummer muss logischerweise die erste sein, dafür steht das property paging.firstPage zur Verfügung. Befinden wir uns auf der ersten Seite wollen wir den Button natürlich deaktivieren, somit kommt der letzte Parameter der drawButton()-Funktion ins Spiel. Da übergeben wir einfach den Rückgabewert der Methode paging.isOnFirstPage(). Eh kloa weil ja dieser Button disabled wird wenn wir uns bereits auf der ersten Seite befinden (on first page…).
Genauso verhält es sich mit all den weiteren Buttons (nächste Seite, vorige Seite, etc.). Nicht viel schwieriger ist die Darstellung der einzelnen Seiten. Ein einfacher Loop. Unabhängig davon ob man die pageable-Klasse bereits kennt oder nicht, der Code ist aufjedenfall verständlich.

2. Die Daten: Da wir die Vorbereitungen erledigt haben benötigen wir nun einige Daten welche “gepaged” werden sollen. Hierfür erstell ich einfach ein Array mit Zufallszeichen als Inhalt. Achtung: dies führt dazu, dass bei jedem Seitewechsel neue Daten in jedem Feld vorhanden sind. Sollte aber für die Demo kein Problem darstellen da wir uns nicht um den Inhalt kümmern sondern um die Aufteilung der Daten in Seiten und deren Navigation. Das Array mit Zufallszeichen:

  1. 'wir verwenden ein array befüllt mit daten
  2. dim data(100)
  3. for i = 0 to uBound(data)
  4.   randomize()
  5.   data(i) = chr(255 * (rnd()))
  6. next

3. pageable konfigurieren: nun kommt der wichigste Part, nämlich die Pageable-Klasse. Dieser müssen wir unsere “Wünsche” bekanntgeben. Also an welcher Seitennummer steht meine Seite gerade (.currentPage) ? Wieviele Datensätze beinhaltet meine Datenspeicher (.recordCount)? Wieviele Datensätze will ich pro Seite anzeigen (.recordsPerPage)? und natürlich wieviele Seiten will ich in meiner Paging-Bar den gleichzeitig anzeigen (.numberOfPages)? Wenn all dies angegeben wurde führt man die perform() Methode aus und kann dannach alle Werte der Pageable verwenden. Hier nun die Instanz der pageable

  1. 'pageable wird instanziert und mit den nötigen werten versorgt
  2. set paging = new Pageable
  3. with paging
  4.   .currentPage = getCurrentPage()
  5.   .recordCount = uBound(data) + 1
  6.   .recordsPerPage = 10
  7.   .numberOfPages = 7
  8.   .perform()
  9. end with

Man sieht hier schön die Verwendung der anfangs definierten Funktion getCurrentPage(), welche uns die aktuelle Seite zurückliefert (speicherung mittels Querystring). Anzahl der Datensätze ist auch klar, +1 weil das Array auch ein 0er Feld hat.

4. Der Output: Zu guter letzt müssen wir es noch am Bildschirm ausgeben.

  1. <style>
  2.   .btn { width:30; border:0; background-color:white; }
  3.   .common { color:blue; }
  4.   .prevnext { font-weight:bold; }
  5. </style>
  6.  
  7.  
  8. <form>
  9.   <div align="center"><% drawPagingBar() %></div>
  10.   <br />
  11.   <div>
  12.     <% for i = paging.dataStartPosition to paging.dataEndPosition %>
  13.       <div align="center">Data<%= i - 1%>: <%= data(i - 1) %></div>
  14.     <% next %>
  15.   </div>
  16. </form>

Wir zeichen die pagingbar an die gewünschte Stelle mit drawPagingBar(). Dann müssen auch noch die richtigen Daten ausgegeben werden. Dafür stellt uns die pageable folgende Eigenschaften zur Verfügung: dataStartPosition und dataEndPosition. Mit Hilfe dieser kann man nun die angeforderten Datensätze aus dem Array darstellen. Innerhalb der Schleife wird 1 subtrahiert, da im 0er Feld auch Nutzdaten vorhanden sind.

Ich verwende dieses Objekt schon einige Zeit (vorallem für Recordsets) und es hat sich bewährt. Viel Spaß damit. Hier gehts zum download der Klasse und des Demobeispiels:

Paging.zip download

Hab ein paar Anfragen bekommen wie man das an ein Recordset hängt. Dafür gibts einen Thread im ASP-Forum: