drupal_Drupal应用程序中的AngularJS

drupal

Angular.js is the hot new thing right now for designing applications in the client. Well, it’s not so new anymore but is sure as hell still hot, especially now that it’s being used and backed by Google. It takes the idea of a JavaScript framework to a whole new level and provides a great basis for developing rich and dynamic apps that can run in the browser or as hybrid mobile apps.

Angular.js是目前在客户端中设计应用程序的热门新事物。 好吧,它不再是新事物了,但是可以肯定的是,它仍然很炙手可热,尤其是在Google正在使用和支持它的情况下。 它将JavaScript框架的思想提升到了一个全新的高度,并为开发可在浏览器中或作为混合移动应用程序运行的丰富而动态的应用程序奠定了良好的基础。

logo_drupal

In this article I am going to show you a neat little way of using some of its magic within a Drupal 7 site. A simple piece of functionality but one that is enough to demonstrate how powerful Angular.js is and the potential use cases even within heavy server-side PHP frameworks such as Drupal. So what are we doing?

在本文中,我将向您展示在Drupal 7站点中使用其一些魔术的巧妙方法。 一项简单的功能,但足以证明Angular.js的功能强大,以及即使在重型服务器端PHP框架(如Drupal)中的潜在用例。 那我们在做什么呢?

We are going to create a block that lists some node titles. Big whoop. However, these node titles are going to be loaded asynchronously using Angular.js and there will be a textfield above them to filter/search for nodes (also done asyncronously). As a bonus, we will also use a small open source Angular.js module that will allow us to view some of the node info in a dialog when we click on the titles.

我们将创建一个列出一些节点标题的块。 哎呀 但是,这些节点标题将使用Angular.js异步加载,并且它们上方将有一个文本字段来过滤/搜索节点(也可以异步完成)。 另外,我们还将使用一个小的开源Angular.js模块,当我们单击标题时,该模块将允许我们在对话框中查看某些节点信息。

So let’s get started. As usual, all the code we write in the tutorial can be found in this repository.

因此,让我们开始吧。 像往常一样,我们在教程中编写的所有代码都可以在此存储库中找到。

配料 (Ingredients)

In order to mock this up, we will need the following:

为了模拟这一点,我们将需要以下内容:

  • A custom Drupal module

    定制的Drupal模块
  • A Drupal hook_menu() implementation to create an endpoint for querying nodes

    Drupal hook_menu()实现创建用于查询节点的端点

  • A Drupal theme function that uses a template file to render our markup

    一个Drupal主题函数,它使用模板文件来呈现我们的标记
  • A custom Drupal block to call the theme function and place the markup where we want

    一个自定义的Drupal块来调用主题函数并将标记放置在我们想要的位置
  • A small Angular.js app

    一个小的Angular.js应用
  • For the bonus part, the ngDialog Angular module

    对于奖励部分,请使用ngDialog Angular模块

模块 (The module)

Let us get started with creating a custom module called Ang. As usual, inside the modules/custom folder create an ang.info file:

让我们开始创建一个名为Ang的自定义模块。 和往常一样,在modules/custom文件夹中创建一个ang.info文件:

name = Ang
description = Angular.js example on a Drupal 7 site.
core = 7.x

…and an ang.module file that will contain most of our Drupal related code. Inside this file (don’t forget the opening <?php tag), we can start with the hook_menu() implementation:

…以及一个包含大多数与Drupal相关的代码的ang.module文件。 在此文件中(不要忘记打开<?php标签),我们可以从hook_menu()实现开始:

/**
 * Implements hook_menu().
 */
function ang_menu() {
  $items = array();

  $items['api/node'] = array(
    'access arguments' => array('access content'),
    'page callback'     => 'ang_node_api',
    'page arguments' => array(2),
    'delivery callback' => 'drupal_json_output'
  );

  return $items;
}
/**
 * API callback to return nodes in JSON format
 *
 * @param $param
 * @return array
 */
function ang_node_api($param) {

  // If passed param is node id
  if ($param && is_numeric($param)) {
    $node = node_load($param);
    return array(
      'nid' => $param,
      'uid' => $node->uid,
      'title' => check_plain($node->title),
      'body' => $node->body[LANGUAGE_NONE][0]['value'],
    );
  }
  // If passed param is text value
  elseif ($param && !is_numeric($param)) {
    $nodes = db_query("SELECT nid, uid, title FROM {node} n JOIN {field_data_body} b ON n.nid = b.entity_id WHERE n.title LIKE :pattern ORDER BY n.created DESC LIMIT 5", array(':pattern' => '%' . db_like($param) . '%'))->fetchAll();
    return $nodes;
  }
  // If there is no passed param
  else {
    $nodes = db_query("SELECT nid, uid, title FROM {node} n JOIN {field_data_body} b ON n.nid = b.entity_id ORDER BY n.created DESC LIMIT 10")->fetchAll();
    return $nodes;
  }
}

In hook_menu() we declare a path (api/node) which can be accessed by anyone with permissions to view content and which will return JSON output created in the callback function ang_node_api(). The latter gets passed one argument, that is whatever is found in the URL after the path we declared: api/node/[some-extra-param]. We need this argument because of we want to achieve 3 things with this endpoint:

hook_menu()我们声明一个路径( api/node ),任何有权查看内容的人都可以访问该路径,并且该路径将返回在回调函数ang_node_api()创建的JSON输出。 后者传递了一个参数,即在我们声明的路径之后的URL中找到的任何东西: api/node/[some-extra-param] 。 我们需要此参数,因为我们希望通过此端点实现三件事:

  1. return a list of 10 most recent nodes

    返回最近的10个节点的列表
  2. return a node with a certain id (api/node/5 for example)

    返回具有特定ID的节点(例如api/node/5 )

  3. return all the nodes which have the passed parameter in their title (api/node/chocolate for example, where chocolate is part of one or more node titles)

    返回所有在其标题中具有传递参数的节点(例如, api/node/chocolate ,其中chocolate是一个或多个节点标题的一部分)

And this is what happens in the second function. The parameter is being checked against three cases:

这就是第二个功能。 正在针对以下三种情况检查该参数:

  • If it exists and it’s numeric, we load the respective node and return an array with some basic info form that node (remember, this will be in JSON format)

    如果它存在并且是数字,那么我们将加载相应的节点,并以该节点的一些基本信息形式返回一个数组(请记住,它将采用JSON格式)
  • If it exists but it is not numeric, we perform a database query and return all the nodes whose titles contain that value

    如果存在但不是数字,则执行数据库查询并返回其标题包含该值的所有节点
  • In any other case (which essentially means the lack of a parameter), we query the db and return the latest 10 nodes (just as an example)

    在任何其他情况下(本质上意味着缺少参数),我们查询数据库并返回最新的10个节点(仅作为示例)

Obviously this callback can be further improved and consolidated (error handling, etc), but for demonstration purposes, it will work just fine. Let’s now create a theme that uses a template file and a custom block that will render it:

显然,可以进一步改进和合并此回调(错误处理等),但是出于演示目的,它将正常工作。 现在,让我们创建一个使用模板文件的主题和一个将渲染该模板的自定义块:

/**
 * Implements hook_theme().
 */
function ang_theme($existing, $type, $theme, $path) {
  return array(
    'angular_listing' => array(
      'template' => 'angular-listing',
      'variables' => array()
    ),
  );
}

/**
 * Implements hook_block_info().
 */
function ang_block_info() {

  $blocks['angular_nodes'] = array(
    'info' => t('Node listing'),
  );

  return $blocks;
}

/**
 * Implements hook_block_view().
 */
function ang_block_view($delta = '') {

  $block = array();

  switch ($delta) {
    case 'angular_nodes':
      $block['subject'] = t('Latest nodes');
      $block['content'] = array(
        '#theme' => 'angular_listing',
        '#attached' => array(
          'js' => array(
            'https://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.min.js',
            'https://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular-resource.js',
            drupal_get_path('module', 'ang') . '/lib/ngDialog/ngDialog.min.js',
            drupal_get_path('module', 'ang') . '/ang.js',
          ),
          'css' => array(
            drupal_get_path('module', 'ang') . '/lib/ngDialog/ngDialog.min.css',
            drupal_get_path('module', 'ang') . '/lib/ngDialog/ngDialog-theme-default.min.css',
          ),
        ),
      );
      break;
  }

  return $block;
}

/**
 * Implements template_preprocess_angular_listing().
 */
function ang_preprocess_angular_listing(&$vars) {
  // Can stay empty for now.
}

There are four simple functions here:

