Menu icon
RedCrackle
Menu icon
Services
01Design Transformation
About UsCase StudiesBlogContact Us

<

Blog post

Configuring Drupal To Enable Integration With AngularJS

Neerav Mehta

Founder & CEO

AngularJS is a JavaScript-based web application framework. It is used to build single-page web applications for client-sides that use JavaScript, HTML and CSS.

In this post, we will show you how to configure Drupal with AngularJS. This will help you make the best use of features from both platforms in your projects.

Why AngularJS?

For the last couple of years, developers relied on tools such as Adobe Flex, Adobe Flash and JS to build Rich Internet Applications (RIA). Even though these tools were convenient to use, applications built with them had several drawbacks. Some of them are listed below:

  • Device incompatibility
  • Browser incompatibility
  • Junky code that’s difficult to debug
  • Large web pages that take a lot of time to load
  • Difficulty in building applications using AJAX because you have to deal with HTTPs and browser compatibility issues.

With AngularJS, things are looking a lot brighter.

Google Developers deserve a lot of credit for building a light-weight and scale-able framework on JS and HTML5 that handles all complexities and takes all the pain away from the developer. Developers can now focus more on the functionality they expect while building highly-interactive web-applications.

In Drupal, AngularJS has an able technology ally that’s probably the most revered CMS on earth.

Let’s Get Started!

We assume you have a basic knowledge of Drupal installation, configuration, Drush, themes and module development. A similar knowledge of JS, Twitter Bootstrap, MVC & AngularJS will be beneficial.

First, we need to configure Drupal with AngularJS. There are a few prerequisites we need to install. They are as follows:

Drupal

  • Drupal setup (download here)
  • AngularJS module (download here)
  • Bootstrap module (download here)
  • Library module (download here)
  • Drush module (download here)
  • AngularJS framework (download here)
  • All the dependencies related to above modules.

AngularJs

  • AngularJS UI Bootstrap (download here)
  • jQuery Fullcalender (download here)
  • AngularJS directive for Fullcalender (download here)

In case you have any issue installing Drupal7, follow this tutorial.

We are using Drush command to install modules in Drupal:

$ drush dl angularjs
$ drush en angularjs

$ drush dl bootstrap
$ drush en bootstrap

$ drush dl libraries
$ drush en libraries

Add other modules you wish to install as well.

After you are done installing Drupal and the desired modules, it’s time to install the AngularJS library in Drupal. For that, you need to create a library folder with the following path: sites/all/libraries as shown in the screen-shot below:

Configuring Drupal To Enable Integration With AngularJS

And now you need to copy the angular folder inside it as shown in the screen-shot below:

Configuring Drupal To Enable Integration With AngularJS

Now, it’s time to visit AngularJS’s configuration section to configure it. A screenshot of the page is shown below:

Configuring Drupal To Enable Integration With AngularJS

Now, you can verify whether AngularJS has been installed correctly on your system. Navigate to the link angular/nodes#/. It will show you a basic AngularJS application to prove that everything is working fine. Check the screen-shot below:

Configuring Drupal To Enable Integration With AngularJS

There is a very good FireFox add-on called AngScope to inspect the AngularJS application object. It will display the AngularJS scope and objects as in the screen-shot below:

Configuring Drupal To Enable Integration With AngularJS

We have now set up Drupal and AngularJS.

Executing A Working Example

We are going to create a Calendar application by using AngularJS and see how we can write a Drupal module to integrate with it.

We have planned a module which is called ng_node_calender. It has an AngularJS application and module files. You can see the screen-shot below for more details:

Configuring Drupal To Enable Integration With AngularJS

Module structure: ng_node_calender/

-ng_node_calender.module

-ng_node_calender.info

-ng_node_calender.install //if required

-themes/* //angular templates file

-js/* //angular app and directives

The JS folder will be home to the AngularJS application. The theme folder will have the Drupal templates files.

Now, we have the directory set up for the module. Let’s start writing our first callback method for the AngularJS application to see how Drupal works with AngularJS.

Module ng_node_calender.info looks like:

name = AngularJS NodeCalender
description = It will provide node calender
core = 7.x
dependencies[] = angularjs


version = "7.x-1.3"
core = "7.x"
project = "ng_node_calender"
datestamp = "1404549418"

Module ng_node_calender.module looks like:

/**
* Implements hook_menu().
*/
function ng_node_calender_menu() {
  $items = array();
  $items['ng_node/calender_app'] = array(
    'title' => t('Angular Node Calender'),
    'page callback' => 'ng_node_calender',
    'access arguments' => array('access content'),
  );

  return $items;
}

