Opa & angularjs


by Adam Koprowski

who am I?

Adam Koprowski (Adam.Koprowski@gmail.com)

Opa developer turned technical evangelist
@MLstate (2009-2012)

Software engineer in test
@Google (2012-...)
In other words: expect code no theorems :)
 

why javascript?

 ca. 1995 







 2004 

 2013 




why not just  javascript?

well...

  • Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
  • Modularity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
  • Database . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
  • Server-side code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
  • No types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
  • Asynchronous, single-threaded . . . . . . . . . . . . . . .   yield, fibers
  • Security  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . templating
  • Low-levelness, cross-browser incompatibilities  . . . . . libraries
~2009
~2011
~2009
~2009
~2012
 

OPA in a nutshell


Web apps re-imagined


client  ⇔ server  ⇔ database


  • Complete language for web apps
  • Static typing

syntax

     Javascript-esque

function showTodoList(items) {
  mkSingleTodo = function(item) {
    <li>{item}</li>
  }
  #todos = <ul>{List.map(mkSingleTodo, items)}</>
}
        Familiarity

     Coffeescript-esque (but: whitespace not significant)  

showTodoList(items) =
  mkSingleTodo = item -> <li>{item}</li>
  #todos = <ul>{List.map(mkSingleTodo, items)}</>
        Conciseness
        Not supported anymore

modularity

     Packages

package just.an.example  // declaring
import stdlib.widgets.{button, bootstrap.*, dateprinter}  // importing

     Modules

module ModuleA {
  function foo() { … }
}
module ModuleB {
  private module InnerModule { … }
  function bar() { ModuleA.foo(); }
}
        Functors? Yes.

Problem #1
Too many moving parts

'one opa to rule them all'

     Input:

     Output:

client server database
        Consistent language for all parts of web apps...
        but somewhat esoteric.
        Debugging auto-generated JS.

database

     Declaring:

type user = { int id, int age, string name }
database example {
  user /me
  user /users[{id}]   // id is the primary key
}

     Using:

/example/me/age++
minors = /example/users[age < 18]
users_segment = /example/users[age < 18 or age > 35;
                               skip 100; limit 50; order +age, -id]
        This extends type-safety to DB.

client/server separation

     Auto-magical

#greeting = <div>You're visitor no. #{/mydb/counter}</>

            Q:  DOM + DB? Where will this run? Client? Server?

            A:  Both

        Opa takes care of communication

      yet controllable

client function runInTheBrowser() { … }server function runOnTheBackend() { … }exposed function markAsClientsAccessible() { … }
        Code re-use (client-server)
        Performance tuning not always obvious

Problem #2
Mistakes too easy to make

… a puzzle break


  • You're a software developer in Java/C++/… 
  • You're writing a library of geometrical shapes.
  • Goal: function to compute distance between two shapes.
  • Start with: circle & square; assume functions to compute distance between any two given shapes readily available.
 

Ready set… code!

Types to the rescue!

     Extensible records

function getCenter(shape) {
  return shape.center;
}
circle = {center: {x: 10, y: 10}};
 getCenter(circle) = {x: 10, y: 10}
 getCenter({circle with color: "red"}) = {x: 10, y: 10}
 getCenter({name: "Joe"}) ↦ compilation error
 getCenter({centre: {x: 1, y: 1}}) ↦ compilation error

     Type inference (structural typing)

function string toString({name: string, age: int, ...} person) {
  return "{person.name}, born in {2013 - person.age}";
}
personToString({name: "John Doe", age: 23}) = "John Doe, born in 1990"
         All benefits of static typing with little to no effort.

Types to the rescue! (2)

     Extensible records  with variants

