28
Client-side Rendering with AngularJS OpenStack Summit, Paris David Lapsley @devlaps, [email protected] November 3, 2014

Client-side Rendering with AngularJS

Embed Size (px)

DESCRIPTION

The OpenStack Horizon project provides a web-based User Interface to OpenStack services. It is constructed in two parts: (1) a core set of libraries for implementing a Dashboard; (2) the dashboard implementation that uses the core set of libraries. Horizon uses python django — server side technology Django is a wonderful framework, but a little dated. Pre-dates the rise in client-side and single page applications. Javascript is used for enhancing the user experience In the time since Horizon was first architected, there have been major advances in the design, and best practices for web applications. In particular, the use of more sophisticated and robust client-side javascript frameworks like BackboneJS, AngularJS, MeteorJS, have come to the fore. These applications provide a much more responsive user experience, much cleaner separation between the client and server, enable configuration driven interfaces, and facilitate more modular testing. This in turn, results in shorter development cycles, more testable software, and above all, a better user experience. In this presentation, we share some of our recent work in re-architecting parts of Horizon to take advantage of these new technologies. We discuss some of the technologies we use, our application architecture, and some of the pitfalls to avoid.

Citation preview

Page 1: Client-side Rendering with AngularJS

Client-side Rendering with AngularJS

OpenStack Summit, Paris

David Lapsley

@devlaps, [email protected]

November 3, 2014

Page 2: Client-side Rendering with AngularJS

Client-side rendering in production

Page 3: Client-side Rendering with AngularJS

Client-side Rendering

“Full” dataset search

Cache up to 1K records client-side“Full” pagination

Page 4: Client-side Rendering with AngularJS

Real-time DataUpdates every 5s

Increased platform visibility

Every node instrumented

Page 5: Client-side Rendering with AngularJS

Historical Metrics

Up to 1 year of data

Increased platform visibility Every node instrumented

Convenient access

Page 6: Client-side Rendering with AngularJS

OpenStack Horizon

Architecture

Page 7: Client-side Rendering with AngularJS

Django Stack

Page 8: Client-side Rendering with AngularJS

Horizon Stack

Page 9: Client-side Rendering with AngularJS

Horizon Stack Extended

Page 10: Client-side Rendering with AngularJS

AngularJS

Page 11: Client-side Rendering with AngularJS

Core concepts

● Model View Controller framework

● Client-side templates

● Data binding

● Dependency injection

Page 12: Client-side Rendering with AngularJS

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

Page 13: Client-side Rendering with AngularJS

Hello World

Page 14: Client-side Rendering with AngularJS

Hello World

Page 15: Client-side Rendering with AngularJS

Adding a new Horizon feature

with AngularJS

Page 16: Client-side Rendering 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

Page 17: Client-side Rendering with AngularJS

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']

Page 18: Client-side Rendering with AngularJS

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.

};

});

Page 19: Client-side Rendering with AngularJS

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 %}

Page 20: Client-side Rendering with AngularJS

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>

Page 21: Client-side Rendering with AngularJS

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>

Page 22: Client-side Rendering with AngularJS

Client-side Rendering

“Full” dataset search

Cache up to 1K records client-side“Full” pagination

Page 23: Client-side Rendering with AngularJS

Why?

Page 24: Client-side Rendering with AngularJS

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

Page 25: Client-side Rendering with AngularJS

Better UX

Faster!

Page 26: Client-side Rendering with AngularJS

Thank You

David Lapsley

@devlaps, [email protected]

Page 27: Client-side Rendering with AngularJS

If this sounds interesting…

http://jobs.metacloud.com

We are hiring!

Page 28: Client-side Rendering with AngularJS