Client-side Rendering with AngularJS
OpenStack Summit, Paris
David Lapsley
@devlaps, [email protected]
November 3, 2014
Client-side rendering in production
Client-side Rendering
“Full” dataset search
Cache up to 1K records client-side“Full” pagination
Real-time DataUpdates every 5s
Increased platform visibility
Every node instrumented
Historical Metrics
Up to 1 year of data
Increased platform visibility Every node instrumented
Convenient access
OpenStack Horizon
Architecture
Django Stack
Horizon Stack
Horizon Stack Extended
AngularJS
Core concepts
● Model View Controller framework
● Client-side templates
● Data binding
● Dependency injection
Hello Worldindex.html
<html ng-app>
<head>
<script src="angular.js"></script>
<script src="controllers.js"></script>
</head>
<body>
<div ng-controller='HelloController'>
<p>{{greeting.text}}, World</p>
<button ng-click="action()">Alert</button>
</div>
</body>
</html>
controllers.js
function HelloController($scope) {
$scope.greeting = { text: 'Hello' };
$scope.action = function() {
alert('Action!');
};
}
AngularJS
By: Brad Green; Shyam Seshadri
Publisher: O'Reilly Media, Inc.
Pub. Date: April 23, 2013
Hello World
Hello World
Adding a new Horizon feature
with AngularJS
Directory Structureopenstacksummit/
hypervisors/
__init__.py
panel.py
urls.py
views.py
tables.py
tests.py
templates/openstacksummit/hypervisors/
index.html
static/openstacksummit/hypervisors/js/
hypervisors-controller.js
rest/nova/
__init__.py
hypervisor.py
instance.py
REST Resource: hypervisors.pyclass HypervisorResource(resource.BaseNovaResource):
pk = fields.CharField(attribute="pk", _("Primary Key"), hidden=True)
hypervisor_hostname = fields.CharField(attribute='hypervisor_hostname',
sortable=True,
searchable=True)
…
actions = fields.ActionsField(attribute='actions',
actions=[HypervisorViewLiveStats,
HypervisorEnableAction,
HypervisorDisableAction],
title=_("Actions"),
sortable=True)
class Meta:
authorization = auth.RestAuthorization()
list_allowed_methods = ['get']
resource_name = '^hypervisor'
field_order = ['pk', 'hypervisor_hostname', 'hypervisor_type', 'vcpus',
'vcpus_used', 'memory_mb', 'memory_mb_used',
'running_vms', 'state', 'status', 'actions']
Controller: hypervisor-controller.js
horizonApp.controller('TableController',
function($scope, $http) {
$scope.headers = headers;
$scope.title = title;
$http.get('/rest/api/v1/nova/instance/').success(
function(data, status, headers, config) {
$scope.instances = transform(data.objects);
});
});
horizonApp.controller('ActionDropdownController',
function($scope) {
$scope.status = {
isopen: false
};
$scope.toggleDropdown = function($event) {
$event.preventDefault();
$event.stopPropagation();
$scope.status.isopen = !$scope.status.isopen;
};
$scope.action = function(action, id) {
// Perform action.
};
…
});
View: index.html
{% extends 'base.html' %}
{% load i18n horizon humanize sizeformat %}
{% block title %}{% trans 'Hypervisors' %}{% endblock %}
{% block page_header %}
{% include 'horizon/common/_page_header.html' with title=_('All
Hypervisors') %}
{% endblock page_header %}
{% block main %}
View: index.html
<div ng-controller="TableController">
<table class="...">
<thead>
<tr class="...">
<th class="...">
<h3 class="...">{$ title $}</h3>
</th>
</tr>
<tr class="...">
<th class="..." ng-repeat='header in headers'>
<div class="...">{$ header.name $}</div>
</th>
</tr>
</thead>
View: index.html<tr ng-repeat="instance in instances">
<td ng-repeat="datum in instance.data">{$ datum $}</td>
<td class="...">
<div ng-controller="ActionDropdownController">
<div class="..." dropdown>
<button class="..."
ng-click="action(instance.actions[0], instance.name)">
{$ instance.actions[0].verbose_name $}
</button>
...
<div class="...">
<li class="..." ng-repeat="action in instance.actions">
<a href="#" class="..."
ng-click="$parent.action(action,parent.instance.name)">
{$ action.verbose_name $}
</a>
</li>
</ul>
</div>
</td>
</tr>
</table>
Client-side Rendering
“Full” dataset search
Cache up to 1K records client-side“Full” pagination
Why?
Advantages
● Clean split between server and client side
● Significantly cleaner, terser, easier to
understand client-side code
● Significant easier to improve UX
● Client- and server-side code can be
developed and tested independently
● Faster feature velocity
Better UX
Faster!
If this sounds interesting…
http://jobs.metacloud.com
We are hiring!