Binding Drupal data with AngularJS - A Step by Step Tutorial
AngularJS is a popular javascript framework backed by Google. This article will guide you through setting up a simple Angular app using data generated by Drupal. If you’re new to Angular, you may like:
http://angular-tips.com/blog/2013/08/why-does-angular-dot-js-rock/
https://docs.angularjs.org/tutorial/step_00
What you’ll need for this tutorial:
DrupaljQuery UpdateLibrariesAngularJSViewsViews Datasource
This tutorial assumes a decent knowledge of Drupal. Our app will be a collection of code snippets (using the default ‘Article’ content type) filtered by ‘section’ taxonomy (say PHP snippets, CSS snippets etc) - Live Demo
First enable the contrib modules:
at /admin/modules
Now let’s configure these modules:
/admin/config/development/angularjs
(we are using CDN here for ease of use, but we would want to have this locally on production)
/admin/config/development/jquery_update
Now let’s create a custom module called “sections”
and place it in your sites/all/modules/custom
This is what it will end up looking like:
create a sections.info
name = Sections description = Angular sections version = 7.x-1.0 core = 7.x
Now enable the “sections” custom module at at /admin/modules
Create sections.module
In there we will add page callback and add our js dependencies:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**
* Implement hook_menu().
*/
function
sections_menu() {
$items [
'sections'
] = array(
'page callback'
=>
'all_sections_page'
,
'access arguments'
=> array(
'access content'
),
);
return
$items;
};
/**
* hook_theme()
*/
function
sections_theme() {
return
array(
'all_sections'
=> array(
'template'
=>
'all-sections'
,
),
);
}
/**
* All sections callback
*/
function
all_sections_page() {
$path = Drupal_get_path(
'module'
,
'sections'
);
Drupal_add_js($path .
'/js/sections.gen.js'
);
return
theme(
'all_sections'
);
}
We are now calling a template file so we need to create it:
in all-sections.tpl.php
1
2
3
<div id=
"sections-app"
class
=
"ng-container"
>
<div ng-view=
""
class
=
"anim"
></div>
</div>
What we are doing here is defining a simple container for our app. The important thing here is the ID “sections-app”. In regular AngularJS apps we would have used ng-app=sections-app instead, but in this case — and because we are using Drupal, we can avoid some issues by bootstrapping with jQuery, which is native to Drupal anyway. The ng-view defines the container for our html files within the “templates” folder.
Before we set up our Angular app, let’s set up the back end that will build our Angular-ready data, basically outputting the Drupal data as Json with views and views data source.
Data
1) A taxonomy type called “Section” (machine name being section)
2) The article content type, which we modify as such:
3) Our first view:
Pretty basic as well. A page with a path we need for our controller, the fields from taxonomy, and a simple filter. The magic resides in the format obviously, provided by the views datasource modules. It will allow us to expose the data as json to the browser (no rendering).
The root object name is pretty important here. We can strip the markup or not, it really depends on the kind of content we have, but eventually it may modify the way we render the data with or without ng-render.
Notice you’ll have to uncheck the views API mode checkbox.
4) Our second view
This one is for the ‘article content type’. Here the path takes an argument, defined by a contextual filter.
That is the main difference, except that we also need to make sure that we don’t strip the html in our json config and use the ‘node’ root object name.
Pretty simple.
The angular code
1) The app
in sections.gen.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
'use strict'
;
var
sectionsApp = angular.module(
'sectionsApp'
, [
'ngRoute'
,
'ngSanitize'
,
'ngAnimate'
,
'sectionsDirectives'
,
'sectionsControllers'
]);
sectionsApp.config([
'$routeProvider'
,
function
($routeProvider) {
$routeProvider
.when(
'/'
, {
templateUrl:
'/sites/all/modules/custom/sections/templates/sections.html'
,
controller:
'sectionsCtrl'
})
.when(
'/section/:tid'
, {
templateUrl:
'/sites/all/modules/custom/sections/templates/articles.html'
,
controller:
'articlesCtrl'
})
.otherwise({
redirectTo:
'/'
});
}]);
jQuery(document).ready(
function
() {
angular.bootstrap(document.getElementById(
'sections-app'
), [
'sectionsApp'
]);
});
First we define the app dependencies, then our routing; pointing to our templates and controllers; then the jQuery call to initiate the app (as explained above). Nothing fancy here, just the basics.
Notice the '/section/:tid' which is our taxonomy ID, which basically acts like an argument when using views conditional filters (in combination with the controller we will define below).
2) The Controllers
in sections.gen.js, below the previous code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
'use strict'
;
var
sectionsControllers = angular.module(
'sectionsControllers'
, []);
sectionsControllers
.controller(
'sectionsCtrl'
, [
'$scope'
,
'$http'
,
'$location'
,
function
($scope, $http, $location) {
$http.get(
'/json/sections'
).success(
function
(result) {
$scope.sections = (
function
() {
return
result.taxonomy;
})();
});
}])
.controller(
'articlesCtrl'
, [
'$scope'
,
'$routeParams'
,
'$http'
,
'$sce'
,
function
($scope, $routeParams, $http, $sce) {
$http.get(
'/json/'
+ $routeParams.tid +
'/articles'
)
.success(
function
(result) {
$scope.renderHtml =
function
(htmlCode) {
return
$sce.trustAsHtml(htmlCode);
};
$scope.articles = (
function
() {
return
result.node;
})();
});
}]);
Here we are setting up our two controllers that will fetch the Drupal data. The second one includes the $routeParams variable that will interact with the contextual filter we set up in the second view (taxonomy id). It also includes the $sce.trustAsHtml(htmlCode) which will render the html from the json safe.
Now to the templates
create sections.html and place it in the “templates” folder
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<div
class
=
"section"
>
<div
class
=
"search-section"
>
<div
class
=
"wrapper"
>
<input ng-model=
"query"
placeholder=
"Quick Search"
class
=
"text"
id=
"search"
></div>
</div>
<ul
class
=
"ngdata"
><li data-ng-repeat=
"section in sections | filter:query | orderBy:'name'"
ng-
class
-odd=
"'odd'"
ng-
class
-even=
"'even'"
ng-
class
=
"{'first':$first, 'last':$last}"
>
<a href=
"#/section/{{section.tid}}"
class
=
"section-link"
>
<div
class
=
"wrapper"
>
<h2>{{section.name}}</h2>
<div
class
=
"description"
>{{section.description}}</div>
</div>
</a>
</li>
</ul></div>
create articles.html and place it in the “templates” folder
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<div
class
=
"article"
>
<div
class
=
"search-section"
>
<div
class
=
"wrapper"
>
<input ng-model=
"query"
placeholder=
"Quick Search"
class
=
"text"
id=
"search"
></div>
</div>
<ul
class
=
"ngdata"
><li ng-repeat=
"article in articles | filter:query | orderBy:'title'"
ng-
class
-odd=
"'odd'"
ng-
class
-even=
"'even'"
ng-
class
=
"{'first':$first, 'last':$last}"
prism=
""
>
<div
class
=
"wrapper"
>
<div
class
=
"articles"
>
<h2>{{article.title}}</h2>
<div
class
=
"description"
ng-bind-html=
"renderHtml(article.body)"
></div>
</div>
</div>
</li>
</ul></div>
The difference between both is mostly the way we render the html with ng-bind-html and the function we wrote in the controller.
That’s it, it should create a basic working version of the demo, minus the theming and a couple of other functions. Of course you’ll have to populate the content by creating articles filtered with the taxonomy term of your choosing. I also have some directives setup in the online demo, but these are calling scripts to highlight the code with prism and the keyboard navigation which are not needed for the purpose of this tutorial.
There is much to improve and once we get a lot of entries it would probably be important to to scale the app as the performance could be affected.
Got help from many sources online and especially this video tutorial: https://www.youtube.com/watch?v=p3zSQieBIe8
Hope you found this helpful!
Terms:
Related Services
Development
High-quality robust websites and web applications are built for change to grow with your organization.
Product Development
TCa.aS, short for Technical Cofounder as a Service, is a new type of software service relationship for product-focused startups and existing businesses looking to start a new venture or product line.