воскресенье, 18 июля 2010 г.

Автоподгрузка списка по скроллингу

Итак, я решил первый технический пост не посвящать азам Sencha Touch и не стал излагать тот минимум, который необходим для создания мобильного web приложения (об этом я расскажу в следующих записях), я хочу рассказать вам о компонентах Ext.List, Ext.util.Scroller, о AJAX-запросах и как с помощью этого всего сделать так, чтобы по достижению дна списка подгружалась следующая порция контента.

(на момент написания использовалась Sencha Touch 0.91 beta)

Когда передо мной возникла эта задача, я первым делом полез в документацию Sencha Touch (http://www.sencha.com/deploy/touch/docs/) и обнаружил, что никаких событий, связанных со скроллингом, у компонента Ext.List нет. Сразу после этого, я открыл исходники компонента List в поисках недокументированных возможностей (в ExtJS к этим махинация приходилось обращаться часто, поскольку стандартным набором API сделать можно далеко не всё, а вот если сделать Ext'овскому компоненту какой-нибудь адаптер, то функционал библиотеки можно существенно расширить). Покопавшись в коде, я обнаружил такой компонент, как scoller, оказавшийся на деле Ext.util.Scroller, у которого в документации уже присутствовали, необходимые мне, события:
  • scrollend - скроллирование окончено (то, что нам надо)
  • scrollstart - скроллирование началось
  • touchend - окончание прикосновения к экрану
  • touchstart - начало прикосновения к экрану
Далее оставалось дело за малым: получить компонент scroller нашего списка, подписаться к нему на событие scrollend, проверять, доскроллировали ли мы до конца, и если да, то вызывать, нужный нам, Ajax-метод.
Трудности возникли с самого первого пункта: в документации Sencha Touch отсутствовали методы или переменные для получения компонента Scroller. Пришлось открыть исходники Ext.List и, прибегнув к хаку, вручную извлечь, необходимый нам, скроллер:
this.scroller.on('scrollend', this.onScrollEnd, this);
(this - это сам наш List, а this.onScrollEnd - функция обработчика события)

Итак, мы подписались на события окончания скроллинга, и теперь необходимо извлечь данные о позиции скролла и данные о общей длинне скроллируемого листа, чтобы сравнить их. В событие scrollend передаётся экземпляр скроллера, из которого мы все эти данные и выпытаем:
onScrollEnd: function(scroller) {
 if(scroller.offset.y == scroller.bounds.y && !this.isLoading) {
  this.isLoading = true;
  this.loadContent(++this.page);
 }
}

scroller.offset.y - текущее положение скроллинга
scroller.bounds.y - общий размер скроллируемого списка
this.loadContent() - функция подгрузки нового контента
this.page - номер страницы
this.isLoading - флаг загрузки (необходим, чтобы нельзя было сто раз вызвать подгрузку новго контента, пока старый не загрузился)
Осталось вызвать Ajax-метод и добавить, подгруженный контент, к существующему:
loadContent: function(page) {
 Ext.Ajax.request({
  /* адрес php-функции, подгружающей контент */
  url  : 'action.php', 
  method : 'POST',
  /* список параметров, передаваемых в запрос */
  params : {
   /* номер страницы (если не определён, то берём страницу №1) */
   page  : page || 1
  },
  scope : this,
  /* запрос прошёл успешно */
  success : function(response, opts) { 
   /* декодируем строку в JSON-объект */
   response = Ext.util.JSON.decode(response.responseText); 
   if(response.content != null) {
    /* если не обнулить это свойство, то все новые записи будут  дублироваться */
    this.getStore().snapshot = null; 
    /* пробегаем по всем записям */
    Ext.each(response.content, function(entry) {
     /* добавляем запись в Store */
     this.getStore().add(entry); 
    }, this);
   }
   /* разрешаем подгрузку следующей порции контента */
   this.isLoading= false; 
  },
  /* запрос не прошёл */
  failure : function(response, opts) { 
   this.isLoading= false;
  }
 });
}
Вот, в принципе, и всё. Теперь, когда вы прокрутите список в самый низ, будет подгружаться новая порция контента. Не забудьте правильно сконфигурировать сам Ext.List и его Ext.data.Store (об этом я расскажу в следующих записях).

2 комментария: