Typesafe Activator

AngularJS Play Mongo

AngularJS Play Mongo

lashford
Source
March 13, 2014
angularjs coffeescript play guice mongodb reactive-mongo bootstrapui rest

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

How to get "AngularJS Play Mongo" on your computer

There are several ways to get this template.

Option 1: Choose modern-web-template in the Typesafe Activator UI.

Already have Typesafe Activator (get it here)? Launch the UI then search for modern-web-template in the list of templates.

Option 2: Download the modern-web-template project as a zip archive

If you haven't installed Activator, you can get the code by downloading the template bundle for modern-web-template.

  1. Download the Template Bundle for "AngularJS Play Mongo"
  2. Extract the downloaded zip file to your system
  3. The bundle includes a small bootstrap script that can start Activator. To start Typesafe Activator's UI:

    In your File Explorer, navigate into the directory that the template was extracted to, right-click on the file named "activator.bat", then select "Open", and if prompted with a warning, click to continue:

    Or from a command line:

     C:\Users\typesafe\modern-web-template> activator ui 
    This will start Typesafe Activator and open this template in your browser.

Option 3: Create a modern-web-template project from the command line

If you have Typesafe Activator, use its command line mode to create a new project from this template. Type activator new PROJECTNAME modern-web-template on the command line.

Option 4: View the template source

The creator of this template maintains it at https://github.com/lashford/modern-web-template#master.

Option 5: Preview the tutorial below

We've included the text of this template's tutorial below, but it may work better if you view it inside Activator on your computer. Activator tutorials are often designed to be interactive.

Preview the tutorial

Modern-web-applictation

This application shows how to build a 'modern' web application, comprising of a Client-side JavaScript App built using AngularJS wrote 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 lets get down to it by seeing the overall structure of the application we are building.

So now we know the components, lets see how they all fit together...

MongoDB integration

I opted to use the Reactive Mongo 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 { users =>
               Json.arr(users)
            }
            // 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

Lets expose these methods as a REST endpoint in Play by adding the following line 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 would 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 your 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 lets 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 gives us access the appropriate modules anywhere in the app. This allows us to register for example a controller see 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 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

List Users

Developement Setup

To run the activator locally you will need to setup a few things, please see the readme for prerequisite 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 make 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 Activator or clone the project and execute the following in a terminal from inside the project

        > play run
        

and Play will compile the CoffeeScript and launch a webserver on localhost:9000, play run is wrapping SBT and so gives us "hot compile" of the code for seamless web development, so saved changes are instantly reflected on 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
comments powered by Disqus