February 04, 2015
Adam Kelso

Enola Labs creates custom strategy and products for mobile and web.

Let’s Talk

Subscribe to our blog:

This is the first of a two part series describing our solution to building Angular apps with a Parse.com backend.

When building single-page apps, it can sometimes be a challenge to work with third-party libraries or SDK’s. One such example we ran into at Enola was building a single-page app that used Parse as a backend. Parse makes several SDKs available for several environments to interact with their database in the cloud; including a JavaScript library. For most cases the SDK provided works perfectly fine and is a real convenience. That being said, Parse’s SDK is based on Backbone, and thus follows several conventions specific to the framework. If you’re planning on building a Backbone app, it’s fantastic. However, we were specifically wanting to integrate Parse with an internal CMS here at Enola that is built on Angular.

TL;DR - Look here to see the final code and examples.

Diagnosing The Challenge

There were four specific challenges we were facing the integrating the Backbone SDK and Angular.

  1. The CMS should not be strongly tied to Parse, so that we could switch to a different backend if needed.
  2. Integrating the Angular digest cycle with parse.js promises
  3. Translating Parse’s getters and setters to Angular’s POJO methodology
  4. Creating customizable hooks for injecting code around database CRUD actions (e.g. beforeSave, soft deleting, etc.)

Facing the Challenges

1. Keeping the Parse specific code separate.

The problem of strongly binding code to a library, no matter the language or framework, is that when something changes down the road a huge amount of the codebase must be rewritten. Unit testing strongly binded code also becomes an issue because the dependency becomes difficult to mock. One particularly good pattern for handling loosely binded code is the Repository Pattern. This methodology basically says to wrap any code that is not strongly binded with a standardized API and inject the implementation.

If you’re familiar with languages that use Interfaces, this is exactly the reason interfaces exist. They provide a standardized API contract for an unknown number of implementations. Interfaces don’t exist in JavaScript, but that doesn’t mean the same method can’t be followed. It just doesn’t make a guarantee that would throw a compiler error if the standard is not followed.

We started out defining what we wanted our API to look like. First off, because we were building a wrapper around the data store it made sense to keep the code in an Angular Service. Services can be injected into any controller, directive, or other service in the Angular framework. Second, it made sense to treat each Parse “class” (think database table) as a separate repository/service. That would allow us to vastly simplify the API. Also, because services in Angular are singletons it would help guard us against an overuse of repository objects. Lastly, we wanted the based CRUD functionality available out-of-the-box on each of these repositories plus a couple others, like “count”.

Function Arguments Returns Promise? Promise Resolves To Does
all() none yes array Grab all entries
create() none no new class object creates a new entity conformed to Parse’s SDK
get(id) entity id yes the requested object fetch an individual entity
save(obj) entity yes null save the provided entity
delete(obj) entity yes null delete the provided entity
count() none yes integer count all entities in the store (up to the limit Parse allows)

With these requests in mind, the following is what we first came up with.

 'use strict'; angular.module('cms') .factory('parseInit', [function() { // Required to use the SDK. Parse.initialize(applicationKey, javascriptKey); // Methods return { CreateRepository: function(className) { var objectClass = Parse.Object.extend(className, {}, { all: function() { var query = new Parse.Query(objectClass); query.limit(1000); return query.find({ success: function(results){ return results; }, error: function(e) { return e; } }); }, count: function() { var query = new Parse.Query(objectClass); query.ascending('objectId'); return query.count({ success: function(result) { return result; }, error: function(e) { return e; } }); }, get: function(id) { var query = new Parse.Query(objectClass); return query.get(id, { success: function(result) { return result; }, error: function(e) { return e; } }); }, save: function(obj) { return obj.save(null, { success: function(result) { return result; }, error: function(object, e) { return e; } }); }, delete: function(obj) { return obj.destroy(null, { success: function(result) { return; }, error: function(e) { return e; } }); }, create: function() { return new objectClass(); } }); return objectClass; }}; }]); 

The code above shows the basic functions we wanted to be available for each repository by default, and the following is an example of a repository using the Parse initializer.

 angular.module('cms') .factory('someService', ['parseInit', function(parseInit){ var Class = parseInit.CreateRepository('ClassName'); // Optionally add or delete methods as necessary. delete Class.count; Class.aNewMethod = function() { // code here }; return Class; }]); 

2. Integrating the Angular digest cycle with parse promises

JavaScript promises are a wonderful tool that are still fairly new. Because it’s not native to all browsers yet, most js frameworks have to create a custom integration that keeps track of promises and any events they create. This also means that the promises created by Parse’s SDK do not fire the Angular digest cycle when they resolve. The following is an example controller implementation of the repository we just created. The one major problem? The $scope array is never rendered to the view.

 angular.module('cms') .controller('DashboardCtrl', ['$scope', 'someService', function($scope, some){ $scope.somes = []; // Initialization some.all().then( function(result) { $scope.somes = result; }, function(error) { // do something on error } ) }]); 

You can always manually fire off the digest cycle, but that’s truthfully not good code for a couple reasons. One, the Angular documentation nudges developers to prefer the framework’s built-in methods for digestion. Two, and more importantly, calling digest forces the controller to not have a separation of concerns. The controller should know nothing about how the service it calls runs. Otherwise, the controller is no longer portable and modular.

The solution is then to wrap all calls to Parse with a promise native to Angular. Internally angular uses a library called $q to create and interact with promises. Using this library, we then added a few lines to each repository method. The following shows a couple of the methods in the parseInit service wrapped with these new promises.

 angular.module('cms') .factory('parseInit', ['$q', function($q){ // ... See first example for full code get: function(id) { var defer = $q.defer(); // Create a deferring object var query = new Parse.Query(objectClass); query.get(id, { success: function(result) { defer.resolve(result); }, error: function(error) { defer.reject(error); } }); return defer.promise; // Create an Angular promise to be resolved }, save: function(obj) { var defer = $q.defer(); obj.save(null, { success: function (result) { defer.resolve(result); }, error: function (object, error) { defer.reject(error); } }); return defer.promise; } }]); 

Now, whenever the promise returned to the controller resolves, the digest cycle is properly started and the view properly renders.

Read Part Two

This blog is brought to you by the software development company, Enola Labs Software.