使用 AngularJS 将 Drupal 功能移植到前端

注意事项:
如支付后未自动显示完整内容,可点击“已支付?点此查询订单”进行查看。
如遇内容不符或缺失,请联系内容作者或平台客服(工作日 9:00-18:00)。

  在Lullabot,让网站追求更高性能和维护性是我们一直的目标。最近,我们开始解耦Drupal中的一些业务逻辑 ,并使用 JavaScript 将其移动到客户端浏览器。

  例如,我们希望在网站上显示给定城市的天气,涉及以下事项:

  1. 调用一些的公共API(可带参数)。 本例中我们选择   OpenWeatherMap
  2. 从返回结果中提取相应的气象数据。
  3. 将数据显示在浏览器中。

  其结果将如下图所示:

 使用AngulaJS在Drupal中实现的天气显示区块

  在 Drupal 中,我们可以创建一个区块,使用 drupal_http_request()   函数来获取数据,然后将它的结果传递给一个主题函数进行渲染。这很简单,也易于维护,只是这项功能即没有数据库参与,也没有Session管理,为什么要让 Drupal 来关心这些事情?

  如果我们的网站依赖于缓存以提高性能,每当区块内容更新时,我们都不得不进行缓存清除。相对的,让我们将这个功能交给 JS 和 HTML 来处理,让客户端浏览器来对数据的获取、加工和缓存负责。

遇见 AngularJS

  AngularJS 是一个MVC JavaScript框架,它将应用程序中的控制器、模板及数据模型优雅的进行分类。通过AngularJS,我们已经从 Drupal 项目中移除了大量的后台逻辑。虽然有很多东西需要学,但目前为止,我们已经取得了相当不错的成绩。

  完整的示例地址 https://gist.github.com/juampynr/6003761 。接下来我们逐步来看如何实现。

引导 AngularJS 应用

  我们可以通过添加 directive(指令), 来引导 AngularJS 应用。将以下属性添加到 html.tpl.php 文件中:

<html data-ng-app="myapp" xmlns="http://www.w3.org/1999/xhtml" xml:lang="<?php print $language->language; ?>" version="XHTML+RDFa 1.0" dir="<?php print $language->dir; ?>"<?php print $rdf_namespaces; ?>>

  属性 data-ng-app="myapp" 告诉 AngularJS 来引导我们命名为“myapp ”的应用程序 。目前只需要做这么多就够, 我们会在稍后实现我们的 AngularJS应用。

在Drupal中实现业务骨架

通过一些简单的代码在Drupal模块中实现一个区块。thememymodule_block_view()函数还包括一个JavaScript文件(AngularJS控制器)和一个模板:

/**
 * Implements hook_block_view().
 */
function mymodule_block_view($delta = '') {
  $block = array();
 
  switch ($delta) {
    case 'weather':
      $path = drupal_get_path('module', 'mymodule');
      $block['subject'] = t('Weather status');
      $block['content'] = array(
        '#theme' => 'weather_status',
        '#attached' => array(
          'js' => array(
            'https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js',
            $path . '/mymodule.js',
          ),
        ),
      );
      break;
  }
  return $block;
}

  Drupal所要做的工作就这些,为 AngularJS应用打好运行基础。

在浏览器中处理

  当页面被传递到客户端,AngularJS 控制器将从 OpenWeatherMap 抓取数据,然后对数据进行处理:

/**
 * Renders the weather status for a city.
 */
var app = angular.module('myapp', [])
.controller('MyModuleWeather', function($scope, $http, $log) {
  // Set default values for our form fields.
  $scope.city = 'Madrid';
  $scope.units = 'metric';
 
  // Define a function to process form submission.
  $scope.change = function() {
    // Fetch the data from the public API through JSONP.
    // See http://openweathermap.org/API#weather.
    var url = 'http://api.openweathermap.org/data/2.5/weather';
    $http.jsonp(url, { params : {
        q : $scope.city,
        units : $scope.units,
        callback: 'JSON_CALLBACK'
      }}).
      success(function(data, status, headers, config) {
        $scope.main = data.main;
        $scope.wind = data.wind;
        $scope.description = data.weather[0].description;
      }).
      error(function(data, status, headers, config) {
        // Log an error in the browser's console.
        $log.error('Could not retrieve data from ' + url);
      });
  };
 
  // Trigger form submission for first load.
  $scope.change();
});

渲染结果

  我们的模板直接引用控制器(AngularJS的绑定方式)和输出在$scope对象中设置的变量。

<div ng-controller="MyModuleWeather">
  <label for="city">City</label>
  <input type="text" ng-model="city" /></br>
  <label for="units">Units</label>
  <input type="radio" ng-model="units" value="metric"/> Metric
  <input type="radio" ng-model="units" value="imperial"/> Imperial</br>
  <button ng-click="change()">Change</button>
  <h3>{{data.name}}</h3>
  <p>{{description}}</p>
  <p>Temperature: {{main.temp}}</p>
  <p>Wind speed: {{wind.speed}}</p>
</div>

  搞定!现在我们就有一个在浏览器中处理的功能完整的区块。如果我们将这种模式应用于页面上其他经常变化的区块,便能够简化Drupal的工作,让页面的缓存效率更高,实现更好的性能。当需要为不同显示不同的内容时,通过这种模式,还可以对内容进行   lazy_load(延迟加载) ,使页面其余部分更容易缓存。

使用外部API注意事项

  当在客户端浏览器中执行跨域请求时,请记住,有时浏览器的安全机制会成为你的阻碍。有两种流行的方法来克服这个问题。一种是通过 CORS 跨域资源共享 :可以在Drupal.org上找到 CORS模块 。另一种方法是使用   JSONP,也是我们在本例中使用的就是这个方法,AngularJS 和 JQuery 都支持。

为什么不使用jQuery?

  从技术上讲,使用 JQuery 也可以实现相同的功能 。只不过那需要更多的代码:你将需要在页面被构建时隐藏模板,为提交按钮定义监听器、净化数据、以及自行处理模板绑定等操作。即使以这个简单例子而 言,AngularJS 方法不仅简单,且更富有条理。你也可以在 AngularJS中使用jQuery

  你也可以查看本例子的jQuery版本 ,并对两者进行比较。

看完了?还不过瘾?点此向作者提问