Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
SlideShare a Scribd company logo
Client-side Rendering with AngularJS 
OpenStack Summit, Paris 
David Lapsley 
@devlaps, dlapsley@cisco.com 
November 3, 2014
Client-side rendering in production
Client-side Rendering 
“Full” dataset search 
“Full” pagination Cache up to 1K records client-side
Real-time Data 
Updates every 5s 
Increased platform visibility 
Every node instrumented
Historical Metrics 
Up to 1 year of data 
Convenient access 
Increased platform visibility Every node instrumented
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 World 
index.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 Structure 
openstacksummit/ 
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.py 
class 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 
“Full” pagination Cache up to 1K records client-side
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!
Thank You 
David Lapsley 
@devlaps, david.lapsley@metacloud.com
If this sounds interesting… 
http://jobs.metacloud.com 
We are hiring!
Client-side Rendering with AngularJS

More Related Content

Client-side Rendering with AngularJS

  • 1. Client-side Rendering with AngularJS OpenStack Summit, Paris David Lapsley @devlaps, dlapsley@cisco.com November 3, 2014
  • 3. Client-side Rendering “Full” dataset search “Full” pagination Cache up to 1K records client-side
  • 4. Real-time Data Updates every 5s Increased platform visibility Every node instrumented
  • 5. Historical Metrics Up to 1 year of data Convenient access Increased platform visibility Every node instrumented
  • 11. Core concepts ● Model View Controller framework ● Client-side templates ● Data binding ● Dependency injection
  • 12. Hello World index.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
  • 15. Adding a new Horizon feature with AngularJS
  • 16. Directory Structure openstacksummit/ 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
  • 17. REST Resource: hypervisors.py class 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']
  • 18. 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. }; … });
  • 19. 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 %}
  • 20. 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>
  • 21. 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>
  • 22. Client-side Rendering “Full” dataset search “Full” pagination Cache up to 1K records client-side
  • 23. Why?
  • 24. 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
  • 26. Thank You David Lapsley @devlaps, david.lapsley@metacloud.com
  • 27. If this sounds interesting… http://jobs.metacloud.com We are hiring!

Editor's Notes

  1. Server provides data over RESTful API – json data Client responsible for rendering and presenting Much more interactive, client can now take immediate action on data it knows about instead of round tripping to server Faster feature velocity, server and client can be developed in parallel Simpler code
  2. Instrument each node with livestastd Controllers and Hypervisors Pull information about processes, network, disk, cpu Updates every 5 seconds Using the same client-side rendering pattern as before Aggregating real-time data Greatly increases visibility into platform..
  3. Our customers also wanted to see more historical data. We instrumented all of our nodes and push data into Graphite Very high performance and flexiblie time series tool Evolution of RRD Tool Graphite provides data over RESTful API, so we just pull that data and use it to populate these charts.. Easy access for the user (simply click on controller or hypervisor row and chart will drop down) Data from 1 hour range to 1 year range..
  4. Add client-side…
  5. Controls view logic
  6. Collaborate, share our experience, find out if there are better ways of doing things