I would like to talk about my experience in AngularJS. Currently, there are proposals underway to create the next major version of AngularJS and the proposals therein are aimed at improving the overall AngularJS experience. I'm putting this out there as my experience with the things I think are not addressed in those proposals. This will be a hodgepodge of things in Angular that I care about so I apologize for the jumpiness and non-cohesiveness of this post!

Testing

The testing story in Angular is pretty good but it can get better. Both the dependency injection framework and karma runner are a huge improvement from what we've used in the past for javascript testing. These tools really make my life easier as an application developer.

The parts of the testing story that are weak in the angular world come in two parts: views testing and ease of testing.

View Testing

It has been established in the angular community that the correct way to test views is to use a selenium web driver-like interface. The logic being that there is not much value or ability to unit test views.

These tests are written in angular-scenario (and now protractor) and execute the full stack of an angular application. You can inject some ngMock stubs into the backend to prevent server communication but otherwise you're executing the whole thing: routes, template compiling, view binding, controller setup and actions, service calls, scope rebinding, etc. These tests certainly have value as a high level 'everything works, right?' acceptance tests, but they should represent a small number of your overall test suite.

However, some developers are finding this inadequate and something that leads to either insufficient code coverage (because the templates are code) or a large number of acceptance tests. Neither are desirable.

So, some of my coworkers set out to solve the problem with a library called duck-angular. This library sets up a mini-container to execute template compilation, view binding, scope rebinding and controller setup. It isn't complete isolation but its a good step forward in the ability to test the views in a karma runner instead of a web driver runner.

This allows developers to write unit tests for their views. Which means that views become regression tested, that they start to become better designed and that we start to make obvious the impact of poor design decisions.

Ease of Testing

The other factor is the ease of testing. Both karma and the dependency injection framework are awesome. They allow me to work faster and with more precision than before. One thing that has come up in my experience with angular, though, is that the testing is difficult to grasp.

For example, to test a controller I'm usually doing a $injector.get('MyCtrl', { myService : stubbedMyService }). This makes sense to me. I'm injecting my dependencies and managing them in that way.

To test a service, I am usually doing a inject(function(myService, httpBackend) { httpBackend.whenGET(...) }). Again, this make sense to me.

However, the cognitive disconnect between why I do one thing in the controller tests and another thing in the service tests make it more difficult for developers to understand testing angular. Further disparities occur with directives and plain javascript objects.

In fact, testing directives is really difficult. There doesn't seem to be an 'angular way' to test these directives and getting your head around how to do it can be pretty mind warping. I feel like the umpteenth ways of creating a directive have something to do with this. It is great to have so many ways of manipulating the DOM and the $scope, but I'm not sure how to test all those variations.

So, what I'd like to see is some consolidation and consistency in testing different aspects of the angular ecosystem. As it stands, it is learning 4 different ways (5 ways, if you add in view testing) of how to test code in the same application. It can become tedious to learn and a huge obstacle to doing the right things. I think there is already a hidden acknowledgement in this in the goals for Angular 2. One of the goals is to make angular easier to understand and pick up. I think testing is one of those areas that contributes significantly to the disconnect people feel in working with Angular applications.

Scope and the Scope Lifecycle

Two way data-binding is awesome and provides a lot of power. I believe there are some really good proposals in the Angular 2 stack already so I won't go too much into the details there. What I am going to talk about is $scope, $q and alternative communication protocols in the Angular stack.

Promises

Angular made the right choice to put promises right into the middle of the framework. Promises reduce callback hell and deliver on a more pleasant development experience. However, angular forked q into $q.

q is Kris Kowal's promise library. It's a forward thinking library that executes on the promises API splendidly. I'm really happy with this choice.

$q is Angular's fork of that library. Why was it forked? Because it needed $scope to be managed inside of it.

This means that $q needs to live inside of an angular application. q used outside of an angular application--like in a dependency or just a plain javascript module, does not have the expected effects to the angular application. For me, this means that I cannot use q unless I'm in angular world. And while angular is great, it isn't where I want to be all of the time.

Angular encourages people to write models in pure javascript. This is great advice. Getting the part that doesn't have anything to do with angular out of angular is a great step towards maintainable and agile codebases. But as soon as a model requires the usage of a promise library, it needs to be sucked back into angular (or have the dependency injected in an odd way.. like through a function parameter). Getting the life cycle out of q and just using the plain q library would be a big step into a less intrusive AngularJS.

Alternative Communication Protocols

One interesting thing that we are doing in our project is communicating via alternative protocols. We're using hardware APIs and WebSockets to talk to peripherals; sending and receiving data. We're also receiving events from those peripherals that stream it at any given point.

This is a relatively minor point, but there's no standard way to put these other protocols into angular land. What we currently do is have a controller register a callback or receive data directly from those and wrap it in a $scope.$apply(). This works okay for us but starts to create some problems when we start injecting different dependencies for those externals (mostly for the purpose of testing). If we replace a asynchronous callback API with a synchronous implementation, we will get $scope already in progress errors. It boils down to the fact that the controller really shouldn't be managing the scope lifecycle.

So, to fix this, we would probably need a way to register our "PeripheralService" with a "WebSocket" or "PhoneGap" type of protocol like we can with $http. So, opening up a "protocol" API that allows for us to define scope management there instead would be ideal.

Conclusion

Of course, these are just my experience and my hopes for a better AngularJS. I could be wrong with some of the details (probably since I haven't had this post reviewed) but I hope that some of this is useful! Let me know if you have any questions. You can email me if you have any other questions.