这里有四个简单的功能:

  1. Using hook_theme() we create our angular_listing theme that uses the angular-listing.tpl.php template file we will create soon.

    使用hook_theme(),我们创建了angular_listing主题,该主题使用了即将创建的angular-listing.tpl.php模板文件。

  2. Inside the hook_block_info() we define our new block, the display of which is being controlled inside the next function.

    hook_block_info()内部,我们定义了新块,该块的显示在下一个函数中进行控制。

  3. Using hook_block_view() we define the output of our block: a renderable array using the angular_listing theme and which has the respective javascript and css files attached. From the Google CDN we load the Angular.js library files, inside ang.js we will write our JavaScript logic and in the /lib/ngDialog folder we have the library for creating dialogs. It’s up to you to download the latter and place it in the module following the described structure. You can find the files either in the repository or on the library website.

    使用hook_block_view(),我们定义块的输出:使用angular_listing主题的可渲染数组,并附加了相应的javascript和css文件。 从Google CDN中,我们加载Angular.js库文件,在ang.js我们将编写JavaScript逻辑,在/lib/ngDialog文件夹中,我们具有用于创建对话框的库。 您可以下载后者,并按照所述结构将其放置在模块中。 您可以在资源库图书馆网站上找到文件。

  4. The last function is a template preprocessor for our template in order to make sure the variables are getting passed to it (even if we are actually not using any).

    最后一个函数是模板的模板预处理器,以确保将变量传递给它(即使实际上我们没有使用任何变量)。

As you can see, this is standard boilerplate Drupal 7 code. Before enabling the module or trying out this code, let’s quickly create the template file so Drupal doesn’t error out. Inside a file called angular-listing.tpl.php, add the following:

如您所见,这是标准的样板Drupal 7代码。 在启用模块或试用此代码之前,让我们快速创建模板文件,以确保Drupal不会出错。 在名为angular-listing.tpl.php的文件中,添加以下内容:

<div ng-app="nodeListing">
	
	   <div ng-controller="ListController">
	
	     <h3>Filter</h3>
	     <input ng-model="search" ng-change="doSearch()">
	
	      <ul>
	        <li ng-repeat="node in nodes"><button ng-click="open(node.nid)">Open</button> {{ node.title }}</li>
	      </ul>
	
	     <script type="text/ng-template" id="loadedNodeTemplate">
	     <h3>{{ loadedNode.title }}</h3>
	     {{ loadedNode.body }}
	     </script>
	
	    </div>
	
	</div>

Here we have some simple HTML pimped up with Angular.js directives and expressions. Additionally, we have a <script> tag used by the ngDialog module as the template for the dialog. Before trying to explain this, let’s create also our ang.js file and add our javascript to it (since the two are so connected):

在这里,我们用Angular.js指令和表达式整理了一些简单HTML。 另外,我们还有一个<script>标记, ngDialog模块将其用作对话框的模板。 在尝试解释这一点之前,让我们还创建我们的ang.js文件并向其中添加我们的javascript(因为两者是如此连接):

angular.module('nodeListing', ['ngResource', 'ngDialog'])

  // Factory for the ngResource service.
  .factory('Node', function($resource) {
    return $resource(Drupal.settings.basePath + 'api/node/:param', {}, {
      'search' : {method : 'GET', isArray : true}
    });
  })

  .controller('ListController', ['$scope', 'Node', 'ngDialog', function($scope, Node, ngDialog) {
    // Initial list of nodes.
    $scope.nodes = Node.query();

    // Callback for performing the search using a param from the textfield.
    $scope.doSearch = function() {
      $scope.nodes = Node.search({param: $scope.search});
    };

    // Callback to load the node info in the modal
    $scope.open = function(nid) {
      $scope.loadedNode = Node.get({param: nid});
      ngDialog.open({
        template: 'loadedNodeTemplate',
        scope: $scope
      });
    };

}]);

