Как стать автором
Обновить

Lift: простой пример AJAX-загрузки данных

Время на прочтение4 мин
Количество просмотров2.9K
Lift — современный фреймворк на языке Scala, предназначенный для создания веб-приложений и предлагающий нестандартные подходы для решения некоторых задач. В данной заметке будет рассмотрен простейший пример (до)загрузки данных с сервера при помощи AJAX-запросов. Пост написан в надежде, что он может быть кому-то полезен и сократить время поиска решения на часик-другой, да и просто в качестве демонстрации возможностей отличного современного, но пока не очень популярного фреймворка. Но я предполагаю, что у читателя уже есть некоторые базовые представления о Lift и о некоторых концепциях, в нем применяемых.


В качестве примера возьмем приложение, которое должно отображать «комментарии» — при загрузке страницы первые пять, а загрузку остальных — по запросу, «порциями» по пять штук, без перезагрузки страницы, конечно же.

Итак, начнем. Пускай, у нас есть модель Post, представляющая единичный комментарий:
class Post extends LongKeyedMapper[Post] with IdPK {
    def getSingleton = Post

    object title extends MappedText(this)
    object text extends MappedText(this)
    object date extends MappedDate(this)
}

и соответствующий companion object:
object Post extends Post with LongKeyedMetaMapper[Post] {
    def getPosts(startAt: Int, count: Int) = {
        Post.findAll(OrderBy(Post.date, Descending), StartAt(startAt), MaxRows(count))
    }

    def getPostsCount = Post.count
}

в котором уже объявлены две функции — получение общего количества объектов и получение некоего настраиваемого диапазона комментариев. В нашем случае можно было обойтись и без этих методов и строить запросы «по-месту» — прямо в коде сниппетов, но, по моему скромному мнению, это затруднит тестирование и перегрузит код.

Теперь надо заняться первоначальным отображением комментариев. Для этого создаем следующий сниппет:
class PostRender {
    def renderPosts(in: NodeSeq): NodeSeq = {
        Post.getPosts(0, 5).flatMap(item =>
            bind("post", in,
                "title" -> Text(item.title),
                "text" -> Text(item.text)
        ))
    }
}

И соответствующий embeded-шаблон, который располагается в /templates/__post.html (да, нижние подчеркивания даже в Sсala/Lifft иногда бывают нужны):
<lift:PostRender.renderPosts>
    <p>
        Title: <post:title />
        <br />
        <post:text />
    </p>
</lift:PostRender.renderPosts>

и который подключается на требуемой странице с помощью:
<div id="posts">
    <lift:embed what="/templates/__post" />
</div>

Хочу заметить, что в данном случае я использую «устаревшую» разметку шаблонов, основанную на спец. xml-тегах, которая в данном случае мне кажется более удобной и наглядной.

В результате, после добавления модели в схему БД, заполнения записей и т.д, получаем примерно такой результат:

всего 5 объектов, как и требовалось. Теперь надо организовать загрузку остальных комментариев.

AJAX-загрузка


Перед тем, как мы продолжим, стоит несколько слов сказать о JsCmd — фактически, подсистеме в Lift, которая предназначена для генерации клиентского JavaScript на стороне сервера, в коде сниппетов и с последующей вставкой их в ответ. Существуют различные способы инжектирования JS-кода — от метода JsRaw, позволяющего вставлять «сырой» JS, до прокси-методов для JS-фреймворков. В первую очередь, JsCmd предназначен для реализации небольших по объему частей JS-кода.

Модифицируем вставку комментариев, добавив туда вызов сниппета для отображения кнопки загрузки дополнительных объектов:
<div id="posts">
        <lift:embed what="/templates/__post" />
</div>

<lift:PostRender.renderLoadMoreControl />

и класс сниппета:
class PostRender {
    val loadSize = 5
    val lastLoadIndex = Post.getPostsCount - loadSize
    var loadStartIndex = 0

    def renderPosts(in: NodeSeq): NodeSeq = {
        Post.getPosts(loadStartIndex, loadSize).flatMap(item =>
            bind("post", in,
                "title" -> Text(item.title),
                "text" -> Text(item.text)
        ))
    }

    def renderLoadMoreControl: NodeSeq = {
        def loadPosts: JsCmd = {
            loadStartIndex += loadSize
            JqJsCmds.AppendHtml("posts", <lift:embed what="/templates/__post" />) & {
                    if (lastLoadIndex < loadStartIndex)
                        JsCmds.Replace("loadButton", NodeSeq.Empty)
                }
        }

        SHtml.ajaxButton("Load More", loadPosts _, "id" -> "loadButton")
    }
}

В класс были введены значения для представления загружаемых «страниц» с комментариями — метод renderPosts использует их первоначальные значения для отображения первых 5 записей, а так же новый метод renderLoadMoreControl, который добавляет кнопку для загрузки следующей «порции» комментариев и связывает ее с внутренней функцией loadPosts, которая и представляет собой реализацию AJAX-ответа. Рассмотрим ее подробнее.

Первым делом мы инкрементируем счетчик индекса записей для загрузки — это очевидно. Далее мы вызываем метод AppendHtml из пакета net.liftweb.http.js.jquery.JqJsCmds, который предоставляет обертки для функций библиотеки jQuery (которая встроена по-умолчанию в Lift). AppendHtml принимает два параметра — id объекта DOM-дерева и объект типа NodeSeq — собственно, тот код, который и будет добавлен. Думаю, теперь становится понятно, почему шаблон комментария был вынесен в отдельный файл — теперь его можно передать в метод AppendHtml в качестве AJAX-ответа, и более того — этот шаблон будет обработан методом renderPosts! Функции, которые возвращают тип JsCmd можно конкатенировать с помощью метода & — в нашем случае, при достижении последней «страницы», прячется кнопка загрузки комментариев. Собственно говоря — это все, весь код :)

На стороне клиента, кнопка превратится в примерно такой код:
<button onclick="liftAjax.lift_ajaxHandler("F167386167581RGRLAG=true", null, null, null); return false;" id="loadButton">Load More</button>
, а код ответа будет содержать команды jQuery, примерно так:
jQuery('#'+"posts").append("\u000a    <p>\u000a        Title: Post #11 ... ");


В качестве заключения


Конечно же, приведенный пример несколько искусственный, но он может продемонстрировать некоторые особенности Lift: встроенные средства для создания AJAX-запросов, обертки и прокси-методы для создания клиентского JavaScript, сквозная обработка шаблонов. Так же, явно не было написано никакого клиентского JS-кода вовсе — все это берет на себя фреймфорк, за пределы которого мы не выходили в этом примере. Впрочем, это не единственный подход, и если надо, то конечно же нет никаких препятствий к «ручному управлению». Код приложения можно получить тут (используется Maven).
Теги:
Хабы:
Всего голосов 33: ↑32 и ↓1+31
Комментарии13

Публикации