Extra - views pages with ajax

Най-интересната част от презентацията - въпросът как се прави менюто да е с ajax остана недообяснена поради липса на време. Надявам се тук да намерите отговорите на въпросите си по това.

Така, както подготвихме в предните постове различните компоненти - нашето меню с категории което сътветно си има целева страница в която да се зареждат резултатите, на практика е лесната част, която става без да се налага да пишем код. За ajax обаче трябва малко да се потрудим - най-малкото няма как да се мине без js.

Какво е необходимо?

  • Скрипт (js), където да прихванем потребителското действие (click), да пратим на системата каква информация иска потребителя.
  • Menu callback - който да получи "горното искане", да намери информацията по този критерий, да подготви резултата в подходящ за представяне формат и да го прати обратно на страницата.

За целта ще направим малък модул, който да добави тази нова функционалност. Обикновено собствени модули, които не са от http://drupal.org ги поставям в sites/all/modules/custom_modules. В тази директория създавам папка views_ext (разширения за/на views). В нея създавам файла views_ext.info с код:

name = Views extention
description = Add ajax to views page
dependencies[] = views
package = Custom
core = 7.x
files[] = views_ext.module

Създавам файл views_ext.module с код:

<?php
/**
 * Implements hook_init().
 */
function views_ext_init() {
  drupal_add_js(drupal_get_path('module', 'views_ext') . '/views_ext.js', 'file');
}
 
/**
 * Implements hook_menu().
 */
function views_ext_menu() {
  $items['views_term/ajax'] = array(
    'title' => 'Views ajax',
    'page callback' => '_views_ajax',
    'page arguments' => array(2),
    'access arguments' => array('access content'),
    'type' => MENU_CALLBACK,
  );
 
  return $items;
}
 
/**
 * Implements of hook_preprocess_HOOK().
 */
function views_ext_preprocess_page(&$variables){
  if($variables['page'] && arg(0) == 'views_term' && arg(1) == 'ajax') {
    $variables['theme_hook_suggestions'][] = 'views__ajax';
  }
}
 
//views ajax
function _views_ajax($arg) {
  $output = '';
 
  $view_name = 'clone_of_taxonomy_term';
  $display = 'page';
 
  $output = views_embed_view($view_name, $display, $arg);
 
  return drupal_json_output(array('views_title' => $arg, 'views_content' => $output));
  exit;
}

и файл за js - views_ext.js с код в него:

(function ($) {
  Drupal.behaviors.views_ext =  {
    attach: function(context, settings) {
 
      $('a.category').click(function() {
        $('a.category').removeClass('active');
        $(this).addClass('active');
 
        var args = $(this).attr('href').split('/');
 
        var arg2 = args.slice(-1)[0];
        var documents_load_url = Drupal.settings.basePath + 'views_term/ajax/' + arg2;
 
        var updateResults = function(data) {
 
          var obj = JSON.parse(data);
 
          $('h1#page-title.title').text(obj.views_title);
          $('#block-system-main .content').html(obj.views_content);
          $('#throbber').fadeOut();
        }
 
        var showThrobber = function() {
          $('#throbber').fadeIn();
        }
 
        $.ajax({
          type: 'POST',
          url: documents_load_url,
          beforeSend: showThrobber,
          success: updateResults,
          dataType: 'html'
        });
        return false;
 
      });
 
    }
  };
})(jQuery);

Готовия модул с този код е прикачен в края на този пост. Инсталирайте по обичайния начин. Всичко е почти готово. Остана да направим темплейта, който ще връща информацията след клик.

Създаваме файл views--ajax.tpl.php в папката на текущата тема. В случай, че имаме много темплейти е добре да въведем някакава организация. Обикновено създавам в папката на темата папка templates и в нея допълнително съдавам папка views за темплейтите на views, папка nodes за темплейтите на различните типове нодове и т.н. Йерархията няма значение (тя е за лично удобство, а също и да се ориентира друг човек, когато преглежда нашият код и файлове) - достатъчно е самият темплейт файл да се намира в папката на текущата тема или в нейна под-папка.

