code-small.jpg

Cake Team Blogs

ReactiveMongo, Play & AngularJS

Posted by alex lashford

13/03/14 18:54

This application shows how to build a 'modern' web application, comprising a Client-side JavaScript app built using AngularJS written in CoffeeScript, served from the Play 2 Framework and using document persistence with Reactive Mongo a non-blocking Scala client for MongoDB.

Sounds like a cool stack, well I think so. In this tutorial I'm going to cover how to structure an AngularJS app, expose a REST API in Play and read/write JSON documents in MongoDB. By the end we will have created a simple user management app, which gives a thin slice of entity management, collecting data in the AngularJS app and persisting in MongoDB.

So let's get down to it by seeing the overall structure of the application we are building.

Overall structure

So now we know the components, let's see how they all fit together...

MongoDB integration

I opted to use the ReactiveMongo driver to give me scalability with an asynchronous, non-blocking interface to MongoDB. The guys over at Play-ReactiveMongo have made this integration even easier with a Play 2 plugin.

Adding the plugin to an app is simples...

First, add the dependency - see Build.scala

libraryDependencies ++= Seq(
   "org.reactivemongo" %% "play2-reactivemongo" % "0.10.2"
)

Once that's done, configure MongoDB in the app config - see application.conf

mongodb.uri = "mongodb://localhost:27017/modern-web-template"

Moving on, add the following to your conf/play.plugins

400:play.modules.reactivemongo.ReactiveMongoPlugin

Now that you have the RactiveMongoPlugin added, you can mix-in the MongoController trait to your controllers. In our app, it is just the Users:

class Users extends Controller with MongoController

The MongoController trait provides convenient functions exposed by the ReactiveMongoPlugin; for example providing us with nice handling of the JSON objects and a friendly wrapper to MongoDB.

With all that, we can implement the createUser function to write the User JSON object to MongoDB, notice the conversion from raw JSON to the User object.

def createUser = Action.async(parse.json) {
   request =>
     request.body.validate[User].map {
       user =>
         // `user` is an instance of the case class `models.User`
         collection.insert(user).map {
           lastError =>
             logger.debug(s"Successfully inserted with LastError: $lastError")
             Created(s"User Created")
         }
     }.getOrElse(Future.successful(BadRequest("invalid json")))
 }

Now that we can create users, we should really implement the findUsers function. It unsurprisingly creates a MongoDB query and return a list of Users as JSON.

def findUsers = Action.async {
    // let's do our query
    val cursor: Cursor[User] = collection.
      // find all
      find(Json.obj("active" -> true)).
      // sort them by creation date
      sort(Json.obj("created" -> -1)).
      // perform the query and get a cursor of JsObject
      cursor[User]
    // gather all the JsObjects in a list
    val futureUsersList: Future[List[User]] = cursor.collect[List]()
    // transform the list into a JsArray
    val futurePersonsJsonArray: Future[JsArray] = futureUsersList.map(Json.arr)
    // everything's ok! Let's reply with the array
    futurePersonsJsonArray.map {
      users =>
        Ok(users(0))
    }
}

Head over to the Users Controller to see all the other details.

And you're done. At this point you have the ability to write and read JSON documents to MongoDB from the controller. Although a controller on its own is pretty useless without a wiring in the routes.

Play REST API

Let's expose these methods as REST endpoints in Play by adding the following lines to conf/routes

GET     /users                      @controllers.Users.findUsers
POST    /user                       @controllers.Users.createUser

At this point you will be able to execute play run, which will start a HTTP server running on port 9000. This will expose the endpoints for creating and listing users.

Using your favourite REST client, you can now test the endpoints by posting some JSON. If you're using PostMan, I have shared the JSON collection here.

Create a user

POST -> http://localhost:9000/user
HEADERS: Content-Type: application/json
BODY:
{ "age": 44,
 "firstName": "jan",
 "lastName": "joe",
 "active": true}

List the users

GET  -> http://localhost:9000/users
HEADERS: Content-Type: application/json

This gives us the back-end to our application, now let's create a UI to consume this API.

AngularJS App

AngularJS is pretty awesome but is a bit of a mind shift from a traditional web application. After playing with this Activator, I suggest doing some reading, the docs and learning material are pretty extensive and the community is very active. I'll run you through the key points of how the code hangs together in CoffeeScript. Let's start by taking a look at app.coffee

dependencies = [
    'ngRoute',
    'ui.bootstrap',
    'myApp.filters',
    'myApp.services',
    'myApp.controllers',
    'myApp.directives',
    'myApp.common',
    'myApp.routeConfig'
]
app = angular.module('myApp', dependencies)
angular.module('myApp.routeConfig', ['ngRoute'])
    .config ($routeProvider) ->
        $routeProvider
            .when('/', {
                templateUrl: '/assets/partials/view.html'
            })
            .when('/users/create', {
                templateUrl: '/assets/partials/create.html'
            })
            .otherwise({redirectTo: '/'})

@commonModule = angular.module('myApp.common', [])
@controllersModule = angular.module('myApp.controllers', [])
@servicesModule = angular.module('myApp.services', [])
@modelsModule = angular.module('myApp.models', [])
@directivesModule = angular.module('myApp.directives', [])
@filtersModule = angular.module('myApp.filters', [])

