This tutorial describes how to build a very simple de-coupled Drupal web application powered by Angular JS and Bootstrap. The inspiration for writing this tutorial came after completing my first Angular JS module ( angular-drupal ), which of course is for Drupal!

To keep things simple, and in the spirit of "Hello World", the application will let us login using credentials from the Drupal website, and then say hello to the user upon successful login.

The complete code for this example app is available here: https://github.com/signalpoint/headless-drupal-angular-bootstrap-hello-w...

Ready? Alright, let's go headless...

Prerequisites

Primary Technologies Involved

0. Clone the Angular Seed Project

Once you have the prerequisites mentioned above installed, you can git clone (or download and extract it to /var/www/headless-drupal) the project:

https://github.com/angular/angular-seed

cd /var/www git clone --depth=1 https://github.com/angular/angular-seed.git headless-drupal chgrp -R www-data headless-drupal chmod g+s -R headless-drupal cd headless-drupal sudo npm install bower install

1. View the Angular Seed Project in a Browser

We should now be able to view the Angular Seed Project in our browser, just navigate to:

http://localhost/headless-drupal/app

2. Add jQuery and Bootstrap to the Project

Open the /var/www/headless-drupal/bower.json file and add bootstrap to the end of the dependencies object:

"bootstrap": "~3.1.1"

Then run the bower install command again:

bower install

This command will install bootstrap, and its dependency jquery.

Next, include the bootstrap css file, and the jquery and bootstrap javascript files in the index.html file right before the closing <head> tag:

<link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.min.css"> <script src="bower_components/jquery/dist/jquery.min.js"></script> <script src="bower_components/bootstrap/dist/js/bootstrap.min.js"></script>

3. Add angular-drupal to the Project

Open the package.json file and add angular-drupal to the devDependencies:

"angular-drupal": "^0.0.2"

Then install this new dependency by running npm install again:

sudo npm install

Then include the angular-drupal javascript file in the index.html file immediately after the other angular javascript files in the <body> tag, and before the app.js file:

<script src="../node_modules/angular-drupal/build/angular-drupal.min.js"></script>

Unfortunately another bower project is using the angular-drupal namespace, so we can't use bower for this, le sigh.

4. Install Drupal Alongside the App

cd /var/www/headless-drupal wget http://ftp.drupal.org/files/projects/drupal-7.38.tar.gz tar -zxvf drupal-7.38.tar.gz rm drupal-7.38.tar.gz mv drupal-7.38/ drupal mysql -u root -p create database headless_drupal; exit; cd drupal/sites/default mkdir files cp default.settings.php settings.php chmod 775 settings.php

Now we can install Drupal by visiting the following URL in our browser:

http://localhost/headless-drupal/drupal

After the installation, remove the write permissions from the settings.php file:

chmod 444 settings.php

5. Install and Setup the Services Module for Drupal

drush dl services drush en -y rest_server

Then create a new endpoint by going to admin/structure/services/add with the following info:

machine name: api server: REST path: api debug: unchecked session authentication: checked

Then click the edit resources link and check the box next to each resource (at minimum we must enable the user login resource for this demo):

comment file node system taxonomy_term taxonomy_vocabulary user

Then click Save. After that, click the Server tab and make sure the following boxes are checked:

json application/json application/x-www-form-urlencoded

Then click Save. After that flush all of Drupal's caches.

drush cc all

6. Create a User Login Form

Hop into the app directory, then make a folder called user_login, along with two files user_login.html and user_login.js:

cd app mkdir user_login cd user_login touch user_login.html touch user_login.js

Open the user_login.html file, paste this code into it and save the file:

<div ng-controller="UserLoginCtrl" class="container"> <form novalidate class="form-signin simple-form"> <h2 class="form-signin-heading">Please sign in</h2> <label for="edit-name" class="sr-only">Username</label> <input type="text" ng-model="user.name" id="edit-name" class="form-control" placeholder="Username" required autofocus> <label for="edit-pass" class="sr-only">Password</label> <input type="password" ng-model="user.pass" id="edit-pass" class="form-control" placeholder="Password" required> <button class="btn btn-lg btn-primary btn-block" type="submit" ng-click="submit(user)" >Login</button> </form> </div> <!-- /container -->

The html for this page was borrowed from the page source of the Signin Template for Bootstrap, and then converted into an Angular JS Form.

Then open the user_login.js file, paste this code into it and save the file:

'use strict'; angular.module('myApp.user_login', ['ngRoute']) .config(['$routeProvider', function($routeProvider) { $routeProvider.when('/user/login', { templateUrl: 'user_login/user_login.html', controller: 'UserLoginCtrl' }); }]) .controller('UserLoginCtrl', ['$scope', 'drupal', function($scope, drupal) { $scope.submit = function(user) { drupal.user_login(user.name, user.pass).then(function(data) { alert('Hello world and hello ' + data.user.name + '!'); }); }; }]);

Notice how we use Angular's dependency injection on the UserLoginCtrl to include drupal, which is a service provided by the angular-drupal module.

Then open the index.html file and replace the entire ul menu with this:

<nav class="navbar navbar-inverse navbar-fixed-top"> <div class="container"> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="#">angular-drupal</a> </div> <div id="navbar" class="collapse navbar-collapse"> <ul class="nav navbar-nav"> <li><a href="#/view1">view1</a></li> <li><a href="#/view2">view2</a></li> <li><a href="#/user/login">Login</a></li> </ul> </div><!--/.nav-collapse --> </div> </nav>

Then include the user_login.js file before the closing <body> tag of the index.html file:

<script src="user_login/user_login.js"></script>

Finally include the myApp.user_login and angular-drupal modules in the app.js file:

'myApp.user_login', 'angular-drupal'

And then add the angular-drupal configuration to the bottom app.js file:

// Angular Drupal Configuration Settings angular.module('angular-drupal').config(function($provide) { $provide.value('drupalSettings', { sitePath: 'http://localhost/headless-drupal/drupal', endpoint: 'api' }); });

7. Add some CSS

Open the app.css file and add some simple styles:

div[ng-view] { margin-top: 60px; } form input { margin-bottom: 0.5em; }

8. Logout of the Drupal Website

Since our Drupal site lives in the same domain as the app, they will share session information. In order to test the user login form in the app, we must log out of Drupal. Do so now, or forever hold your peace.

9. Run the App

Now if we reload the app, and navigate to the Login page we can then enter our credentials for Drupal user #1:

And if all goes well, we can login and a nice machine will say hello world to us!

10. Conclusion

Well that was fun, wasn't it? From here it's up to you how the app takes shape. The angular-drupal module is there to easily handle reading/writing entities to/from Drupal, as well as handling user logout and registration.

In the mean time, keep a lookout for the release of DrupalGap 2.x, the next generation application development kit for Drupal websites.