Кода на views--ajax.tpl.php e

<?php 
print render($page['content']); 
?>

Има го също като прикачен файл.

Просто, нали? :)

Някои обяснения по кода:

С този код казваме най-общо, че за тази страница ни трябва само съдържанието на централния регион най-общо - без заглавната част, без регионите, без футър.
Този темплейт се прилага когато се посети url като example.com/views_term/ajax/Term name

function views_ext_preprocess_page(&$variables){
  if($variables['page'] && arg(0) == 'views_term' && arg(1) == 'ajax') {
    $variables['theme_hook_suggestions'][] = 'views__ajax';
  }
}

Какво точно да съдържа този $page['content'] се казва в

//views ajax
function _views_ajax($arg) {
  $output = '';
 
  $view_name = 'clone_of_taxonomy_term';
  $display = 'page';
 
  $output = views_embed_view($view_name, $display, $arg);
 
  return drupal_json_output(array('views_title' => $arg, 'views_content' => $output));
  exit;
}

Спомняте си, че клонирахме вю Taxonomy term и променихме контекстния му филтър да приема име на термин вместо id на термин. С
$output = views_embed_view($view_name, $display, $arg);
получаваме изхода на това вю с контекстен филтър Име на термин, получен от нашия menu_callback

/**
 * Implements hook_menu().
 */
function views_ext_menu() {
  $items['views_term/ajax'] = array(
    'title' => 'Views ajax',
    'page callback' => '_views_ajax',
    'page arguments' => array(2),
    'access arguments' => array('access content'),
    'type' => MENU_CALLBACK,
  );
 
  return $items;
}

'page callback' => '_views_ajax',
указва каква функция ще се изпълни за този url

'page arguments' => array(2),
указва, че горната функция ще получи един аргумент (в този масив има един елемент), и този аргумент е третия аргумент от пътя (2 е третия, нали броим от 0 нула - при example.com/views_term/ajax/Term name аргумент 2 е Term name). Натам е лесно - views_embed_view($view_name, $display, $arg) ще свърши останалото.

За троберчето css

#throbber {
display: none;
}
.throbber {
background: url('../images/ajax-loader-big.gif') center center no-repeat;
border-radius: 5px;
width: 60px;
height: 60px;
}

ajax-loader-big.gif е при прикачените файлове, или си направете собствен http://ajaxload.info/

За експерти:
Вероятно някой ще каже, че не е това съвсем правилният начин за имплементиране на подобна функционалност. Въпрос на гледна точка. Друг начин за реализиране на някои от горните неща има на http://api.drupal.org/api/drupal/modules%21system%21system.api.php/funct...

още в редакция

Comments

в добавка към това можем динамично да сменяме html тага title на страницата без презареждането й (освен за потребителя, това няма голямо значение за друг) - като добавим в .js към функцията updateResults $(document).attr('title',obj.views_title + ' | ' + obj.site_name);
и
към .module към функцията function _views_ajax($arg) $site_name = variable_get('site_name', ''); и съответно return да стане return drupal_json_output(array('views_title' => $arg, 'views_content' => $output, 'site_name' => htmlentities($site_name)));

така var updateResults изглежда вече така:

        var updateResults = function(data) {
 
          var obj = JSON.parse(data);
 
          $(document).attr('title',obj.views_title + ' | ' + obj.site_name);
          $('h1#page-title.title').text(obj.views_title);
          $('#block-system-main .content').html(obj.views_content);
          $('#throbber').fadeOut();
        }

и
function _views_ajax($arg) така:

function _views_ajax($arg) {
  $output = '';
  $site_name = variable_get('site_name', '');
 
  $view_name = 'clone_of_taxonomy_term';
  $display = 'page';
 
  $output = views_embed_view($view_name, $display, $arg);
 
  return drupal_json_output(array('views_title' => $arg, 'views_content' => $output, 'site_name' => htmlentities($site_name)));
  exit;
}