Wednesday, March 11, 2015

DrEdit for Google Drive and Learning AngularJS

Since we released version 2 of the Google Drive SDK at Google I/O, we’ve been quietly updating the DrEdit sample application to use the new API. As part of the update, the UI for DrEdit has been rewritten to use AngularJS, a modern web application toolset developed by Google and used in apps at DoubleClick. You might be wondering -- why go through the trouble of rewriting the UI for a basic sample app just to show off some new API features? Turns out it was more of a happy coincidence, but a valuable one and great learning experience!

Practice what you preach

I had the pleasure of co-presenting a session on building great apps for Google Drive, and a big focus of the talk was on all the little things that go into making an app intuitive and user-friendly. This is particularly important for Google Drive, where many users are already familiar with the built-in apps like Docs, Presentations, and Spreadsheets.

The first version of DrEdit was a good demo app, but didn’t follow all of our recommendations. I didn’t want to tell developers all the things they should be doing without having tried them myself. I decided to write a separate sample for the talk and needed a solid base to build on. It was the perfect opportunity to learn a new tool!

HTML & Javascript, only smarter

Angular doesn’t aim to abstract away HTML, Javascript & CSS. Rather, it enhances HTML to make building dynamic apps easier. One benefit, besides a nice short learning curve, is the positive interaction with other tools. To give the app some structure, I used Bootstrap. For example, the HTML for displaying the authenticated user’s info and a small dropdown to link to their profile in the navigation bar only required a few minor changes from typical Bootstrap usage (shown in bold) to wire up to a controller.


<ul class="nav pull-right" ng-controller="UserCtrl">
<li class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
{{user.email}}
</a>
<ul class="dropdown-menu">
<li><a href="{{user.link}}" target="_blank">Profile</a></li>
</ul>
</li>
</ul>

Even models are plain javascript objects. Anything reachable through a scope (the binding between a view and controller) is considered part of the model. These can be primitives, hashes, or objects. No need to extend a base class or access properties through special properties. Rather than use change listeners that require special instrumentation, Angular uses dirty checking to detect model changes and update views.

The one catch with this approach is it requires any changes to the model to be made inside the scope of a scope.$apply(fn) call. In most cases, this is done automatically. When working with external libraries or raw XMLHttpRequests that can fire asynchronous callbacks, calling $apply yourself is necessary to make sure mutations are tracked correctly.

Speaking of asynchronous tasks…

Promises, Promises

No, I’m not talking about the hit song by 80’s band Naked Eyes, rather Angular’s $q service based on one of the proposed CommonJS Promises APIs. If you’re already familiar with JQuery’s deferred object or any of the other related implementations, this is familiar territory. If not, time to learn. Working with deferred objects can be a lot easier than the traditional callback approach. You can compose async tasks either serially or in parallel, chain callbacks, and return deferred objects from functions like normal results.

Where this mostly comes into play is Angular’s $http service. If you’ve used jQuery, you’ll find it similar to jQuery.ajax() & the jqXHR result. It is based on the deferred/promises API and also ensures callbacks are executed correctly inside $apply for safe & efficient model mutations. This combination makes it easy to work with remote services in Angular.

Room for improvement

Trying to learn some new frameworks while preparing for Google I/O and helping developers to launch apps on our updated API all within a few weeks was a lot to take on. A few corners were cut and there are a few things I’d like to revisit when time permits:

  • Tests! AngularJS boasts testability as one of its key features and leverages dependency injection throughout to help keep things simple and testable. Since this was originally intended as a live demo instead of a reference app, I cut a few corners here. Yeah, I know better than that...
  • Rethink how ACE is used. In most cases it’s easy to figure out if something should be a controller, directive, filter, or service. But trying to pigeonhole libraries like ACE into one of those is daunting. Out of expediency, I chose to hide ACE behind a service, but it feels like it belongs in a directive. It would be nice to be able to declare in HTML:
    <editor content=”myModel.text”/>
    and have that sync with the model just like any other input in Angular. I started down that route, but correctly wiring up ACE to do that was more effort than it seemed worth at the time. This resulted in some other warts with how the app’s routes & views are structured.
  • Talking to the backend servers. Not so much an issue with Angular, but rather with a late decision to replace the DrEdit UI. The original goal was a separate app. Once we decided to build on the previous sample, I didn’t want to make unnecessary changes to the server side code that was already written. This led to implementing some of the new features in less than ideal ways. For example, the editor can not independently save metadata from the document content when the file is renamed or starred but the content left untouched. A minor inefficiency, but something that could have been done better.

I know I’ve only scratched the surface and have a lot more to learn. Even so, it was incredibly fun diving head first into AngularJS, and I highly recommend considering it if you’re dissatisfied with your current framework or just want to learn something new!



Steven Bazyl   profile | twitter | events

Steve is a Developer Advocate for Google Drive, Google Apps, and the Google Apps Marketplace. He enjoys helping developers find ways to integrate their apps and bring added value to users.

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.