Alright. Now we have everything (make sure you also add the ngDialog files as requested in the #attached key of the renderable array we wrote above). You can enable the module and place the block somewhere prominent where you can see it. If all went well, you should get 10 node titles (if you have so many) and a search box above. Searching will make AJAX calls to the server to our endpoint and return other node titles. And clicking on them will open up a dialog with the node title and body on it. Sweet.

好的。 现在我们拥有了所有内容(确保您还按照上面编写的可渲染数组的#attached键中的要求添加了ngDialog文件)。 您可以启用该模块并将该块放置在可以看到它的显着位置。 如果一切顺利,您应该获得10个节点标题(如果有太多的话)和上面的搜索框。 搜索将向我们的端点发出对服务器的AJAX调用,并返回其他节点标题。 然后单击它们将打开一个对话框,其中包含节点标题和正文。 甜。

But let me explain what happens on the Angular.js side of things as well. First of all, we define an Angular.js app called nodeListing with the ngResource (the Angular.js service in charge communicating with the server) and ngDialog as its dependencies. This module is also declared in our template file as the main app, using the ng-app directive.

但是,让我解释一下在Angular.js方面会发生什么。 首先,我们定义了一个名为nodeListing的Angular.js应用,其中ngResource (负责与服务器通信的Angular.js服务)和ngDialog作为其依赖项。 此模块还使用ng-app指令在我们的模板文件中声明为主应用ng-app

Inside this module, we create a factory for a new service called Node which returns a $resource. The latter is in fact a connection to our data on the server (the Drupal backend accessed through our endpoint). In addition to the default methods on it, we define another one called .search() that will make a GET request and return an array of results (we need a new one because the default .get() does not accept an array of results).

在此模块内部,我们为名为Node的新服务创建了一个工厂,该工厂返回$resource 。 后者实际上是到服务器上我们数据的连接(通过我们的端点访问的Drupal后端)。 除了默认方法外,我们还定义了另一个名为.search() ,该方法将发出GET请求并返回结果数组(我们需要一个新方法,因为默认的.get()不接受结果数组) )。

Below this factory, we define a controller called ListController (also declared in the template file using the ng-controller directive). This is our only controller and it’s scope will apply over all the template. There are a few things we do inside the controller:

在该工厂下,我们定义一个名为ListController的控制器(也使用ng-controller指令在模板文件中声明)。 这是我们唯一的控制器,其作用域将应用于所有模板。 我们在控制器内部做一些事情:

  1. We load nodes from our resource using the query() method. We pass no parameters so we will get the latest 10 nodes on the site (if you remember our endpoint callback, the request will be made to /api/node). We attach the results to the scope in a variable called nodes. In our template, we loop through this array using the ng-repeat directive and list the node titles. Additionally, we create a button for each with an ng-click directive that triggers the callback open(node.nid) (more on this at point 3).

    我们使用query()方法从资源中加载节点。 我们没有传递任何参数,因此我们将获得站点上的最新10个节点(如果您还记得端点回调,则将向/api/node发出请求)。 我们将结果附加到名为nodes的变量的作用域中。 在我们的模板中,我们使用ng-repeat指令遍历此数组并列出节点标题。 此外,我们为每个按钮创建一个带有ng-click指令的按钮,该指令会触发回调open(node.nid) (有关第3点的更多信息)。

  2. Looking still at the template, above this listing, we have an input element whose value will be bound to the scope using the ng-model directive. But using the ng-change directive we call a function on the scope (doSearch()) every time a user types or removes something in that textfield. This function is defined inside the controller and is responsible for performing a search on our endpoint with the param the user has been typing in the textfield (the search variable). As the search is being performed, the results populate the template automatically.

    仍然看一下清单上方的模板,我们有一个input元素,其值将使用ng-model指令绑定到范围。 但是,使用ng-change指令时,每当用户键入或删除该文本字段中的内容时,我们都会在范围( doSearch() )上调用一个函数。 此函数在控制器内部定义,负责使用用户在文本字段中键入的参数( search变量)在我们的端点上执行搜索。 执行搜索时,结果将自动填充模板。

  3. Lastly, for the the bonus part, we define the open() method which takes a node id as argument and requests the node from our endpoint. Pressing the button, this callback function opens the dialog that uses a template defined inside of the <script> tag with the id of loadedNodeTemplate and passes to it the current scope of the controller. And if we turn to the template file, we see that the dialog template simply outputs the title and the body of the node.

    最后,对于奖励部分,我们定义open()方法,该方法将节点ID作为参数并从端点请求该节点。 按下按钮,此回调函数将打开对话框,该对话框使用在<script>标记内定义的模板,其ID为loadedNodeTemplate ,并将当前控制器范围传递给该对话框。 而且,如果我们转到模板文件,则会看到对话框模板只是输出节点的标题和主体。

结论 (Conclusion)

You can see for yourself the amount of code we wrote to accomplish this neat functionality. Most of it is actually boilerplate. A very fast node query block that delivers results asynchronously with all of its benefits. And if you know Angular.js, you can imagine the possibility of enhancing the Drupal experience further.

您可以亲自了解我们为完成此功能而编写的代码量。 实际上大多数都是样板。 一个非常快速的节点查询块,它以其所有优势异步交付结果。 而且,如果您知道Angular.js,您可以想象进一步增强Drupal体验的可能性。

Now, are you interested to learn more about the love between Angular.js and Drupal? Would you have done anything differently? Let us know in the comments below!

现在,您是否有兴趣了解有关Angular.js与Drupal之间的爱的更多信息? 您会做其他不同的事情吗? 在下面的评论中让我们知道!

翻译自: https://www.sitepoint.com/angularjs-drupal-apps/

drupal

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值