My previous post showed how to set up a component’s controller for unit testing using the $componentController service.
We also learned that Angular doesn’t trigger the different lifecycle hooks by itself, meaning that you need to manually call $onInit, etc.
In this post I’ll show an example for testing a component that makes use of the $onChanges hook.
Take a look at this simple word-counting component’s $onChanges hook:
As you can see, the hook uses the changes object in order to know when the text has changes, detect the initial change, and also use the previousValue to calculate the word difference (yes, I know we could use the old words value, but this is just for the example).
Now, testing this component is relatively trivial.
We simply need to pass it some different values for text, trigger $onChanges, and that’s it.
But, the fact is that Angular, for some reason, doesn’t expose the changes object it uses internally.
That means that you’ll have to implement your own object that conforms to the changes object protocol–each changed property needs an isFirstChange method and 2 properties, currentValue and previousValue.
Since that’s a PITA, I’ve open sourced a tiny helper just for that, angular-stub-changes.
describe('wordCount component',function(){varctrl;beforeEach(function(){angular.mock.module(app);angular.mock.inject(function($componentController){ctrl=$componentController('wordCount',null,{text:'1 2 3'});});});it('counts words',function(){varchanges=newStubChanges().addInitialChange('text','1 2 3').build();ctrl.$onChanges(changes);expect(ctrl.words).toBe(3);});it('does not show difference when initializing',function(){varchanges=newStubChanges().addInitialChange('text','1 2 3').build();ctrl.$onChanges(changes);expect(ctrl.difference).toBe(0);});it('calculates difference when changing text',function(){varchanges=newStubChanges().addInitialChange('text','1 2 3').build();ctrl.$onChanges(changes);ctrl.text='1 2 3 4 5';changes=newStubChanges().addChange('text','1 2 3 4 5','1 2 3').build();ctrl.$onChanges(changes);expect(ctrl.difference).toBe(2);});});
Basically, note that we make sure to call $onChanges with a properly configured changes object.
Also, you still have to set the updated properties on your controller instance yourself, e.g. ctrl.text = '...'.
The creation of the stub changes object is, I hope, straightforward.
It’s a builder pattern, so you can keep on adding how many changes (.addChange(..).addChange(..)) as needed for your component
That’s about it, happy testing!
“Maintaining AngularJS feels like Cobol 🤷…”
You want to do AngularJS the right way.
Yet every blog post you see makes it look like your codebase is obsolete.
Components? Lifecycle hooks? Controllers are dead?
It would be great to work on a modern codebase again, but who has weeks for a rewrite?
Well, you can get your app back in shape, without pushing back all your deadlines!
Imagine, upgrading smoothly along your regular tasks, no longer deep in legacy.
Subscribe and get my free email course with steps for upgrading your AngularJS app
to the latest 1.6 safely and without a rewrite.
Success! You rock. You'll get some knowledge shipped to your inbox soon. Let's do this!
Angular 1.5+’s components are great, but testing them properly requires some changes to they way you were used to testing before.
Directives were always a bit clunky to test in Angular.
You would either have to deal with recreating their DOM elements, or exposing the controller outside of the directive in order to test it directly.
Since components are essentially wrappers around directives, you might expect the same dance.
But, along the introduction of components we also got the handy $componentController service.
This service enables testing a component’s controller even without exposing it, and it also provides a simple way to supply a controller with bindings in the test.
As you can see, $componentController receives the name of the component that we’d like to test and also the bindings it should be initialized with, and returns the instantiated controller.
You can now start testing the controller, by invoking its functions and asserting the different results:
1
expect(fooCtrl.getDisplayResult()).to.equal('baz value bar value')
Don’t forget $onInit
In case your component implements the $onInit lifecycle hook, which is very likely starting from Angular 1.6, you should make sure to explicitly call it in your tests.
Angular and $componentController does not take care of that for you, for different reasons.
This means that the test above should look like this:
123
varfooCtrl=$componentController('foo',null,{...});fooCtrl.$onInit();expect(fooCtrl.getDisplayResult()).to.equal('baz value bar value')
That’s it!
Handling $onChanges is even trickier in tests, make sure to subscribe below to get the next part about it.
“Maintaining AngularJS feels like Cobol 🤷…”
You want to do AngularJS the right way.
Yet every blog post you see makes it look like your codebase is obsolete.
Components? Lifecycle hooks? Controllers are dead?
It would be great to work on a modern codebase again, but who has weeks for a rewrite?
Well, you can get your app back in shape, without pushing back all your deadlines!
Imagine, upgrading smoothly along your regular tasks, no longer deep in legacy.
Subscribe and get my free email course with steps for upgrading your AngularJS app
to the latest 1.6 safely and without a rewrite.
Success! You rock. You'll get some knowledge shipped to your inbox soon. Let's do this!
Angular 1.6 was released a few weeks ago (and actually 1.6.1 was released a week ago).
I’m so happy to see the Angular team keep spending time in making 1.x better and improving it.
As opposed to 1.5, 1.6 doesn’t introduce a lot of new features or changes, which I think is a good thing.
I believe most 1.x projects have yet to integrate 1.5’s components properly, and so adding more changes might be overwhelming.
1.6 is more of a clean up release, that, in order to fix bugs and remove deprecations, introduces a handful of breaking changes.
I’ll go over the ones that I’m sure will trip a lot of people up.
You can see the full, really long, list here.
Controller pre-assignment of bindings–or $onInit all the things
1.6 changes the default behavior of $compile in that it no longer, by default, initializes a controller’s bindings before $onInit is called (the new lifecycle hook introduced in 1.5, you can read more about it here).
In case you’ve already adopted your code to use $onInit you’re all good.
Also, since most style guides, e.g. John Papa’s, have recommended the use of an init() function of sorts in controllers for quite a while, I’m assuming for most projects this is not a big deal.
Simply rename your current init() functions to be $onInit methods.
What if you don’t want to go over all controllers right now?
In case you don’t have the time or energy to change all your controllers now, but still want to upgrade to 1.6, you can re-enable the old behavior:
But keep in mind that this is likely to be removed in a future version of Angular.
$onInit won’t work for standalone controllers
Standalone controllers are controllers that aren’t defined as part of a directive or component.
Most commonly, these are controllers used as part of route definitions, or, heaven forbid, ng-controller.
Standalone controllers do not get the lifecycle hooks, and don’t have bindings anyway, so just make sure not to accidentally try and update them too in your migration frenzy.
It won’t work.
$http.success and $http.error are finally gone
I’m really hoping not a lot of people are still using these relics of older Angular days, but I keep coming across projects that use it here and there (I believe old blog posts all over the web are at fault, most people aren’t aware of the difference when just starting).
Well, 1.6 has finally killed them.
If you’re still using them for some reason, here’s a great excuse to spend 20 minutes and use proper promises everywhere.
Upgrade and enjoy!
“Maintaining AngularJS feels like Cobol 🤷…”
You want to do AngularJS the right way.
Yet every blog post you see makes it look like your codebase is obsolete.
Components? Lifecycle hooks? Controllers are dead?
It would be great to work on a modern codebase again, but who has weeks for a rewrite?
Well, you can get your app back in shape, without pushing back all your deadlines!
Imagine, upgrading smoothly along your regular tasks, no longer deep in legacy.
Subscribe and get my free email course with steps for upgrading your AngularJS app
to the latest 1.6 safely and without a rewrite.
Success! You rock. You'll get some knowledge shipped to your inbox soon. Let's do this!
After digging into Angular’s components $onChanges lifecycle hook to show how you can replace a regular shallow $scope.$watch with it, let’s see what the latest Angular has to offer when it comes to deep watches.
Deep watches are what Angular does when you supply a third argument to $scope with the value of true:
By using deep watches, we can ask Angular to keep track of changes happening inside an object, by mutating it, instead of only notifying us when the object’s reference has changed (which is the default behavior of $scope.$watch).
While it’s possible to use $onChanges for being notified about changes to references on bindings, there’s no easy way to do it for mutations without $scope.$watch.
The problem is that Angular is moving off $scope, and we would like to be able to use it only where it’s really a must, so what can we do?
First, think whether you really need to be mutating state.
In a lot of scenarios, using immutable models makes a lot more sense, and using one-way data flow makes code easier to maintain.
But, in cases you have to keep using deep watches, there’s some more modern solution offered by Angular.
The $doCheck lifecycle hook is a bit peculiar.
It’s basically a hook that Angular calls on each turn of the digest cycle, for us to add our logic in it.
That allows us to regularly check if anything has changed and requires our component’s attention.
What this enables us to do, in practice, is replace our deep watches.
Here’s an example:
Now, this is a bit of a mouthful.
As you can see, when we want to drop deep watches we actually have to do quite some work on our own: keep track of the previous value, make the comparison, and make sure we are holding a copy of the value (otherwise we’ll never see changes).
Angular is basically telling us that deep watches belong to the past, and that there’s a way to keep doing them without scopes, but you’ll need to work for it.
Personally, I don’t love the extra typing, but I think that if you have to keep using deep watches for now and are upgrading your code to the latest 1.6 standards, you should probably be doing this.
Nowadays, a modern Angular app should be making very small use of scopes, and deep watches are a big red flag usually.
This makes sense both from the standpoint of moving to a one-way data flow, and for keeping your options open when it comes to migrating to Angular 2+ down the road.
“Maintaining AngularJS feels like Cobol 🤷…”
You want to do AngularJS the right way.
Yet every blog post you see makes it look like your codebase is obsolete.
Components? Lifecycle hooks? Controllers are dead?
It would be great to work on a modern codebase again, but who has weeks for a rewrite?
Well, you can get your app back in shape, without pushing back all your deadlines!
Imagine, upgrading smoothly along your regular tasks, no longer deep in legacy.
Subscribe and get my free email course with steps for upgrading your AngularJS app
to the latest 1.6 safely and without a rewrite.
Success! You rock. You'll get some knowledge shipped to your inbox soon. Let's do this!
It is not uncommon to have an Angular directive or component that needs to perform some work when its bounded inputs are changed.
And we all know watches are bad for performance, and that you should only use them when you really need them.
But sometimes your code really needs them.
What to do?
With Angular 1.5’s introduction of components, and the back-porting of lifecycle hooks from Angular 2, we have cleaner ways of achieving this.
Note: This post will use components, but lifecycle hooks are available in Angular’s directive as well.
You can make use of this technique even if your team hasn’t moved to components yet, as long as you’re using Angular 1.5 or later.
Let’s look at a an example component.
For brevity I’ll be using ES6’s arrow functions, but of course you don’t have to:
123456789101112131415
app.component('chart',{templateUrl:'chart.html',bindings:{dataSeries:'='},controller:function($scope){this.$onInit=()=>{$scope.$watch(()=>this.dataSeries,()=>this._updateChart());};this._updateChart=()=>{// Some D3 code to update the chart};}});
As you can see, this is a very basic component that wraps around some native D3 code to render a chart.
Whenever its input binding, dateSeries, is changed the component re-renders the chart.
And it keeps track of those changes using $scope.$watch.
Now, let’s make use of the $onChanges lifecycle hook.
$onChanges is called automatically by Angular whenever an input binding is changed by the component’s parent.
A couple of important details to notice: $onChanges only works with one-way bindings (and @ bindings), which is what you should be using 99% of the time, and $onChanges is only triggered when the parent component reassigns the value.
It will not be triggered if you reassign it inside the component itself.
So let us update our component to use one way bindings and $onChanges instead of a watch:
1234567891011121314151617
app.component('chart',{templateUrl:'chart.html',bindings:{dataSeries:'<'// Changed to one way},controller:function(){this.$onChanges=(changes)=>{if(changes.dataSeries){this._updateChart();}};this._updateChart=()=>{// Some D3 code to update the chart};}});
That’s about it.
We no longer need to inject $scope, which is always a good thing.
And we also removed a watch: Angular has its own watch on the binding anyway in order to sync it between components, and we’re taking advantage of it.
Detecting the Initialization Call
Sometimes when using $watch we would like to treat the first time it is called differently, since $watch triggers immediately after starting a watch.
The way we identify it would be to write code such as this:
As you can see, the changes object comes with a handy isFirstChange() method.
Keeping Track of the Previous Value
Another useful capability of $watch is that whenever it was triggered it would supply our listener with both the current value and the previous value.
This allows the code to compare them:
It just so happens that Angular triggers the initial $onChanges right before calling the $onInit hook.
You should be aware of that when you write your component’s lifecycle hooks and make sure that you don’t rely in $onChanges on anything that gets setup by $onInit, and if so, make sure to account for it on the first change call.
That’s it!
You just got rid of some needless $scope.$watch calls.
Better performance, and modern code – win!
Pat yourself on the back for me.
“Maintaining AngularJS feels like Cobol 🤷…”
You want to do AngularJS the right way.
Yet every blog post you see makes it look like your codebase is obsolete.
Components? Lifecycle hooks? Controllers are dead?
It would be great to work on a modern codebase again, but who has weeks for a rewrite?
Well, you can get your app back in shape, without pushing back all your deadlines!
Imagine, upgrading smoothly along your regular tasks, no longer deep in legacy.
Subscribe and get my free email course with steps for upgrading your AngularJS app
to the latest 1.6 safely and without a rewrite.
Success! You rock. You'll get some knowledge shipped to your inbox soon. Let's do this!
Continuing from my previous post about advanced component communication with Angular’s require mechanism, I decided to try and walkthrough such an example refactoring, based on real code I’ve seen multiple times.
This shows the actual steps you need to take to change existing code to use require and also shares some of my philosophy on when it should be used.
This is my first video ever, and I’d love to hear your feedback!
The source code for the before and after states is available on GitHub.
“Maintaining AngularJS feels like Cobol 🤷…”
You want to do AngularJS the right way.
Yet every blog post you see makes it look like your codebase is obsolete.
Components? Lifecycle hooks? Controllers are dead?
It would be great to work on a modern codebase again, but who has weeks for a rewrite?
Well, you can get your app back in shape, without pushing back all your deadlines!
Imagine, upgrading smoothly along your regular tasks, no longer deep in legacy.
Subscribe and get my free email course with steps for upgrading your AngularJS app
to the latest 1.6 safely and without a rewrite.
Success! You rock. You'll get some knowledge shipped to your inbox soon. Let's do this!
Angular 1.5’s components have made scope isolation the default, and rightly so.
But, it does mean that inter-component communication requires more thought to get right.
Angular’s require mechanism provides a good way to allow some forms of components communication, especially across a suite of related components.
What does require let you do?
When should you use it?
Why it’s not the right tool for everything?
Keep reading!
How require works?
The require mechanism is an old one, and has been in use quite a lot for advanced Angular directive communication.
Prior to components, you’d use require in order to allow a directive access to a different directive’s controller.
That allows directives to expose APIs between one another.
A directive can require the existence of another specific directive, either on its own element or on one of its parent elements.
An example of a same-element kind of communication require is used for, is adding your own validation directive to an input element, and accessing that input’s ngModelController in order to modify the validators.
Accessing parent controllers is more useful for suites of interacting components, such as the way Angular’s form validation works.
Every input element in the form can notify the form that it has become valid, invalid, dirty, etc. by optionally requiring a parent ngForm controller and updating it of changes.
As you can see, require is a very powerful mechanism and an inherent part of Angular, that you can use to make your own components cleaner.
When should or shouldn’t you use require?
First and foremost, require doesn’t work in all scenarios.
It can only be used by a component/directive to get access to a parent controller (or a controller on the same element).
Sibling components can’t communicate using it, and neither can a parent component access its children using it (at least not directly, keep reading for an interesting option).
Another limitation of require is that it assumes the child component knows the name of the other component that it want to require.
There’s no abstraction/indirection here – the name of the required component is hard-coded in the requiring component.
Also, I wouldn’t rush to use require to replace any kind of simple binding.
It’s totally fine to pass some values between components using bindings–it can be more explicit and simpler to maintain.
After all, it’s usually easier to understand what a component depends on if all you need to do is look at its bindings.
Require shines when it allows you setup a family of interacting components, and exposing a rich interface between them without having to wire up half a dozen bindings across multiple layers of components.
Real require example deep-dive: Angular’s forms
As already mentioned, Angular’s whole form and validations mechanism makes heavy use of require.
When you create a <form> in Angular, every element inside it that uses ng-model, such as <input>s, <select>s, etc. registers itself as a control inside that form.
You can see here in the source of ngModel how it optionally requires the form directive.
In case a parent form directive exists, ngModelController uses the form directive’s controller and registers as a control.
See here.
The form directive, in turn, keeps a list of all these controls and updates them on changes such as $setPristine and $setUntouched.
Notice that while there’s no direct way to access a child component from a parent component, the Angular core team did it here by using require from the child and registering it in the parent, exposing the child controllers to the parent.
Angular’s forms, in turn, have the ability to be nested inside other forms.
This happens by optionally requiring a parent form controller.
If require does find another parent form directive, the nested form registers in it to let the parent form know it has nested forms.
This is an excellent example of setting up a component architecture, where our components know about one another and use that knowledge to their advantage.
Real talk: How do you use require in your components
Let’s say that we’ve got this structure in our application:
123
<parent><child></child></parent>
And we want to allow the child component to access parent component’s controller.
We’ll add a require block in child’s component definition, and then be able to access the controller inside the controller, like so:
What do we have here?
The require object allows us to pass as many required component as we’d like.
parentCtrl is the name the required controller will be bound as in our component’s controller.
^^parent might seem live a bit of voodoo, but basically we’re saying that we require a parent component called parent to exist.
The ^^ prefix means the component has to be a parent.
There’s also the ^, which means we can use a component that’s either a parent or on the current element.
And if you simply provide the name, e.g. fooCtrl: 'foo' it means you expect the foo directive to exist on the current element–it will not be searched for anywhere else.
When we’re talking about components, and not directives, you almost always want ^^, though you can use just ^ if you don’t really want to make sure you’re not accidentally requiring the current element.
After we require a component, we can access it from our controller with the name we provided, e.g. this.parentCtrl.
I prefer to make sure the name still uses the original component’s name, to be explicit and make maintenance easier.
If the require string is written like we saw above, it means the component is strictly required.
Angular will error out if it can’t find the component we asked for.
In cases where a component can operate without that required component, you can set the requirement as optional:
123
require:{parentCtrl:'?^^parent'}
The question mark means that in case Angular can’t find a parent component with that name it will silently ignore it.
If you’ll try to access this.parentCtrl you’ll see it’s just nulled.
With great require comes great responsibility
Require is an advanced Angular technique that can really boost certain parts of your app.
It’s great for exactly those few highly-interactive components you need to write.
But, require surely shouldn’t be your default or go-to solution for all communication.
Start by using regular one-way (<) and expression (&) bindings, and reach for require when it’s required (pun slightly intended).
“Maintaining AngularJS feels like Cobol 🤷…”
You want to do AngularJS the right way.
Yet every blog post you see makes it look like your codebase is obsolete.
Components? Lifecycle hooks? Controllers are dead?
It would be great to work on a modern codebase again, but who has weeks for a rewrite?
Well, you can get your app back in shape, without pushing back all your deadlines!
Imagine, upgrading smoothly along your regular tasks, no longer deep in legacy.
Subscribe and get my free email course with steps for upgrading your AngularJS app
to the latest 1.6 safely and without a rewrite.
Success! You rock. You'll get some knowledge shipped to your inbox soon. Let's do this!
As Angular is maturing, it gets harder and harder for newcomers to make sense of the abundance of materials you find online.
I’m pretty sure that at this point, the vast majority of Angular 1.x tutorials use outdated Angular practices–things that modern Angular 1.x projects should not be using.
In order to make it easier for everyone, I decided to grab a list of things that should indicate to you that the post you’re reading might be a bit… stale.
Using $http’s deprecated .success and .error methods
Originally, Angular’s $http service would return promises that had a couple of extra methods on them.
While regular promises just have .then (and the sugar-syntax .catch and .finally), those returned from $http also had special .success and .error that weren’t standard and didn’t behave like regular promise callbacks.
I think this was done in order to make things easier for new comers to Angular that also didn’t know much about promises, but we’ve come a long way since then.
These were deprecated quite some time ago, and are completely gone in Angular 1.6.
Ever since the announcement that controllers are being killed in Angular 2, the community has been moving off of using standalone controllers.
By “standalone” controllers I mean controllers that are used without an encompassing directive/component, e.g. using ng-controller or in a ui-router definition.
ng-controller is used quite a bit in blog posts since it makes things shorter which is helpful.
The problem is that developers then go their projects and use what they saw in the examples, not knowing that ng-controller belongs to the past.
Using $scope bindings and not using controller-as syntax
Controller-as syntax was introduced in Angular 1.3 and has since become the de-facto standard for writing directives/components in Angular.
Prior to controller-as we would slap everything on the $scope, e.g. $scope.model = {name: ''} and then use the scope directly from our template:
1
<inputng-model="model.name">
This, again, makes things shorter requires less typing, but it is not the way we write proper Angular now.
With controller-as we’d initialize the controller code like so this.model = {name: ''}, and the template would be changed appropriately to not access $scope directly:
1
<inputng-model="$ctrl.mode.name">
Note that I’m using $ctrl as my controller-as name, which is the default for components in Angular 1.5.
You might see vm used a lot in blog posts.
That’s fine, though I’d recommend just going with the default.
Using non-isolated scopes
This is another thing that was used a lot more in the standalone controller days.
Controllers by themselves never have isolated scopes, it’s up to the directive/component to isolate the controller it’s encompassing.
So whenever you used ng-controller, or didn’t specify explicitly that you want your scope to be isolated in the directive definition, you’d instead get a scope that prototypically inherits from its parent scope.
This would quickly become a non-maintainable soup of properties where refactoring is almost impossible without breaking something, somewhere.
That is why for quite some time now the rule of thumb has been to write maintainable, isolated directives.
Using directives instead of components
Since Angular 1.5 was released, introducing components, about 99% of your directives should be written using components.
Essentially, any directive that has its own template should be written as a component.
So, ng-click-use a directive, anything else–components.
Yeah, most online material hasn’t been updated to use components whenever appropriate (heck, I’ve got a bunch of posts that need updating as well).
But, modern Angular 1.x is components based.
Do it.
It has been common practice to stay clear of $watch when possible.
And yet, going through some guides you’d think it has to be used way more often that it really does.
Ever since 1.5 introduced lifecycle hooks in components, we no longer need to use $watch to trigger some logic whenever a binding changes from a parent component.
Instead, use the $onChanges hook to perform whatever is needed without adding an extra watch,
and maybe even saving you the need to inject $scope at all.
I hope this helps you evaluate the freshness of information and understand that just because something doesn’t come up in some posts it doesn’t mean it’s not not popular – just too new to be popular in posts :)
“Maintaining AngularJS feels like Cobol 🤷…”
You want to do AngularJS the right way.
Yet every blog post you see makes it look like your codebase is obsolete.
Components? Lifecycle hooks? Controllers are dead?
It would be great to work on a modern codebase again, but who has weeks for a rewrite?
Well, you can get your app back in shape, without pushing back all your deadlines!
Imagine, upgrading smoothly along your regular tasks, no longer deep in legacy.
Subscribe and get my free email course with steps for upgrading your AngularJS app
to the latest 1.6 safely and without a rewrite.
Success! You rock. You'll get some knowledge shipped to your inbox soon. Let's do this!
ng-include might seem like an innocent, helpful directive.
After all, it allows us to break down a big template into simpler chunks, which makes code more readable, right?
Personally, I never liked ng-include or used it, but I didn’t care much if I saw code that made use of it.
But years ago I found out that in addition to being a kind of code smell, ng-include also has a pretty significant performance hit.
Since then I’ve religiously made sure my clients stay clear of it.
And yet, whenever I start working on another Angular codebase it’s always there like clockwork – another whole slew of ng-includes.
So, today, we will see why you shouldn’t be using it, for elegance and for performance!
ng-include is a code smell
I know most people use ng-include because it “makes code clearer” or “makes templates easier to maintain”.
But come on, does it, really?
Look at this:
12345
<div><divng-include="'header.html'"><div><div>Some things go here</div><divng-include="'footer.html'"></div></div>
If you think that this will be easier to maintain, you’re wrong.
In fact, what we just did is break a single component to have its template spread across 3 different places.
Want to make a change in the component’s controller to refactor an object?
Make sure to track down all the sub-templates that use it to change it there, too.
The indirectness between the component and its newly-split template makes for a very common pitfall.
I’ve seen these break and cause bugs in production so many times, because it’s the easiest thing to miss when you quickly scan a template file and do a search for the name of a variable you just changed.
You simply don’t expect it to be used elsewhere.
Also, if what you’re saying is that your component’s template is so big you need to break it down, why not create a new component to make it smaller?
Most of the times, it’s not just the template that’s getting bigger, since things aren’t static.
You may be making the template files shorter, but in the background we’ll have a controller that’s growing bigger and bigger, with way more responsibility than it aught to have.
My rule of thumb is that if I feel like something is getting too big, I’ll break it down to smaller components, not just try to sweep parts of the template under the rug.
But even you don’t agree, performance will kill you
I was actually very surprised when I first saw this while debugging performance issues in a big app a few years ago.
And this has yet to change.
ng-include is just way slower than using a directive/component instead.
Yeah, even though components create new scopes and actually introduce more watches, they are faster.
Benchmarks shows that if you have a lot of ng-includes on a screen, initial loading time of these sub-templates will be about 50%-60% longer than if you’d use plain sub-components.
And it doesn’t stop at initial loading.
Once everything is one the screen, even though using a subcomponent easily doubles the amounts of watches, the impact of using ng-include on the digest cycle can be a whopping 100% increase.
Yes, your average digest cycle can take twice as long, simply because of that, making everything in your app feel slower and laggy.
It’s even as simple as replacing:
123
<divng-repeat="todo in $ctrl.todos"><divng-include="'todo.html'"></div></div>
With:
123
<divng-repeat="todo in $ctrl.todos"><todotodo="todo"></todo></div>
This alone, with a big enough todos list, will show you initial loading taking seconds longer, and digest cycles twice as slow.
You can see it for yourself, compare this ng-include option with this sub-components option.
On the upper left corner you will see ng-stats running and displaying the digest cycle time.
At least on my machine this shows ng-include as 27ms components 14ms on Chrome (93% more), and 4.3ms vs 1.6ms in Safari (169% more!).
So please, stay away from ng-include.
Not using it is a simple win-win – code that’s more resilient to breaking, easier to maintain, and more performant.
“Maintaining AngularJS feels like Cobol 🤷…”
You want to do AngularJS the right way.
Yet every blog post you see makes it look like your codebase is obsolete.
Components? Lifecycle hooks? Controllers are dead?
It would be great to work on a modern codebase again, but who has weeks for a rewrite?
Well, you can get your app back in shape, without pushing back all your deadlines!
Imagine, upgrading smoothly along your regular tasks, no longer deep in legacy.
Subscribe and get my free email course with steps for upgrading your AngularJS app
to the latest 1.6 safely and without a rewrite.
Success! You rock. You'll get some knowledge shipped to your inbox soon. Let's do this!
Angular’s dependency injection is a great boon to productivity, but even it has its limits.
While not an everyday occurrence, it is quite possible to come across a circular dependency.
This happens when service A injects service B, but service B in turn injects service A, usually indirectly.
For example, B depends on service C which depends on A – A -> B -> C -> A forms a nice little circle.
When Angular’s bootstrap process tries to setup all the services and inject each service’s dependencies, it detects when such a scenario happens and errors out, instead of getting stuck in an infinite loop.
In most cases, circular dependencies are code smells for design that could be made clearer.
Most likely you have a service that’s gotten too big, and splitting it will result in cleaner code and no circular dependency.
But, there are a few common scenarios that come up in a lot of apps where some kind of circular dependency makes sense.
Let’s look at an example and a solution.
A real-world circular dependency
Enter HTTP interceptors.
Interceptors are Angular’s very handy tool for handling cross-app concerns when it comes to handling HTTP requests and responses.
They are probably most often used for handling authentication.
I’ve come across circular dependencies showing up in interceptors at several clients.
It usually goes something like this:
As part of implementing the authentication mechanism of the app, we create an interceptor to be in charge of handling the different responses.
One of the behaviors it needs depends on an external service, which in turn makes an HTTP request.
For example, we would like to redirect the user to a login page on every 401 error.
An interceptor watches for these errors, and once it sees them it calls our AuthService to tell it to handle an expired session.
The same AuthService depends on $http for some reason, like performing a login request.
A naive setup would look somewhat like this:
12345678910
appModule.factory('AuthService',function($http){return{login:function(user,password){// This uses $http to login},handleExpiredSession:function(){// Redirect to login page}};});
123456789
appModule.config(function($httpProvider){$httpProvider.interceptors.push(function(AuthService){return{response:function(response){// Detect and handle 401 errors}};});});
If you’ll try running an app with this code, Angular will spit out an error: Error: [$injector:cdep] Circular dependency found: $http <- AuthService <- $http.
That’s because $http depends on our interceptor, which depends on AuthService, which depends on $http.
(Are you getting dizzy, too?)
$injector to the rescue
Just for cases like these Angular provides us with the $injector service.
The injector is the programmatic way to access Angular’s dependency injection.
Using it, we can manually inject AuthService inside our interceptor and break the circular dependency:
12345678910
appModule.config(function($httpProvider){$httpProvider.interceptors.push(function($injector){return{response:function(response){// Detect and handle 401 errors$injector.get('AuthService').handleExpiredSession();}};});});
Calling $injector.get('AuthService') will return the exact same singleton instance of AuthService.
The main difference is that now we are performing this at a later point, after Angular has finished bootstrapping the project.
At this point in time, where everything is up and running, it’s safe to inject AuthService.
Thus we have effectively broken out of the circular dependency.
Voila!
“Maintaining AngularJS feels like Cobol 🤷…”
You want to do AngularJS the right way.
Yet every blog post you see makes it look like your codebase is obsolete.
Components? Lifecycle hooks? Controllers are dead?
It would be great to work on a modern codebase again, but who has weeks for a rewrite?
Well, you can get your app back in shape, without pushing back all your deadlines!
Imagine, upgrading smoothly along your regular tasks, no longer deep in legacy.
Subscribe and get my free email course with steps for upgrading your AngularJS app
to the latest 1.6 safely and without a rewrite.
Success! You rock. You'll get some knowledge shipped to your inbox soon. Let's do this!