Build A Drupal-free Theme with 8's REST API and Javascript
rcaracaus
themer @ PreviousNext
Disclaimer: I am not an AngularJS developer
- I am a Drupal themer
- At DrupalSouth in Wellington...
- Important that we understand the basics of how these new technologies work
<DIVITIS>
This markup needs 4+ template overrides to remove/alter.
<div class="block-views-markup first">
<div class="view">
<div class="views-row views-row-first">
<div class="node content">
<div class="field field-recipe-title">
This is a Recipe's title
</div>
<div class="field field-recipe-body">
This is body content of recipe.
</div>
</div>
...
-
block.tpl
-
views--foo.tpl
-
node.tpl
-
field.tpl
- Not even getting into preprocess_hooks or config.
- Twig didn't fix our markup problem.
- Dream markup will never happen because there are benefits to markup hell.
Lean markup, scalable css.
<article class="recipe">
<div class="heading--primary">{{ recipe.title }}</div>
<div class="bodyText--light">{{ recipe.body }}</div>
</article>
Web component:
<recipe></recipe>
- What if there was a way to write markup and classes first and fetch data later?
- Download Mothership!
- OOCSS, BEM syntax. Maybe you don't want a bunch divs
- Angular can help you work pull in data.
- Backbone.js and Ember.js can also help you do this.
- Templating and fetching data is only part of Angular's super human strength
- First exposure to API's and web services in Drupal
- API and endpoints "in a box" with petitions distro.
- Drupal 7 services contrib module
Configure D8 REST
- configure a rest endpoint
- don't forget permissions
Postman!
- Demo local postman (node/1) after setting up endpoint
Enable CORS
header('Access-Control-Allow-Origin: *');
D8 CORS contrib module
- To share across domins you need CORS - Cross-origin resource sharing.
- Chat briefly about headers.
- Posting becomes more complicated... Pre-flight request.
Drupal 8 Server
/node/{nid}
/views-node-listing
Client-side Theme
Node Detail Page
/node/{nid}
endpoint/foo
Home page
/views-listing
- Let's see how these two sites will interact.
- We need to move raw data from Drupal into our Angular theme.
- Briefly show demo site. -- Fix Node pages.
- But we need to learn a bit more about Angular..
Angular Directives
<!-- index.html -->
<html ng-app="clientTheme">
<body>
<div ng-contoller="FormCtrl">
<a href="" ng-click="showForm = !showForm">Click to show form!</a>
<form ng-show="showForm"></form>
</div>
<script src="js/angular.js"></script>
<script src="js/app.js"></script>
</body>
</html>
//app.js
var theme = angular.module('clientTheme', []);
theme.controller('FormCtrl', function($scope) {
$scope.showForm = false;
});
-
index.html
-
app.js
-
angular.js
http://plnkr.co/edit/r1jXoOYfyRaWXE3k3KHO
$Scope
[{
"title": "Burrito",
"body": "Very yum..."
}]
Controller
Controller defines $scope.
// app.js
theme.controller('recipeCtrl',
function($scope) {
$scope.recipe.title = 'Burrito';
$scope.recipe.body = 'Very yum...';
});
$scope to Markup
ng-controller binds $scope to <div>
<!-- index.html -->
<div ng-controller="recipeCtrl">
<b>{{ recipe.title }}</b><br>
<em>{{ recipe.body }}</em>
</div>
{{ recipe.meNoUnderstand }}
BurritoVery yum...
Local Data File
recipes.json
[{
"id" : 1,
"name": "Tacos de Guerrero"
},
{
"id" : 2,
"name": "Burritos de Jalisco"
},
... more recipes
]
Service
$http.get('recipes.json')
$Scope
[{
"id" : 1,
"name": "Tacos de Guerrero"
},
{
"id" : 2,
"name": "Burritos de Jalisco"
},
... more recipes
]
Controller
recipesCtrl defines $scope.
// defined by recipeCtrl
$scope.recipes
= $http.get('recipes.json');
Markup
ng-controller binds $scope to <div>
<div ng-controller="recipesCtrl">
{{ recipes }}
</div>
Fetch raw data with a service
theme.factory('recipeService', function($http) {
return {
getRecipes: function(callback) {
$http.get('recipes.json').success(callback);
}
}
});
Then controller uses service to fetch data
theme.controller('recipesCtrl', function($scope, recipeService) {
recipeService.getRecipes(function(data) {
$scope.recipe = data;
});
});
http://plnkr.co/edit/a7wVJBTraXKmmaObDJXk
- Added app alias for eaiser reference.
GET and REST
theme.config(function($httpProvider){
$httpProvider.defaults.headers.common['Accept'] = 'application/hal+json';
// delete $httpProvider.defaults.headers.common['X-Requested-With'];
});
- The Restful approach is to have one resource, multiple formats.
- We need "Accept" "application/hal+json" header on requests.
- X-Requested-With depending on which version of angular you have.
GET a node from Drupal site
theme.factory('nodeService', function($http) {
return {
getNode: function(nid, callback) {
$http.get('http://mydrupalsite.com/node/' + nid).success(callback);
}
}
}
http://plnkr.co/edit/2y2pnDs9mHalL5MJqmVe
Routing
- Pathauto, pathalias, and menu system is usesless.. unless we use Rest to export config.
- Single-page apps used to use hashbang URL's (anchors).
- pushState was developed from HTML5's History API
- I am pushing it trying to mimic Drupal's functionality.
Set up Angular routes
<head>
<script src="js/angular.js"></script>
<script src="js/angular-route.js"></script>
<script src="app.js"></script>
</head>
<body>
<a href="/">Home</a>
<a href="/contact">Contact</a>
<a href="/about">About</a>
// ng-view is like $content in page.tpl
<div ng-view></div>
</body>
- Discuss dependencies and ng-route.. mention bower.
Set up Angular routes
var theme = angular.module('clientTheme', ['ngRoute']);
clientTheme.config(function($routeProvider) {
$routeProvider
.when('/', {
templateUrl : 'templates/home.html',
controller : 'mainController'
})
.when('/contact', {
templateUrl : 'templates/contact.html',
controller : 'contactController'
});
.when('/about-us', {
templateUrl : 'templates/about.html',
controller : 'aboutController'
});
});
- Routes are specified from a single location. Routing table.
- Nodes are not assumed to be pages by default.
http://plnkr.co/edit/O61t3k4mguAkV354XZaK
Arguments in Angular
- Arguments are known as $routeparams in Angular
- These can be specified as a wildcard, similar to Drupal's (%) sign.
Arguments with Angular via $routeparams
Node detail page: node/1 vs. node/2
clientTheme.config(function($routeProvider) {
$routeProvider
.when("/node/:nid", {
templateUrl : "node.html" ,
controller : "NodeCtrl"
});
});
app.controller("NodeCtrl", function($scope, $routeParams) {
$scope.nid = $routeParams.nid;
});
http://plnkr.co/edit/pJCzB8vYfN6W23MsHQNr
- :nid is like % sign in Drupal views or tokens, serves as a wildcard route.
- In our controller we can assign :nid in the URL to the route and prints out on page. See example.
Node detail pages with $routeparams
app.factory('dftService', function($http) {
return {
getNode: function(nid, callback) {
$http.get('http://mydrupalsite.com/node/' + nid)
.success(callback);
}
}
});
Controller
app.controller('NodeCtrl',
function($scope, dftService, $routeParams) {
dftService.getNode($routeParams.nid, function(data) {
$scope.node = data;
});
});
http://plnkr.co/edit/3ekxhyl9kZdVIwVBRT40
- This is why we abstracted into a service, we might want to feed it an argument. NID
- Now in our controller, since we know $routeparams we can feed that into our service.
- Demo on live site.
GET a view
- Views are powerful when combined with Web Services.
- Live demo
- Similar to recipes.json -- we can use ng-repeat
Filtering, sorting and searching data with Angular
ng-repeat="recipe in recipes | filter:{field_tags:searchTermText}"
- Live demo
- Using categories via taxonomy to create angular filters.
GET custom block onto homepage > DBLOGresource
- Live demo if internet working
Posting, editing, and authentication
- Just like GET, you can also POST, one of main advantages of DRUPAL 8 REST
Should we use a client-side theme?
- I would not use Angular for a content heavy website.
- The purpose was to show you some ideas around client-side templating.
- Other MVC frameworks offer similar features. Backbone more stripped down but in core.
Routing
- Originally used hashbang URLs until history API came out
- SEO.. still shaky, but some good results. Prerender.io
- Google analytics.
Learning curves
- Using a client-side theme means no Drupal setups, no LAMP.
- Angular was originally developed for designers. Now it is for developers. More curves.
Templating
- Dev/themer analogy.
- Drupal 8 div crazyness is necessary sometimes, it injects logic into theme.