In the above code, we have registered menu callback to load the AngularJS application inside the Drupal environment.

/**
* Page callback for ng_node_calender().
*/
function ng_node_calender(){
  angularjs_init_application('ng_node_calender');
  //CSS files for calender
  drupal_add_css(drupal_get_path('module', 'ng_node_calender') . '/js/lib/fullcalendar-2.0.2/fullcalendar.css');
  drupal_add_css(drupal_get_path('module', 'ng_node_calender') . '/js/lib/fullcalendar-2.0.2/fullcalendar.print.css');
  //JS files for calender
  drupal_add_js(drupal_get_path('module', 'ng_node_calender') . '/js/lib/fullcalendar-2.0.2/lib/moment.min.js');

  drupal_add_js(drupal_get_path('module', 'ng_node_calender') . '/js/lib/fullcalendar-2.0.2/fullcalendar.min.js');
  drupal_add_js(drupal_get_path('module', 'ng_node_calender') . '/js/lib/fullcalendar-2.0.2/gcal.js');
  drupal_add_js(drupal_get_path('module', 'ng_node_calender') . '/js/directives/calendar.js');
  drupal_add_js(drupal_get_path('module', 'ng_node_calender') . '/js/directives/ui-bootstrap.min.js');
  drupal_add_js(drupal_get_path('module', 'ng_node_calender') . '/js/directives/ui-bootstrap-tpls.min.js');

  drupal_add_js(drupal_get_path('module', 'ng_node_calender') . '/js/app.js');
  return theme('ng_node_calender');
}

Here, we have registered all the required JS files, which we will be using in our application.

/**
* Implements hook_theme
*
* @return array
*/
function ng_node_calender_theme() {
  $themes = array();
  $themes['ng_node_calender'] = array(
    'template' => 'theme/ng_node_calender',
    'parameters' => array(),
  );
  return $themes;
}

We have registered template files which actually initialize the application by using ng markups. We have created ng_node_calender.tpl.php inside the theme directory and its code is as follows:

<div id="ng_node_calender" ng-app="ng_node_calender">
  <div ng-view></div>
</div>

If you look at the angularjs module, you will see a file called angularjs.api.inc. It provides hooks which are given by the angularjs module in order to register AngularJS application directives , controllers and so on.

Now, let us focus on app.js, which actually loads the whole application.

Here is the code:

var app = angular
  .module("ng_node_calender", [
    "node",
    "nodes",
    "ui.calendar",
    "ui.bootstrap",
    "ui.bootstrap.modal",
  ])
  .config(function ($routeProvider) {
    $routeProvider
      .when("/", {
        controller: CalenderCtrl,
        templateUrl:
          Drupal.settings.angularjsApp.basePath + "/ng_node/calender/display",
      })
      .otherwise({ redirectTo: "/" });
  });

Drupal.settings.angularjsApp.basePath will contain the base path and CalenderCtrl contains the controller object.

Let us have a look at the CalenderCtrl() function:

function CalenderCtrl($scope, $rootScope, $modal, Nodes, Node) {
  var date = new Date();
  var d = date.getDate();
  var m = date.getMonth();
  var y = date.getFullYear();

  $scope.alerts = [];
  $scope.changeTo = "Hungarian";
  /* event source that pulls from google.com */
  $scope.eventSource = {
    url: "http://www.google.com/calendar/feeds/usa__en%40holiday.calendar.google.com/public/basic",
    className: "gcal-event", // an option!
    currentTimezone: "America/Chicago", // an option!
  };
  /* event source that contains custom events on the scope */
  $scope.events = [
    { title: "All Day Event", start: new Date(y, m, 1) },
    {
      title: "Long Event",
      start: new Date(y, m, d - 5),
      end: new Date(y, m, d - 2),
    },
    {
      id: 999,
      title: "Repeating Event",
      start: new Date(y, m, d - 3, 16, 0),
      allDay: false,
    },
    {
      id: 999,
      title: "Repeating Event",
      start: new Date(y, m, d + 4, 16, 0),
      allDay: false,
    },
    {
      title: "Birthday Party",
      start: new Date(y, m, d + 1, 19, 0),
      end: new Date(y, m, d + 1, 22, 30),
      allDay: false,
    },
    {
      title: "Click for Google",
      start: new Date(y, m, 28),
      end: new Date(y, m, 29),
      url: "http://google.com/",
    },
  ];
  /* event source that calls a function on every view switch */
  $scope.eventsF = function (start, end, callback) {
    var s = new Date(start).getTime() / 1000;
    var e = new Date(end).getTime() / 1000;
    var m = new Date(start).getMonth();
    var events = [
      {
        title: "Feed Me " + m,
        start: s + 50000,
        end: s + 100000,
        allDay: false,
        className: ["customFeed"],
      },
    ];
    callback(events);
  };
  $scope.closeAlert = function (index) {
    $scope.alerts.splice(index, 1);
  };
  $scope.calEventsExt = {
    color: "#f00",
    textColor: "yellow",
    events: [
      {
        type: "party",
        title: "Lunch",
        start: new Date(y, m, d, 12, 0),
        end: new Date(y, m, d, 14, 0),
        allDay: false,
      },
      {
        type: "party",
        title: "Lunch 2",
        start: new Date(y, m, d, 12, 0),
        end: new Date(y, m, d, 14, 0),
        allDay: false,
      },
      {
        type: "party",
        title: "Click for Google",
        start: new Date(y, m, 28),
        end: new Date(y, m, 29),
        url: "http://google.com/",
      },
    ],
  };

  /* alert on eventClick */
  $scope.alertOnEventClick = function (event, allDay, jsEvent, view) {
    $scope.alerts.push({ msg: "Another alert!" });
  };

  /* alert on Drop */
  $scope.alertOnDrop = function (
    event,
    dayDelta,
    minuteDelta,
    allDay,
    revertFunc,
    jsEvent,
    ui,
    view
  ) {
    $scope.alertMessage = "Event Droped to make dayDelta " + dayDelta;
  };
  /* alert on Resize */
  $scope.alertOnResize = function (
    event,
    dayDelta,
    minuteDelta,
    revertFunc,
    jsEvent,
    ui,
    view
  ) {
    $scope.alertMessage = "Event Resized to make dayDelta " + minuteDelta;
  };
  /* add and removes an event source of choice */
  $scope.addRemoveEventSource = function (sources, source) {
    var canAdd = 0;
    angular.forEach(sources, function (value, key) {
      if (sources[key] === source) {
        sources.splice(key, 1);
        canAdd = 1;
      }
    });
    if (canAdd === 0) {
      sources.push(source);
    }
  };

  /* add custom event*/
  $scope.addEvent = function () {
    $scope.events.push({
      title: "Open Sesame",
      start: new Date(y, m, 28),
      end: new Date(y, m, 29),
      className: ["openSesame"],
    });
  };
  /* remove event */
  $scope.remove = function (index) {
    $scope.events.splice(index, 1);
  };
  /* Change View */
  $scope.changeView = function (view, calendar) {
    calendar.fullCalendar("changeView", view);
  };
  /* Change View */
  $scope.renderCalender = function (calendar) {
    if (calendar) {
      calendar.fullCalendar("render");
    }
  };
  /* config object */
  $scope.uiConfig = {
    calendar: {
      height: 450,
      editable: true,
      eventStartEditable: false,
      header: {
        left: "prev,next today",
        center: "title",
        right: "month,basicWeek,basicDay",
      },
      eventClick: $scope.alertOnEventClick,
      eventDrop: $scope.alertOnDrop,
      eventResize: $scope.alertOnResize,
    },
  };

  $scope.changeLang = function () {
    if ($scope.changeTo === "Hungarian") {
      $scope.uiConfig.calendar.dayNames = [
        "Vasárnap",
        "Hétfő",
        "Kedd",
        "Szerda",
        "Csütörtök",
        "Péntek",
        "Szombat",
      ];
      $scope.uiConfig.calendar.dayNamesShort = [
        "Vas",
        "Hét",
        "Kedd",
        "Sze",
        "Csüt",
        "Pén",
        "Szo",
      ];
      $scope.changeTo = "English";
    } else {
      $scope.uiConfig.calendar.dayNames = [
        "Sunday",
        "Monday",
        "Tuesday",
        "Wednesday",
        "Thursday",
        "Friday",
        "Saturday",
      ];
      $scope.uiConfig.calendar.dayNamesShort = [
        "Sun",
        "Mon",
        "Tue",
        "Wed",
        "Thu",
        "Fri",
        "Sat",
      ];
      $scope.changeTo = "Hungarian";
    }
  };
  /* event sources array*/
  $scope.eventSources = [$scope.events, $scope.eventSource];
}