type point = {float x, float y}
type shape = {circle, point center} or {square, point ul, point dr};
function point getCenter(shape s) {
  match (s) {
    case ~{circle, center}: center
    case ~{square, ul, dr}: …
  }
}
 getCenter({circle, center: {x: 10., y: 10.}}) = {x: 10., y: 10.}
 getCenter({square, ul: {x: 1., y: 2.}, dr: {3., 4.}}) = {x: 2., y: 3.}
 getCenter({point, center: {x: 2., y: 13.}) ↦ compilation error
 getCenter({circle, center: {x: "zero", y: 1.}) ↦ compilation error
         Large class of errors caught at compilation time (NPE, …).
         Flexible enough to model HTML & CSS as types.
         Extends to the database.

types and security

     Opa:

function page() {
  userInput = "<script type=\"text/javascript\">alert('Pwned!')</script>"
  <div>{userInput}</>
}
Server.start(Server.http, {title: "Test", page: page})

     Javascript:

<div id="test">
<script type="text/javascript">
  //<![CDATA[
    document.getElementById('test').innerHTML =
      "<script type='text/javascript'>alert('Pwned!')</script>";
  //]]>
</script>

... back to the puzzle

Java:

abstract class Shape {
    public static double distance(Shape s1, Shape s2) { return s1.distanceTo(s2); }
    protected abstract double distanceTo(Shape s);
    abstract double distanceToCircle(Shape s);
    abstract double distanceToSquare(Shape s);
}
class Circle extends Shape {
    Point center;
    double radius;
    
    protected double distanceTo(Shape s) { return s.distanceToCircle(this); }
    protected double distanceToCircle(Shape s) { … }
    protected double distanceToSquare(Shape s) { … }
}
class Square extends Shape {
    Point ul;
    Point dr;
    
    protected double distanceTo(Shape s) { return s.distanceToSquare(this); }
    protected double distanceToCircle(Shape s) { … }
    protected double distanceToSquare(Shape s) { … }
}

Opa:

type shape = {circle, center: Point, radius: float}
          or {square, ul: Point, dr: Point}
function distance(shape s1, shape s2) {
  match (s1, s2) {
    case {s1={circle ...}, s2={square ...}}
    case {s1={square ...}, s2={circle ...}}: …
    case {s1={circle ...}, s2={circle ...}}: …
    case {s1={square ...}, s2={square ...}}: …
  }
}

puzzle: java

abstract class Shape {
  public static double distance(Shape s1, Shape s2) {
     return s1.distanceTo(s2);
  }
  protected abstract double distanceTo(Shape s);
  abstract double distanceToCircle(Shape s);
  abstract double distanceToSquare(Shape s);
}

class Circle extends Shape {
  Point center;
  double radius;
    
  protected double distanceTo(Shape s) {
      return s.distanceToCircle(this);
  }
  protected double distanceToCircle(Shape s) { … }
  protected double distanceToSquare(Shape s) { … }
}

class Square extends Shape {
  Point ul;
  Point dr;
    
  protected double distanceTo(Shape s) {
    return s.distanceToSquare(this);
  }
  protected double distanceToCircle(Shape s) { … }
  protected double distanceToSquare(Shape s) { … }
}

puzzle: opa

type shape = {circle, center: Point, radius: float}
          or {square, ul: Point, dr: Point}

function distance(shape s1, shape s2) {
  match (s1, s2) {
    case {s1={circle ...}, s2={square ...}}
    case {s1={square ...}, s2={circle ...}}: …
    case {s1={circle ...}, s2={circle ...}}: …
    case {s1={square ...}, s2={square ...}}: …
  }
}

 

Angularjs in a nutshell


HTML re-imagined


HTML  ⇔ JavaScript


  • No boilerplate
  • Structure
  • Testability

No boilerplate

data-binding

     JS:

$scope.name = 'Bob';

     HTML:

<!-- 1-way data-binding -->
Hello, {{name}}
<!-- 2-way data-binding -->
Your name: <input ng-model="name" />

http://jsfiddle.net/koper/uMYbH/                 

       ✔ Changes model ↔ view synchronized automatically.

2-way data binding

(with AngularJS)

3-way data binding

(with Firebase & AngularFire)

declarative markup

     JS:

function ColorsCtrl($scope) {
  $scope.colors = ['blue', 'red', 'green'];
}

     HTML:

<table ng-controller="ColorsCtrl">
    <tr><th>Color</th></tr>
    <tr ng-repeat="color in colors">
        <td ng-bind="color"></td>
    </tr>
</table>

http://jsfiddle.net/koper/Atf7U/                 

       ✔ No DOM manipulations!

declarative markup (ctd.)

JS:

function ColorsCtrl($scope) {
    $scope.colors = ['blue', 'red', 'green'];
    $scope.mkBackgroundColor = function(color) {
        return {'background-color': color};
    }
    $scope.duplicate = function(color) {
        $scope.colors.push(color);
    }
}

HTML:

<table ng-controller="ColorsCtrl">
    <tr><th>Color</th><th>Name</th></tr>
    <tr ng-repeat="color in colors">
        <td ng-style="mkBackgroundColor(color)"
            ng-click="duplicate(color)">
        </td>
        <td ng-bind="color"></td>
    </tr>
</table>

http://jsfiddle.net/koper/t4g87/

Structure

MVC

MODEL:

[ { name: 'Apple Inc', price: 525.63 },
  { name: 'Facebook Inc', price: 49.42 },
  { name: 'Google Inc', price: 1024.95 } ];

VIEW:

<button ng-click="randomize()">Randomize</button>
<table>
    <tr><th>Name</th><th>Price</th></tr>
    <tr ng-repeat="company in companies">
        <td ng-bind="company.name"></td>
        <td ng-bind="company.price | currency"></td>
    </tr>
</table>

CONTROLLER:

function StocksCtrl($scope) {
    $scope.companies = StocksFetcher.get();
    $scope.randomize = function() { ... }
}

http://jsfiddle.net/koper/YtDgh/

reusable components

     JS:

app.module('components', [])
    .directive('tabs', function() {
        // ...
    })
    .directive('pane', function() {
        // ...
    });

     HTML:

<tabs ng-app="components">
    <pane title="Tab 1">Engaging content</pane>
    <pane title="Tab 2">Even more engaging content</pane>
</tabs>

http://jsfiddle.net/koper/WQPHz/                 

       ✔ Domain-specific markup

Testability

dependency injection

     JS:

function FooService() { … }
module.service('Foo', FooService);
function BarController($window, $http, Foo) { … }

       ✔ Explicit dependencies  ➦  easy mocks

       Also:

       ✔ Models as POJOs  ➦  easy logic testing

       ✔ DOM restricted to directives  ➦  most tests DOM-free

there's more...

  • Promises
  • Services
  • Animations
  • Filters
  • Forms
  • Routes
  • Restfullness
  • i18n & L10n

resources


           

     

       http://opalang.org                                           http://angularjs.org

       












Special thanks for slides' ideas to:
Wes Alvaro, Brad Green, Anant Narayanan & Cedric Soulas 

Opa & AngularJS

By Adam Koprowski

Opa & AngularJS

A short introduction to two modern approach to developing web-apps: Opa and AngularJS.

  • 4,998