Headless Drupal with Angular JS and Bootstrap - Hello World
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.
Comments
Craig Roth (not verified)
Fri, 06/19/2015 - 13:53
Permalink
I got tripped up on step 0:
I got tripped up on step 0: bower; command not found. I'm not sure what to do to fix that so that I can continue the tutorial.
tyler
Fri, 06/19/2015 - 14:06
Permalink
Thanks for pointing that out,
Thanks for pointing that out, I've updated the prerequisites to include Bower, which can be installed via npm with the following command:
RAJEEV (not verified)
Sun, 06/21/2015 - 23:24
Permalink
nice tutorial, can i know
nice tutorial, can i know which app/site are developed with angular-drupal module.
tyler
Tue, 06/23/2015 - 08:48
Permalink
Since this module was just
Since this module was just released, I'm guessing no apps/sites are using it yet. I personally built it as a prerequisite for DrupalGap 2.x, so we can expect much usage when DrupalGap 2.x is released: http://drupalgap.org/angular
If you do end up using the angular-drupal module, please let me know, thanks.
Bryan Savage (not verified)
Mon, 06/22/2015 - 20:23
Permalink
I like this approach to
I like this approach to Drupal-based mobile apps. I have been learning and working on an app since Drupal Camp Ohio. I settled on AngularJS + the IONIC Framework that gets content from a Drupal 7 site with Services.
tyler
Tue, 06/23/2015 - 08:51
Permalink
Thanks for the feedback Bryan
Thanks for the feedback Bryan. My plan for DrupalGap 2.x (http://drupalgap.org/angular) is to use Angular JS + Bootstrap for web apps, and Angular JS + IONIC for mobile apps, while supporting Drupal 7 Services, and Drupal 8 core's REST.
I briefly read that IONIC isn't necessarily meant for web apps, and that's why I'm planning the separation between the two (plus it'll be great to support multiple front end frameworks with the development kit). Have you tried running your IONIC app as a web app in a browser?
I had a great time at DrupalCamp Ohio last year, and hope to attend/present again this year.
Bryan Savage (not verified)
Wed, 06/24/2015 - 07:15
Permalink
The IONIC app worked fine in
The IONIC app worked fine in the browser until I added Cordova features, then it broke in the browser. Bootstrap seems like a great in-browser choice.
Thank you for sharing your experience & knowledge.
Andrew Grigoriev (not verified)
Thu, 07/09/2015 - 14:57
Permalink
I've followed instructions
I've followed instructions from article and recieved error with "angular-drupal" module load. It is require to include angular-drupal.min.js file before app.js in point 3.
Thanks for your great article.
Headless Horseman (not verified)
Mon, 07/20/2015 - 23:24
Permalink
Awesome article !
Awesome article !
Thank you.
I did run into the same problem as Andrew.
It might be worth editing your article to clarify that small point.
angular-drupal.min.js file needs to be before app.js
tyler
Tue, 07/21/2015 - 08:04
Permalink
Thanks, I've updated the
Thanks, I've updated the article to reflect this.
Charles Novick (not verified)
Thu, 07/23/2015 - 16:27
Permalink
Worked a charm!
Worked a charm!
Christian Galicia (not verified)
Mon, 11/02/2015 - 12:25
Permalink
hello! This is great info,
hello! This is great info, thank you very much!
However im not sure how to procede now...
When i enter some credentials at the login form a cookie SESS... gets created and a log message gets added at Drupal log messages registering that the "user session is opened". Look like the session is opened and there should be a way to retrieve the session and current user information and that it should be persistent if i refresh the page and it should persist until the user gets logged out.
However when i refresh the page, it seems like the session gets destroyed since the cookie disapears from the browser debuger and if i try to run
drupal.connect()
it doesnt seem to be able to retrieve the user nor the session data.Im doing it between two different environments, Drupal is at a server on a_domain.org
locally im using yeoman so i have the webapp running at http://localhost:9000
I installed Drupal module CORS and added this configuration
*|<mirror>|POST,ADD,GET,PUT,DELETE,OPTIONS|X-CSRF-Token|true
With this configuration is how i finnally achieved to make this hello world example work.
However now im now sure how should i build my application and handle user permissions and identify the logged in user and session information??
Im i missing something here? How should i handle the website, all of its components so once the user is logged in i can get user information and show and limit functionality according to roles and permisssions?
Thank you for this great documentation!
Steve (not verified)
Fri, 11/20/2015 - 13:03
Permalink
According to this issue
According to this issue (referenced above), it looks like you can now use npm to install angular-drupal.
vvv (not verified)
Wed, 04/13/2016 - 12:29
Permalink
Hi - wondering if this is
Hi - wondering if this is still current. I went through all the steps and got this running on my linode vps. i get the login form to come up at the end but then I get an error with angular complaining about
Uncaught Error: [$injector:nomod] Module 'angular-drupal' is not available! You either misspelled the module name or forgot to load it. If registering a module ensure that you specify the dependencies as the second argument.
Not sure how to troubleshoot, i double checked all my angular dependencies...
it then continues with
angular.js:12783 TypeError: Cannot read property 'sitePath' of null at new drupal (http://23.239.13.122/ctrlcon1/node_modules/angular-drupal/build/angular-...)
Any ideas?
Appreciate any help! Thanks
vvv (not verified)
Wed, 04/13/2016 - 15:33
Permalink
posted something earlier but
posted something earlier but this is just to say i got it working! turns out i had to move the angular-drupal.min.js module out of node_modules and into the app's root directory, renamed it angular-drupal.js and my issues were solved
thanks for an awesome idea!
Lactarius (not verified)
Sun, 04/24/2016 - 10:38
Permalink
angular.js:68 Uncaught Error:
angular.js:68 Uncaught Error: [$injector:nomod] Module 'angular-drupal' is not available! You either misspelled the module name or forgot to load it. If registering a module ensure that you specify the dependencies as the second argument.http://errors.angularjs.org/1.4.10/$injector/nomod?p0=angular-drupal
Paul Coleman (not verified)
Thu, 08/11/2016 - 19:15
Permalink
Hi Tyler,
Hi Tyler,
Thanks for this tutorial.
I wanted to go a little further than login. I modified view1 to display a node. I have included the code to view the title and the body, for node 1. This should work with all Drupal installations all you need to do is create a node, the Title and Body are standard fields. Drupal will return an array of information on a field, so you need to navigate to the part of the content you want to display. One thing not covered in the attached code is adding ngSanitize to your installation so that it will allow you to display HTML content. Angular does not like to display HTML that has not been sanitized, Drupal does this in the Safe_Value setting but this is an easy way to pass this through Angular.
view1.html
<div ng-controller = "View1Ctrl">
<h1>{{node.title}}</h1>
<div ng-bind-html=body></div>
view1.js
'use strict';
angular.module('myApp.view1', ['ngRoute'])
.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/view1', {
templateUrl: 'view1/view1.html', controller: 'View1Ctrl'
});
}])
.controller('View1Ctrl', function($scope, drupal) {
drupal.node_load(1).then(function(node) {
$scope.node = node;
$scope.body = node.body.und[0].value;
});
});
Then I have a question, are views implimented? I could not get views_json to work so I looked at the code and could not find anything for views. Next I will see about coding this. I should just be able to cut and past from the node_lode to add the views_json code.
tyler
Fri, 08/12/2016 - 08:42
Permalink
Although I haven't touched
Although I haven't touched this in a while, Views support should be all set. See here for more information: https://github.com/easystreet3/angular-drupal/blob/7.x-1.x/README.md#views
John (not verified)
Mon, 08/22/2016 - 09:12
Permalink
I've got this example working
I've got this example working working as described, but what is the benefit of replacing the theme layer with angular?
tyler
Mon, 08/22/2016 - 10:56
Permalink
Technically you can replace
Technically you can replace the theme layer with whatever you want (Foundation, roll your own CSS, etc), in this example I'd consider the theme layer as Bootstrap, and Angular as the SDK. The advantage is (IMO) that we have much tighter control over the user interfaces that we build, the big down side is we lose all Drupal theme layers (regions, blocks, form widgets, field displays, etc). The down side is the main reason I started DrupalGap.
Aaron Saunders (not verified)
Mon, 11/21/2016 - 22:01
Permalink
Is the Angular-Drupal module
Is the Angular-Drupal module still supported? Is there any work on supporting angular2 can you contact me about potential contract to get this module integrated into an application?
tyler
Mon, 11/21/2016 - 22:18
Permalink
The module is still supported
The module is still supported yes, although co-maintainers are welcome. The future of this module is dependant on jDrupal, where most improvements are. I haven't personally tried Angular 2, nor have I touched Angular 1 in over a here. Thank you for the inquiry about contract work, at this time I am not taking on any new projects.
g (not verified)
Sun, 01/29/2017 - 07:04
Permalink
I've installed this twice,
I've installed this twice, and both times it does not work properly. I get to the final test of the app where I click the login button, or any of the buttons for that matter, nothing happens. Any tips for troubleshooting it? It's hosted on a Rackspace Centos server v. 6.8 using virtual hosts. I'm fried. Would love to get it working. Thanks.
tyler
Sun, 01/29/2017 - 11:45
Permalink
I honestly haven't tried
I honestly haven't tried Angular since writing this, I've been focused soley on DrupalGap 7 and 8. I've heard some folks say that this is now required to login:
Otherwise, I'd open the Console tab in my browser's development tools, and look for JavaScript warnings, and then move to the Network tab and investigate network activity to see if any errors/warning stood out.