This is where we are plugging the different modules together: notice at the bottom of the file we are creating globally-scoped variables which give us access to the appropriate modules anywhere in the app. This allows us to register, for example, a controller. Viz UserCtrl.coffee.

class UserCtrl
	constructor: (@$log, @UserService) ->
    	@$log.debug "constructing UserController"
    	@users = []
	...
controllersModule.controller('UserCtrl', UserCtrl)

I use the same approach to register a service UserService.coffee:

class UserService
    constructor: (@$log, @$http, @$q) ->
        @$log.debug "constructing UserService"
...
servicesModule.service('UserService', UserService)

Defining these classes at global scope allows AngularJS to do its magic with dependency injection. From the UserCtrl, I want access to the UserService so I can call the Play REST API. By declaring the UserService as a constructor dependency, AngularJS will look for the thing registered using the name 'UserService' to inject when constructing the controller. This gives us the benefits of DI with the ability to test and mock out services, testing components in isolation.

Note that the name of the service DOES matter here as the name in quotes will be used when looking up the reference in AngularJS.

Serving AngularJS from a single page.

So now we have the AngularJS app created, we need to serve the index page from Play, which will provide the full AngularJS framework to the client browser.

Create an index.scala.html template page where you can define the AngularJS directives and include the required JavaScript libraries.

<!doctype html>
<html lang="en" ng-app="myApp">

<body>
	<div ng-view></div>
</body>

<script src='//ajax.googleapis.com/ajax/libs/angularjs/1.2.13/angular.js' type="text/javascript"></script>
<script src='//ajax.googleapis.com/ajax/libs/angularjs/1.2.13/angular-route.js' type="text/javascript"></script>
<script src='@routes.Assets.at("javascripts/vendor/ui-bootstrap-tpls-0.10.0.js")' type="text/javascript"></script>
<script src='@routes.Assets.at("javascripts/main.js")' type="text/javascript"></script>

<script src='@routes.Assets.at("javascripts/app.js")' type="text/javascript"></script>
<script src='@routes.Assets.at("javascripts/common/Config.js")' type="text/javascript"></script>
<script src='@routes.Assets.at("javascripts/directives/AppVersion.js")' type="text/javascript"></script>

<script src='@routes.Assets.at("javascripts/users/UserService.js")' type="text/javascript"></script>
<script src='@routes.Assets.at("javascripts/users/UserCtrl.js")' type="text/javascript"></script>
<script src='@routes.Assets.at("javascripts/users/CreateUserCtrl.js")' type="text/javascript"></script>

</html>

Notice here we have added the ng-app directive to the html tag. This binds the AngularJS app to this page and when loaded it will construct the app for us. Each of the CoffeeScript files are compiled into individual JavaScript files that need adding as resources to the single page.

Now we can add a routes entry and a controller to serve this single page, Routes Controller

   GET     /       @controllers.Application.index
class Application extends Controller {
   def index = Action {
      logger.info("Serving index page...")
      Ok(views.html.index())
   }
}

And there we have the application all wired together

Screenshots

Bellow are screenshots of the activator running, showing the Create User form and the List Users pages.

Create User

User Creation

List Users

View Users

Developement Setup

To run the activator locally you will need to setup a few things. Please see the readme for prerequisites Instructions

Summary

So why the tech choices?

AngularJS CoffeeScript

AngularJs is a client side MVC style framework written in Javascript. The framework adapts and extends traditional HTML to better serve dynamic content through two-way data-binding that allows for the automatic synchronisation of models and views. As a result, AngularJS de-emphasises DOM manipulation and improves testability.

Traditionally AngularJS applications are written in JavaScript, my main objection to JavaScript is its clunky Java-esque syntax and its darn demand for all those braces. So in steps CoffeeScript.

CoffeeScript is a little language that compiles into JavaScript. Underneath that awkward Java-esque patina, JavaScript has always had a gorgeous heart. CoffeeScript is an attempt to expose the good parts of JavaScript in a simple way.

Read more about CoffeeScript AngularJS

BootstrapUI

Well to be honest any CSS framework would do here, I quite like Bootstrap, its well supported and means I don't need to hand crank CSS. Because well at the end of the day I'm not a web designer, I like Arial font, primary colours and simple layouts.

For more reading check out

AngularJS Bootstrap Or Zorb Foundation

MongoDb

MongoDB (from "humongous") is an open-source document database, and the leading NoSQL database. Its schema-free design makes it highly scalable, cost effective and more. MongoDB enables profound developer agility through its flexible data model.

MongoDb

Play 2 Framework & Scala

Play is a high-productivity web framework with a great Scala API, making it easy to create modern, reactive web applications

Play 2 Framework

Try it out

Download the Acivator or clone the project and execute the following in a terminal from inside the project

> play run

Play will compile the CoffeeScript files and launch a web server that listens on localhost:9000. Using play run is wrapping SBT and so gives us "hot compile" of the code for seamless web development. Any changes you make to your source code are instantly reflected in the browser.

So what are you waiting for, download this activator and check out the code

The code is available to download or fork here, please feel free to raise issues and submit enhancements via pull requests.

That's all folks, enjoy...

Alex Lashford

 

Topics: AngularJS, mongodb, Play

Posts by Topic

see all

Subscribe to Email Updates