Ruby On Rails: Ajax Paginate und Suche

Auf meiner Internet-Lernkartei namens Web-Lernbox kann man ja per Ajax nach Lernkarten suchen und in der Liste blättern (dies nennt man Pagination). Das Ergebnis sieht (zumindest in meinem Augen) recht ansprechend aus.

Hier die Details, wie man so einen Effekt recht einfach in Rails programmiert.
Web-Lernbox Suchmaske

Phase 1: Das Suchfenster
Wir brauchen ein Textfeld. Wenn nun der Benutzer eine Eingabe tätigt, soll eine Aktion auf dem Server ausgeführt werden. Klingt schwierig, ist es aber nicht. So sieht der entsprechende Teil in der View-Datei aus:

(...)
< %= text_field_tag :filter %>
< %= observe_field(:filter, :frequency => 0.2,
:update => "cardlist",
:url => {:action => :filter_elements, :id => @box}) %>

Der erste Befehl ist klar. text_field_tag definiert ein Inputfeld. Anschließend definieren wir ein Observer_field. Der erste Parameter definiert das Feld, welches beobachtet werden soll. :frequency=>0.2 sagt, dass alls 0,2 Sekunden auf eine Änderung geprüft werden soll. Gibt der Benutzer etwas sein, wird die Methode „filter_elements“ aufgerufen. Diese Methode bekommt den Paramenter id mit, damit wir wissen, welche Lernbox eigentlich gefiltert werden soll. Schließlich haben wir noch das Attribut :update=>“cardlist“. Das sagt, welcher Bereich eigentlich nach dem Aufruf neu beschrieben wird (wir wollen ja nicht die komplette Seite neu zum Client schicken, sondern nur das Ergebnis).

In dem View gibt es also einen Bereich „cardlist“ den habe ich in eine zweite Datei (_cardlist.rhtml) gepackt und rufe ihn im View über
< %= render( :partial =>'edit/cardlist' ) %>
auf.
Wichtig ist natürlich, dass in der Datei cardlist ein Bereich mit der id „cardlist“ definiert ist:
Datei _cardlist.rtml:
<div id="cardlist">
<table cellpadding="5" class="edit_list_table" width="100%">
(... Anzeige der Elemente... )
</table>
</div>

Das war bislang einfach, oder? Also weiter.

Phase 2: Die Ajax-Suche
In meinem Controller sieht die Suchmethode nun so aus:
def filter_elements
filter_name = request.raw_post
box_id = current_user.Boxes.find(params[:id]).id;
@cards_pages, @cards = paginate(:cards, :conditions => ['(frage like ? or antwort like ?) and box_id = ?',
filter_name, filter_name, box_id])
render :partial => 'cardlist'
end

Kleine Anmerkung: Ok ok, in Wirklichkeit sieht die Funktion bei der echten Lernbox ein wenig anders aus, aber es geht ja ums Prinzip. Es passiert hier also folgendes. Für Für jeden Filteraufruf wird der Datenbestand neu gefiltert und mit dem Befehl paginate in mehrere Seiten geteilt.

Phase 3: Blättern mit Ajax
Jetzt gibt es aber noch ein Problem. Die Paginate Funktion existiert schon in Rails und kann mit dem Befehl „pagination_links“ im View auf die entsprechenden Navigationselemente erzeugen. Dann würde aber die komplette Seite neu aufgerufen und nciht per Ajax nur der notwendige Bereich neu geschrieben. Also musste ich mir einen kleinen Helfer schreiben. In die _cardlist.rhtml kommt:
< %= draw_paginator(@cards_pages) %>

Jetzt muss man nur noch der Helper entsprechend erweitern:

def my_pagination_links(paginator,action)
if paginator then
pagination_links_each(paginator,{:link_to_current=>false}) do |n|
link_to_remote(n.to_s, { :url=>{:action=>action, :page =>n.to_s}, :update=>'cardlist' })
end
else
return ''
end
end

def draw_left_paginator(paginator,action)
if paginator.current.previous then
link_to_remote('Zurück',
:url=>{:action=>action, :page =>paginator.current.previous}, :update=>'kart_list')
else
return ''
end
end

def draw_right_paginator(paginator,action)
if paginator.current.next then
link_to_remote('Nächste Seite', :url=>{:action=>action,
:page =>paginator.current.next}, :update=>'kart_list')
else
return ''
end
end

def draw_paginator(paginator,action='list')
if paginator then
return draw_left_paginator(paginator,action) + my_pagination_links(paginator,action) + draw_right_paginator(paginator,action)
else
return ''
end
end

(Auch hier: der echte Code sieht anders aus)

Im Prinzip habe ich die ursprünglichen Pagination Sourcen genommen und verändert. Klar, man hätte auch einfach die entsprechende Methode überschreiben können. Aber so fand ich es sinniger.

Das war es auch schon. Der Aufwand für das Ajax-Frontend ist faktisch genauso hoch, wie bei einer normalen Seite, sieht aber viel hübscher aus.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

What is 12 + 15 ?
Please leave these two fields as-is:
IMPORTANT! To be able to proceed, you need to solve the following simple math (so we know that you are a human) :-)