Reactive All The Way Down – Angular & Bootstrap UI



Reactive All The Way Down – Angular & Bootstrap UI

1 1


reactive-all-the-way-down


On Github jamesward / reactive-all-the-way-down

Reactive All The Way Down

James Ward ~ @_JamesWard

Blocking Request

def foo = Action {
  Ok("foo")
}

Async Request

def foo = Action.async {
    Future.successful(Ok("foo"))
}

Reactive Request (Async + Non-Blocking)

def pause(duration: Int) = Action.async {
  Promise.timeout(Ok(duration.toString), duration seconds)
}

Reactive WS Client

val f: Future[Response] = WS.url("http://www.foo.com").get

Reactive Composition

def foo = Action.async {
  val futureResponse = WS.url("http://www.foo.com").get
  futureResponse.map { response =>
    Ok(response.body)
  }
}

Reactive Composition

def foo = Action.async {
  val futureJW = WS.url("http://www.jamesward.com").get
  val futureTwitter = WS.url("http://www.twitter.com").get
  for {
    jw <- futureJW
    twitter <- futureTwitter
  } yield Ok(jw.response.body + twitter.response.body)
}

Reactive JSON Services

def tweets(q: String) = Action.async {
    val futureResponse = WS.url(s"http://twitter-search-proxy.herokuapp.com/search/tweets?q=$q").get()
    futureResponse.map { response =>
        Ok(JsArray((response.json \\ "text").distinct))
    }
}

Angular & Bootstrap UI

"org.webjars" %% "webjars-play" % "2.3-M1",
"org.webjars" % "bootstrap" % "3.1.1",
"org.webjars" % "angularjs" % "1.2.16"
<link rel="stylesheet" href="@routes.WebJarAssets.at(WebJarAssets.locate("bootstrap.css"))">
<script src="@routes.WebJarAssets.at(WebJarAssets.locate("angular.min.js"))"></script>
<script src="@routes.Assets.at("main.js")"></script>

Angular & Bootstrap UI

<nav class="navbar navbar-default navbar-static-top" role="navigation">
    <div class="container-fluid">
        <a class="navbar-brand" href="#">Go Reactive</a>
    </div>
</nav>
<div class="container">
    <div ng-controller="Hello">
        <form ng-submit="sendName()">
            <label>Name:</label>
            <input type="text" ng-model="yourName">
            <input class="btn-primary" type="submit" value="Send">
        </form>
        <ul><li ng-repeat="tweet in tweets">{{tweet}}</li></ul>
    </div>
</div>

Angular & Bootstrap UI

angular.module('myApp', []).
    controller('Hello', function($scope, $http, $timeout) {

        $scope.msg = "";

        $scope.sendName = function() {
            $http({method: 'GET', url: '/tweets', params: {q: $scope.yourName}}).
                success(function(data) {
                    $scope.tweets = data;
                });
        };

    });

Request Handling with Actors

Controller:

def tweets(q: String) = Action.async {
    val actorRef = Akka.system.actorOf(Props[TwitterActor])
    (actorRef ? q).map {
        case j: JsArray => Ok(j)
    }
}

Actor:

def receive = {
    case s: String =>
        val futureResponse = WS.url(s"http://twitter-search-proxy.herokuapp.com/search/tweets?q=$s").get()
        futureResponse.map { response =>
            JsArray((response.json \\ "text").distinct)
        } pipeTo sender
}

Resilient Futures

future.recover {
    case Exception =>
        Logger.error("Failed!")
        InternalServerError("Boum!")
}

2-Way Reactive with WebSockets

def echoWs = WebSocket.using[String] { request =>
  val (enumerator, channel) = Concurrent.broadcast[String]
  val in = Iteratee.foreach[String](channel.push)
  (in, enumerator)
}

Angular with WebSockets

Template:

<h2>{{msg}}</h2>

Controller:

$scope.socket = new WebSocket("ws://localhost:9000/echo-ws");
$scope.socket.onmessage = function(msg) {
    $timeout(function() { $scope.msg = "hello, " + msg.data; });
};

$scope.socket.send($scope.yourName);

WebSocket Handling with Actors

Controller:

def echoWs = WebSocket.acceptWithActor[String, String] { request => out =>
    Props(classOf[EchoActor], out)
}

Actor:

class EchoActor(out: ActorRef) extends Actor {

    def receive: Receive = {
        case s: String => out ! s
    }

}

Further Learning

Activator Templates

Other