This controller function is the implementation of angular-ui calendar directive, which can be found here.

So let’s see how it works. We have a menu callback ng_node/calender_app. Hitting it will trigger a callback method called ng_node_calender, which loads and initializes the AngularJS application with all the dependencies.

The question is how will AngularJS controller be called? How will it load Fullcalender AngularJS directives? We know that an AngularJS directive is required to include markup inside template file. How can we achieve this inside Drupal menu callback and with templates files?

Here is the answer to all these questions. We need to create another menu callback which will load the partial view of AngularJS inside Drupal templates. Let’s have a look at it:

function ng_node_calender_menu() {
  $items = array();

  /**
  * @todo: Create menu callback which load angular js calender
  */
  $items['ng_node/calender_app'] = array(
    'title' => t('Angular Node Calender'),
    'page callback' => 'ng_node_calender',
    'access arguments' => array('access content'),
  );
  $items['ng_node/calender/display'] = array(
    'title' => t('Display calender'),
    'page callback' => 'ng_node_calender_display',
    'access arguments' => array('access content'),
  );

  return $items;
}

By hitting ng_node/calender/display through the AngularJS application, it will trigger ng_node_calender_display, which will load a partial view through the Drupal template system. We have used <div ui-calendar="uiConfig.calendar" ng-model="eventSources"></div>, which is responsible for loading AngularJS directives for Calendar. Look at the code below to understand better:

function ng_node_calender_display(){
  return drupal_get_form('calender_display_form_builder');
}

function calender_display_form_builder($form, &$form_state){
  $form['calender_display'] = array(
      '#type' => 'item',
      '#ng_controller' => 'CalenderCtrl',
      '#markup' => '<div ui-calendar="uiConfig.calendar" ng-model="eventSources"></div>'
  );
  return $form;
}

If you look at the above code, you will notice #ng_controller => CalenderCtrl. This property is provided by the angularjs module so that we can bind controller class through Drupal Form API. In this way, angularjs will know we have attached a controller with this element. As there are other properties also provided by that module (fpr eg., #ng_model), you can also add more AngularJS properties by using the #attributes property of FORM API.

Here is the screen-shot of the final result:

Configuring Drupal To Enable Integration With AngularJS

Conclusion

We have covered the basic techniques to show you how to use Drupal Framework with AngularJS Framework with adherence to Drupal Standards. Hope this post helps you with your Drupal-Angularjs integration project!

Neerav Mehta

Neerav Mehta

Founder & CEO

Neerav Mehta is the Founder & CEO of Red Crackle. With sterling qualities, Neerav’s technological acumen is firing a generation of progressive companies on the digital path. With an undergraduate degree in Electrical Engineering from India's most prestigious institution IIT Bombay and having spent seven years developing and contributing to the launch of AMD's innovative line of computer products, Neerav founded Red Crackle where he is lauded for his dynamic and innovative genius.

View all posts

>

Read Next

10 Tips For Entrepreneurs In 2015

10 Tips For Entrepreneurs In 2015

Learn more

10 Ways To Increase Productivity At Work

10 Ways To Increase Productivity At Work

Learn more

30 best WordPress widgets for your site

30 best WordPress widgets for your site

Learn more

Let’s get you started!

Contact Us

>

RedCrackle

Explore

About Us

Services

Contact Us

Our address

5346 Gerine Blossom Dr,

San Jose, CA 95123

USA

Socials

Twitter
LinkedIn

© 2023 RedCrackle. All rights reserved.