An Angular JS module for Drupal 8 RESTful Web Services.
This Angular module makes it easy to read/write
entity data to/from
Drupal,
handles user authentication and registration, and makes it easy to retrieve
JSON data from Views.
Here's a very simple Angular app that loads node # 123
from Drupal and then
displays the node's title (via an alert
):
// My simple app.
angular.module('myApp', ['angular-drupal']).run(['drupal', function(drupal) {
drupal.node_load(123).then(function(node) {
alert(node.title);
});
}]);
// The angular-drupal configuration settings for my simple app.
angular.module('angular-drupal').config(function($provide) {
$provide.value('drupalSettings', {
sitePath: 'http://my-drupal-site.com'
});
});
There are two main parts to the installation and usage of this module. First, on your Drupal site you need to install the Angular Drupal module, then install and configure the Services module. Finally, include the angular-drupal module in your Angular JS application.
https://www.drupal.org/project/angular_drupal
drush dl angular_drupal
drush en -y angular_drupal
https://www.drupal.org/project/restui
Then go to "admin/config/services/rest" and enable your desired resources. We recommend the following resources, http methods, authentications, and formats:
User - GET - json - cookie
User - POST - json - cookie
Go to admin/people/permissions and allow a user role(s) to access some of these resources. We recommend the following (at minimum) for anonymous and authenticated users:
Access GET on Content resource
Access GET on User resource
As usual, be sure to include the angular-drupal.js
file in your app. This
typically is included via the index.html
file somewhere after you include the
angular.js
file:
<script src="angular-drupal.js"></script>
The angular-drupal
module comes with a Service called drupal
. You can
include this service throughout your app using Angular's dependency injection
mechanism.
The simple app, listed above, injects the drupal
service into the app's run
function. Then when the app runs it loads node # 123
from Drupal and then
alerts the node's title.
Notice how we used a config
function on the angular-drupal
module in the
simple app to provide the URL to our Drupal site, as well as the machine name of
the Services endpoint. Without this, the module won't know how to connect to
our Drupal site, so this must be added to our app as in the example above.
drupal.connect().then(function(data) {
if (data.user.uid) { alert('Hello ' + data.user.name + '!'); }
else { alert('Please login.'); }
});
var account = {
name: 'bob',
mail: 'bob@example.com',
pass: 'secret'
};
drupal.user_register(account).then(function(data) {
alert('Registered user # ' + data.uid);
});
@see https://www.drupal.org/node/2403307
drupal.user_login('bob', 'secret').then(function(data) {
});
drupal.user_logout().then(function(data) {
if (!data.user.uid) {
alert('Logged out!');
}
});
var node = {
type: 'article',
title: 'Hello world',
language: 'und',
body: { und: [ { value: 'How are you?' }] }
};
drupal.node_save(node).then(function(data) {
alert('Created node: ' + data.nid);
});
drupal.node_load(123).then(function(node) {
alert('Loaded node: ' + node.title);
});
var node = {
nid: 123,
title: 'Goodbye world',
language: 'und',
body: {
und: [ { value: 'I am fine, thank you.' }]
}
};
drupal.node_save(node).then(function(data) {
alert('Updated node: ' + data.nid);
});
drupal.node_delete(123).then(function(data) {
if (data[0]) {
alert('Deleted node.');
}
});
var query = {
parameters: {
'type': 'article'
}
};
drupal.node_index(query).then(function(nodes) {
var msg = '';
for (var i = 0; i < nodes.length; i++) {
var node = nodes[i];
msg += 'Loaded node: ' + node.title + '\n';
}
alert(msg);
});
To create a new user account, the user must have the Administer users
permission enabled in Drupal.
var account = {
name: 'jane',
mail: 'jane@example.com',
pass: 'secret-sauce'
};
drupal.user_save(account).then(function(data) {
alert('Created new user #' + data.uid);
});
drupal.user_load(1).then(function(account) {
alert('Loaded user: ' + account.name);
});
To update an existing user account, the user must have the Change own username
or Administer users
permission enabled in Drupal.
var account = {
uid: 123,
name: 'john'
};
drupal.user_save(account).then(function(data) {
alert('Name changed to: ' + data.name);
});
The user must have the Administer users
permission to delete a user account.
drupal.user_delete(123).then(function(data) {
if (data[0]) {
alert('Deleted user.');
}
});
var query = {
parameters: {
'name': 'bob'
}
};
drupal.user_index(query).then(function(users) {
var msg = '';
for (var i = 0; i < users.length; i++) {
var user = users[i];
msg += 'Loaded user: ' + user.name + '\n';
}
alert(msg);
});
var comment = {
nid: 123,
subject: 'Hello world',
comment_body: { und: [ { value: 'How are you?' } ] }
};
drupal.comment_save(comment).then(function(data) {
alert('Created comment: ' + data.cid);
});
drupal.comment_load(123).then(function(comment) {
alert('Loaded comment: ' + comment.subject);
});
var comment = {
cid: 456,
subject: 'Goodbye world',
comment_body: { und: [ { value: 'I am fine, thank you.' }] }
};
drupal.comment_save(comment).then(function(data) {
alert('Updated comment: ' + data.cid);
});
drupal.comment_delete(123).then(function(data) {
if (data[0]) {
alert('Deleted comment.');
}
});
var query = {
parameters: {
'nid': 123
}
};
drupal.comment_index(query).then(function(comments) {
var msg = '';
for (var i = 0; i < comments.length; i++) {
var comment = comments[i];
msg += 'Loaded comment: ' + comment.subject + '\n';
}
alert(msg);
});
var base_64_encoded_image = 'abc...xyz';
var file = {
file: base_64_encoded_image,
filename: 'my_image.jpg',
filepath: 'public://my_image.jpg'
};
drupal.file_save(file).then(function(data) {
alert('Saved file # ' + data.fid);
});
To load a file the user must have the Get any binary files
permission in
Drupal.
drupal.file_load(123).then(function(file) {
alert('Loaded file: ' + file.filename);
});
// @see https://api.drupal.org/api/drupal/includes!common.inc/constant/SAVED_NEW/7
var taxonomy_term = {
vid: 1,
name: 'Hello world'
};
drupal.taxonomy_term_save(taxonomy_term).then(function(data) {
if (data[0] == 1) { // SAVED_NEW
alert('Created taxonomy term.');
}
});
drupal.taxonomy_term_load(123).then(function(term) {
alert('Loaded term: ' + term.name);
});
// @see https://api.drupal.org/api/drupal/includes!common.inc/constant/SAVED_UPDATED/7
var taxonomy_term = {
vid: 1,
tid: 123,
name: 'Goodbye world'
};
drupal.taxonomy_term_save(taxonomy_term).then(function(data) {
if (data[0] == 2) { // SAVED_UPDATED
alert('Updated taxonomy term.');
}
});
// @see https://api.drupal.org/api/drupal/includes!common.inc/constant/SAVED_DELETED/7
drupal.taxonomy_term_delete(123).then(function(data) {
if (data[0] == 3) { // SAVED_DELETED
alert('Deleted taxonomy term.');
}
});
var query = {
parameters: {
'vid': 1
}
};
drupal.taxonomy_term_index(query).then(function(taxonomy_terms) {
var msg = '';
for (var i = 0; i < taxonomy_terms.length; i++) {
var taxonomy_term = taxonomy_terms[i];
msg += 'Loaded taxonomy term: ' + taxonomy_term.name + '\n';
}
alert(msg);
});
// @see https://api.drupal.org/api/drupal/includes!common.inc/constant/SAVED_NEW/7
var taxonomy_vocabulary = {
name: 'Fruits',
machine_name: 'fruits',
description: 'Fruit is delicious.'
};
drupal.taxonomy_vocabulary_save(taxonomy_vocabulary).then(function(data) {
if (data[0] == 1) { // SAVED_NEW
alert('Created taxonomy vocabulary.');
}
});
drupal.taxonomy_vocabulary_load(1).then(function(vocabulary) {
alert('Loaded vocabulary: ' + vocabulary.name);
});
// @see https://api.drupal.org/api/drupal/includes!common.inc/constant/SAVED_UPDATED/7
var taxonomy_vocabulary = {
vid: 2,
name: 'Colorful Fruits',
machine_name: 'fruits',
description: 'Colorful fruit is even more delicious.'
};
drupal.taxonomy_vocabulary_save(taxonomy_vocabulary).then(function(data) {
if (data[0] == 2) { // SAVED_UPDATED
alert('Updated taxonomy vocabulary.');
}
});
// @see https://api.drupal.org/api/drupal/includes!common.inc/constant/SAVED_DELETED/7
drupal.taxonomy_vocabulary_delete(123).then(function(data) {
if (data[0] == 3) { // SAVED_DELETED
alert('Deleted taxonomy vocabulary.');
}
});
var query = {
parameters: {
'name': 'tags'
}
};
drupal.taxonomy_vocabulary_index(query).then(function(taxonomy_vocabularys) {
var msg = '';
for (var i = 0; i < taxonomy_vocabularys.length; i++) {
var taxonomy_vocabulary = taxonomy_vocabularys[i];
msg += 'Loaded taxonomy vocabulary: ' + taxonomy_vocabulary.name + '\n';
}
alert(msg);
});
If you install the Views JSON module, which is available as a sub module of the Views Datasource module (https://www.drupal.org/project/views_datasource), you can easily set up a View page display to return JSON to your app:
var path = 'articles'; // The Drupal path to the Views JSON page display.
drupal.views_json(path).then(function(rows) {
angular.forEach(rows, function(row, i) {
console.log(row.title);
});
});
For more information on creating Views JSON page displays, read this:
The angular-drupal
module automatically takes care of the X-CSRF-Token
when
it is needed. If you need to manually get the token it can easily be retrieved:
drupal.token().then(function(token) {
console.log('Got the token: ' + token);
});
This module has unit test coverage to maintain quality, and welcomes comments, criticisms and pull requests.
Thank you!