Why We Switched from AngularJS to FlightJS
Haste Makes Waste
How do we (web developers) keep up with all the JS frameworks popping up monthly? That is a challenge indeed. The thing I try to remember is, rushing to adopt the latest thing can lead you and your team to a painful misery.
Sure, you should check out these frameworks. Read about them, code a little with them, run some tests with them. Just don’t go all-in with a framework for the sake of “being current”. Avoid heavy rewrites of your system(s) without knowing how key parts will perform under the new regime.
Angular is Super-Awesome
AngularJS is quite different than most of the MV*1 JS frameworks out there. Angular lets you, basically, extend HTML to your liking. This is a key concept for the framework. There is a very strong template engine built into Angular around this extension concept. Furthermore, directives provide developers with the power to extend HTML however they see fit. For me, the thing I really like about Angular is its two-way data binding.
Angular’s two-way data binding makes me smile. With other similar frameworks, you either get one-way data binding, or you get some twisted mass of wrappers in order to achieve mostly two-way data binding. Now, I’m fine with one-way binding, if it’s easy enough to get the data out when I need to. However, if you’re going to offer two-way data binding, it has to be seamless and unencumbered.
I loathe having to wrap my models in some specialized structure. It’s even worse when I have to use multiple types of wrappers depending on what type of model I’m using. The worst of all is being forced to not only wrap my main model with a certain wrapper object, but also having to then wrap some properties on that model further with other wrapper objects. Yeah, I get why it’s considered required in order to achieve the goal.
There are a ton of books on AngularJS. Even a top 10 seller on Amazon (at the time of this writing) for all JS development books is on AngularJS!
So, Why Abandon Angular?
If you are expecting to read that we “abandoned” AngularJS for FlightJS because Flight is better, well…go back and read the introduction of this article. I learned that lesson long ago. You really should have good reasons against the framework already in-place before deciding to leave it. I try to never make such decisions purely on how much I like some other framework over the current one. That mentality will have you hopping from rewrite to rewrite, and your projects will suffer.
We also didn’t abandon AngularJS. We’re watching it’s future and viability carefully though. We still have projects using it where a decision to switch has not been made at this time.
Like so many frameworks, AngularJS has some strict rules you have to follow (once you learn them) in order for everything to work together. It’s to be expected to some degree. However, when you find that your creativity or ability to generate behavior in your application suffers because of those rules, you have a problem.
With AngularJS, try getting field validation on a form that doesn’t trigger on most fields until the user moves focus from the corresponding input, but also doesn’t trigger on some fields until the form is submitted. I dare you! Can it be done? Sure. However, it is a horrible, ugly mess to get something like that working due to limitations imposed by the framework.
Let’s not argue over the viability of the above validation request. The scenario I’ve depicted is actually one with proven UX benefits, no matter how poorly I’ve described it here. That’s not the point anyway. What gives Angular the right to tell my team how to implement a good validation UX? None.
We also found creating custom directives of any heft was somewhat painful. Having to avoid DOM manipulation outside of directives (never tweak the DOM from a controller) added tons of extra ceremonial code just to fit that restriction.
Scope is cool, but it is so misused in a lot of “educational” material, it’s hard to tell how it should be used. Even the official learning documentation uses Scope frivolously. There are several articles about using Scope properly. It’s obviously not so obvious how you should use it.
We’re not the only ones spotting the flaws. Others are questioning if this is a fad framework. You should always question a framework and its direction. If it changes for the worse, stick with a version you still trust, or look to get out. Have you heard about AngularJS 2.0 and the direction shift it’s taking? Don’t judge 2.0 just yet; wait for it to release first. However, be prepared for massive change.
The Angular Way
I’ve seen/read/heard so many times that you need to do this or that “the Angular way.” That’s half the problem though! There needs to be more flexibility. I’m not talking about how you need to use dependency injection with a framework, or that it relies on jQuery. What I’m talking about is how much of a pain it is to utilize other external libraries with a framework. Before AngularUI’s Bootstrap project hit the scene, it was very painful to use Bootstrap components with Angular; I’d say impossible, but that’s just lazy.
So, what if I’m a Foundation user instead? Again, it took some time, but some folks came up with directive projects to integrate Foundation with AngularJS. Still, it feels clunky to have to go to all that trouble just to integrate with other libraries. Pick another, lesser-known library and there likely isn’t something out to easily allow you to use it with Angular. What’s more, you’ll be putting in a lot of time writing your own integration.
You should control your framework, it should not control you.
How is Flight Different?
Communication is Important
So, if no component can reference another component, and each is a small, self-contained bit of behavior, how do they all communicate? Just because a component cannot directly hold a reference to other components, that doesn’t mean no coordination can be achieved between groups of components. I don’t have your cell phone number, but I’m communicating with you right now.
Events are the sole form of communication between components. When one of your components does something notable, it should publish an event about that accomplishment. Sometimes, events are just notifications that something was done. Other times, components include full details about what was done. Other components subscribe to the events they care about and respond how they see fit.
UI components are attached to a specific DOM node that they work against and possibly manipulate. This type of component subscribes to events within its node to respond to user interactions (i.e., click, change, keydown, etc.) and to other external events. These components also publish custom events that make sense to the system (i.e., ui-user-list-requested, ui-user-selected, etc.).
Data components are attached to the document node and don’t mess with elements in the DOM. Instead, they provide non-visual behavior (AJAX interactions, data sorting/filtering, etc.), then publish events about those tasks (i.e., data-user-list-retrieved, data-user-selection-saved, etc.).
How to Manage All Those Events
If you think about all the tiny little components you could end up creating in a given application (Twitter has hundreds in play on what seems like a simple front-end), you may wonder how it’s possible to keep track of all the events each publishes and subscribes to.
It can certainly be a tangled web of event names to manage, but remember; each component will likely only be concerned with a handful of events. That said, if you misspell an event when subscribing, even your tests can’t catch it if you think you spelled it correctly.
I use Trello to manage the components that need to be worked on. I also use shared documents (for my teams, they are on Google Drive) to catalog every event that is published by components on a page. I am also toying with the idea of writing a simple Node.js tool to parse a project’s components in search of event publishing and subscribing in order to generate a living document of event usage.
Mix It Up
Composability is another feature of Flight I find remarkable. This may seem counter-intuitive considering that components aren’t allowed to reference other components. However, there are other ways to achieve composition. Flight uses mixins to “include” shared behavior with your components.
Suppose you want to encapsulate a form in a component and make it modal when shown. You could write a dialog mixin. Your form component could then “mix in” the dialog mixin to extend its behavior with modal focus. Likewise, if several components you are authoring have many shared behaviors, even with slight variations, you can write a mixin to act as a “base type” for those components. You can even override just the bits you need to for any mixin your component uses via advice; a special type of mixin for just that purpose.
Wait, ‘X’ and ‘Y’ are Missing
There is no template engine in FlightJS. There is no data binding in FlightJS. These things are “missing” intentionally. Flight doesn’t impose those things upon you. That does mean you need to handle those things yourself or via other libraries. However, I have found it freeing.
Why should a framework tell me I have to always use client-side templating? What if server rendered views retrieved via AJAX are the most performant for my application? I should have the freedom to choose how I render information to the user on an individual basis.
In fact, in one of our websites (a hybrid with separate areas that act sort of like SPAs themselves), we discovered that for some views server-side rendering was much faster. Yet other views benefited most from client-side templating with raw JS object data bound to them. With Flight, we were able to choose what worked best for each situation.
There is very little information about FlightJS out there compared to more popular frameworks. In fact, I’ve struggled quite a bit to find much of anything. I did buy the book shown above; Getting Started with Twitter Flight. It’s written by a Twitter developer that works on TweetDeck, so you can count on accuracy and insider knowledge of the framework. The book has been invaluable for some of the finer points of Flight.
Another great way to learn is to also read the tests written for the framework.
Who are “We”?
I’ve been referring to this elusive “we” and “my team” throughout this article. I work on several teams, as lead on most (but not all). I can’t reveal all of those I’m discussing in this article outright. However, I can talk about one very small team.
My team at Wish on a List is currently in the process of moving from AngularJS to FlightJS. I plan on writing about that transition on this blog over time. We’re over half way done, so there is plenty to write about from our perspective.
1 MV* indicates an array of patterns that all share concepts of models and views. It’s the last parts of the patterns that differ; thus the *. Some of the many MV* patterns are: MVC, MVP, MVVM, MVPVM, etc.