PaginatorSnippet with filtering capability

46 views
Skip to first unread message

Riccardo Sirigu

unread,
Nov 16, 2016, 4:20:25 AM11/16/16
to Lift
Hi folks,

I'm trying to add the filtering capability to a result set using PaginatorSnippet.

Is there a way to update the page in order to fetch the filtered data from the database and present the new result set?

class AdminPoi extends PaginatorSnippet[Poi]{

 
override def count: Long = Poi.count

 
override def page: Seq[Poi] = Poi.paginate(itemsPerPage).setPage(curPage).fetch()

 
var searchText = ""

  def processSearch(text: String): JsCmd = {
   
searchText = text
   
if(text.nonEmpty){
     
Alert("Searching for " + text)
   
}
   
else Noop
  }

 
def render = {
   
SHtml.makeFormsAjax andThen
   
"#search-name" #> SHtml.ajaxText(searchText, processSearch) &
   
".table-row *" #> page.zipWithIndex.map{ case (poi, index) => {
       
".num *" #> (index + 1) &
       
".name *" #> poi.name.get.it.get &
       
".category *" #> poi.category.get &
       
".is-visible *" #> poi.visible.get &
       
".is-visible [class+]" #> {if(poi.visible.get) "success" else ""} &
       
".btn-edit [href]" #> (Site.editPoi.url + s"?id=${poi.id.get}") &
       
".btn-delete [href]" #> ""
      }
   
}
 
}

}

Thank you

Antonio Salazar Cardozo

unread,
Nov 16, 2016, 9:12:12 AM11/16/16
to Lift
The Seq[T]---in this case, Seq[Poi]---represents the current page. So, you can filter
before paginating in your `Poi.paginate(...)` call, I would think. That said, I'm not sure
which persistence layer you're using, so I'm not completely sure what the correct way
to do that would be. With a made-up API, this might be:

  override def page: Seq[Poi] = Poi.filter("name" -> nameFilter).paginate(itemPerPage).setPage(curPage).fetch()

Thanks,
Antonio

Riccardo Sirigu

unread,
Nov 16, 2016, 9:22:59 AM11/16/16
to Lift
Thank you Antonio,

I'm using Rogue for mongodb

I need to perform the filtering after the page of type Seq[Poi] has been created, without reloading the html, using ajax.
But I think I can't with PaginatorSnippet

Antonio Salazar Cardozo

unread,
Nov 17, 2016, 9:06:28 AM11/17/16
to Lift
Ah yes I see. I would actually put that in a separate snippet. Something like this:

<section id="pois" data-lift="AdminPoiSearch.render">
  <form><input id="search-name"></form>
  <table data-lift="AdminPoi.render">
    <tr class="table-row"> ... </tr>
  </table>
</section>

And then:

object nameFilter extends RequestVar[Option[String]](None)
class AdminPoin extends PaginatorSnippet[Poi] {
  // .....

  override def page = Poi.whereOpt(nameFilter.is)(_.name eqs _).paginate(itemsPerPage).setPage(curPage).fetch()

  // .....
}

Then in the other snippet, you can use `idMemoize`:

class AdminPoiSearch {
  // The renderer itself does nothing here, just allows rerendering.
  val pageRenderer = SHtml.idMemoize { renderer => PassThrough }

  def processSearch(term: String): JsCmd = {
    nameFilter.set(Some(term))

    pageRenderer.setHtml
  }

  def render = {
    SHtml.makeFormsAjax andThen
    "#search-name" #> SHtml.ajaxText(searchText, processSearch) &
    "table" #> pageRenderer
  }
}

The second binding attaches the renderer to the page, while the pageRenderer.setHtml
call will rerender the page. The pagination snippet uses the name filter, if it's set, to filter
the query.

Hope that helps!
Thanks,
Antonio

Riccardo Sirigu

unread,
Nov 17, 2016, 9:32:04 AM11/17/16
to Lift
Awesome! I really like it
Thank you Antonio

Antonio Salazar Cardozo

unread,
Nov 17, 2016, 9:39:43 AM11/17/16
to Lift
Glad to hear it! :)
Thanks,
Antonio

Riccardo Sirigu

unread,
Nov 18, 2016, 5:20:44 AM11/18/16
to Lift
Unfortunatly it doesn't work.. 
idMemoize memoizes the template but when I call setHtml to trigger the search, it rerenders the first template.. and the  other snippet doesn't rerender itself.
I'm trying to figure out a solution ;)

Antonio Salazar Cardozo

unread,
Nov 18, 2016, 8:40:59 AM11/18/16
to Lift
Hmm, this seems strange. You're saying that it doesn't rerun the pagination snippet?

I'll try to investigate this today, as it seems very surprising.
Thanks,
Antonio

Riccardo Sirigu

unread,
Nov 21, 2016, 10:19:29 AM11/21/16
to Lift
That's correct, it doesn't reload the snippet for whatever reason

Antonio Salazar Cardozo

unread,
Nov 22, 2016, 11:37:45 AM11/22/16
to Lift
Very odd. Haven't had time to look at it, but is there any chance you could post a
sample project so I can have a quick look when I get a chance?
Thanks,
Antonio

Riccardo Sirigu

unread,
Nov 22, 2016, 1:05:40 PM11/22/16
to Lift
For sure, here it is https://github.com/ricsirigu/lift-ajax-examples

On the /search path

Thanks

Antonio Salazar Cardozo

unread,
Nov 27, 2016, 9:32:16 AM11/27/16
to Lift
Looked at this a little more closely, and I see what's up.

Two important things when using idMemoize: you have to attach it to a place in
the DOM (otherwise it never captures any content to rerender) and the template
that it captures consists of the *children* of wherever you attach it. So, in that
example, we have:

        <div data-lift="Results">
            <ul>
                <li>Result 1</li>
            </ul>
        </div>

And, the idMemoize just looks like:

  var searchResults = SHtml.idMemoize(renderer => PassThru)

The bind is:

  def render() ={
    "#search-field" #> SHtml.ajaxText(searchTerm, updateResults)
  }

First off, we want to attach the idMemoize to a particular point. To do
that, we add an id:

        <div id="results" data-lift="Results">
            <ul>
                <li>Result 1</li>
            </ul>
        </div>

And then attach it:

  def render() ={
    "#search-field" #> SHtml.ajaxText(searchTerm, updateResults) &
    "#results" #> searchResults
  }

Unfortunately, this means the idMemoize captures the *children* of the
results div, so it captures:

            <ul>
                <li>Result 1</li>
            </ul>

So while the first time it runs, we execute the Results snippet, when
we re-render the template, there's no snippet invocation in the captured
content! We can fix this by pushing the snippet into the `ul` instead:

        <div id="results">
            <ul data-lift="Results">
                <li>Result 1</li>
            </ul>
        </div>

That does what we expect it to. Give it a shot and let me know if that helps!
Thanks,
Antonio

Riccardo Sirigu

unread,
Nov 28, 2016, 4:55:30 AM11/28/16
to Lift
Works perfectly now, 

Thank you Antonio

Antonio Salazar Cardozo

unread,
Nov 28, 2016, 9:21:16 AM11/28/16
to Lift
Awesome! Glad to hear it and glad to help :)
Thanks,
Antonio
Reply all
Reply to author
Forward
0 new messages