A lot of people are (rightfully) overwhelmed and confused when they start using AngularJS. There are a ton of new concepts to grasp - $scope
, controllers, services, and the often-confusing directives. And then what’s the difference between a service and a factory? And how do you use promises?
This post aims to clear up some of the confusion. And don’t worry – it’s normal, even expected, to be confused when you start working with Angular. But you’ll get over it quickly as you start to build apps, and hopefully this overview will help you along the way.
A story, for starters
Code can do more harm than good in the beginning, so let’s start off with a story.
Our tale takes place on a cold, blustery night when Lucy, collapsing down on her couch after a long day at work, realizes an urgent problem: she’s hungry. “It’s 8pm! I forgot about dinner again,” Lucy scoffs to herself.
Lucy works as a developer at Food Delivery Corp. It’s a very hectic startup where she’s the sole developer, and she’s responsible for writing the web app that lets customers get food delivered from nearby restaurants. So it’s especially ironic that she would forget about dinner, like ever.
In any case, she decides to order some food online (using the app she wrote, obv).
She brings up the Place Order page, and even though it’s just a simple list of restaurants, her mind is running through the code as she clicks around.
“List the restaurants using ng-repeat. Click ‘Order Here’, triggers an ng-click handler on the controller. Not on $scope, that’s for newbs. I’m using controllerAs.”
“Off goes a call to the data service, and a request to the server using $http. Request comes back, promise resolves, calls my then() handler, which shows the order form.”
“Here we go. Small hawaiian, bacon instead of ham. Throw in a Coke, too. Should be here in about 15 minutes.”
[ed] They must have magic elves or something. 15 minutes for delivery?! I’ve never gotten anything faster than 45.
Lots of pieces
Let’s walk through what Lucy was muttering to herself during that pizza order. There are a bunch of components interacting there. Did you catch all of them?
- 1 View: some HTML that shows a list of a restaurants, and displays an order form
- 1 Controller: to hold on to the list of resturants, and handle the user’s interaction
- 2 Directives: ng-repeat and ng-click, both built-in to Angular
- 2 Services:
$http
, which comes built-in with Angular, and also a custom “data” service - 1 Promise: returned by
$http
, and handled by a function that updates the restaurant data - 1 Delicious Pizza: Pineapple and bacon, amirite?
Down to business
We’ll dive into each component and see if we can reverse-engineer something that looks like the app Lucy wrote. Her boss probably won’t be happy but we’ll learn a lot about Angular this way.
Views
Let’s start with the easy one: the view. It’s what renders on the web page. It’s what the user sees. It’s mostly plain old HTML, but there can be some Angular magic sprinkled in there too (in the form of directives, which we’ll get to later).
The Place Order page that Lucy was using is a view. For the sake of argument, let’s say it looks something like this:
Aside: The
h1
tag should be used for the most important thing on the page, and describe what the page is for. “Food Delivery Corp” is not that. Lucy argued with her boss for hours over that one.
Controllers
It’s the controller’s job to give data to the view, and to handle user interaction. In the case of Lucy’s ordering page, the controller needs to provide the list of restaurants and also a way to order from one.
In a real app, you probably wouldn’t hard code a list of 3 restaurants like this, but bear with me.
I want to talk about the line var ctrl = this
for a minute.
There are 2 ways controllers can pass data to views:
- Using
$scope
, and setting variables on it like$scope.restaurants = [...]
- Using
controllerAs
, and putting data on the controller’sthis
object
You can see here that Lucy used option #2.
But wouldn’t it be possible to just use this
directly without assigning it to ctrl
? Why take that extra step? Well, because variable scope in JavaScript is a little unusual, and if you were to use this
inside a nested function, like inside showRestaurant
, it’d refer to something totally different, and cause weird bugs.
So be safe, save yourself some hair pulling, and assign this
to a variable at the top of your controller.
It’s common to name it something like ctrl
or vm
(for ViewModel). I didn’t name it vm
lest you think that the name vm
in the view must match the one in the controller. But we’ll get to that later.
Interlude: controllerAs
The controllerAs
construct is relatively new in Angular (introduced in 1.2). Todd Motto has a great writeup about it, but the 30-second version is that it reduces your dependency on $scope
, treats the controller more like a proper class, and helps to disambiguate variable names when working with nested scopes.
It’s the current “best practice” to use controllerAs
instead of injecting $scope
into your controllers. So instead of:
You’ll do something like this (note the as main
, and main.location
):
Directives
Angular would not be much of anything special if it weren’t for directives. They make it possible to extend HTML with custom elements and attributes that bring their own behavior.
In the story above, Lucy used two directives that are built in to Angular: ng-repeat
and ng-click
. These are pretty easy. Let’s go over how they work.
ng-repeat
It works like a for
loop in your HTML, iterating over an array of elements and rendering each one.
Every restaurant will get its own <li>
tag showing its name and the “Order Here” link.
ng-click
If you’ve ever used onclick
, or jQuery’s .click(function() {...})
, well, ng-click
is very similar to those.
If you’ve never used those things, what you need to know is this: ng-click
will call the given function when the user clicks on the element. In this case, it’s vm.showRestaurant(restaurant)
. Super simple.
What about custom directives?
Writing your own directives are a big enough topic that talking about them here will take this post off into the weeds. I’ll cover how to create your own directives in another post! (If you want to be sure you don’t miss it, sign up for email updates at the end!)
Services
It’s widely regarded as a good idea to keep logic (and especially HTTP requests) out of your controllers. The best place for that kind of stuff is in a service.
Angular comes with bunch of services built-in, and they all start with a $
. $http
is a prime example, and one that Lucy used in her app. Keeping up with best practices, she didn’t call $http
directly from the controller; rather, she created a service to take care of that. It probably looks something like this:
You can see that the $http
service is a parameter to the RestaurantData function. It’s not just any old parameter though, it’s being injected by Angular’s dependency injection system.
In getOrderingInfo
, we’re making a call to $http.get
which returns a promise. The promise will be resolved when the HTTP request comes back successfully (or rejected if the request fails).
Dependency injection
DI is at the core of how Angular works. It’s a fancy term, but the concept is simple: rather than having each object know about how to create the things it needs, those things are automatically created and handed to the object.
Angular will look at the argument list to your controller/service/factory/directive, go and find objects that match those names, create them, and pass them in to your thing.
In the example above, $http
is a built-in Angular service, but you can just as easily define and later inject your own custom objects. This is what the line .factory('RestaurantData', RestaurantData)
is doing: it says to create an object named “RestaurantData”, implemented by the function RestaurantData
, so that when some other object calls for a RestaurantData argument, it will get this function.
It’s nice that the names match, but they don’t have to – just make sure when you go to inject an object, that you use the string name.
Now that the name “RestaurantData” is registered with Angular, it can be used in other objects you create. If you look back to the Controllers section, above, you’ll notice that RestaurantListCtrl
calls for RestaurantData
as an injected argument.
Why factory
and not service
? What’s the difference?
-
A factory returns an object that contains a set of behavior, variables, etc.
RestaurantData
, above, is done as a factory. -
A service is different in that Angular will call
new
on it before injecting it. So rather than returning an object from a service, the behavior should all go on thethis
object. Here’sRestaurantData
rewritten as a service:
Factory and service are similar, but they are not interchangable without some modifications.
As John Papa recommends in his Angluar style guide:
Since [services] are so similar to factories, use a factory instead for consistency.
Read more about factory
vs service
here.
Promises
The last common stumbling point we’ll talk about is promsies.
It’s ok, and very normal, if you’re confused by how they work. They confused the heck out of me when I started with Angular. Even now, when more advanced uses come up, it might take me a few tries to get it right. So don’t worry.
Promises are a way to deal with calling functions that take a while to return. The reason you don’t want to call a function and just wait (even if that might be easier to think about) is because the browser is only running one thread for JavaScript. If you make it sit there and wait, it can’t do anything else, like respond to the user furiously clicking buttons because the damn thing isn’t responding.
So, when you make a call to something that returns a promise, the actual result of that call is deferred until later, even though the function returns immediately. Here’s an example:
When this runs, it will print about to send the request!
followed by request has been sent!
. When $http.get()
is called, it returns right away, with a promise. But the request isn’t finished yet.
Only once the request completes successfully will it call the function passed to then()
, which will print out request completed!
. Got that?
Another nice thing that promises let you do is chain them together, like so:
Maybe this is self-explanatory, but I’ll explain it anyway:
$http.get
is called, and the request goes off to the server. The function returns a promise immediately.- That returned promise has a method called
then
, which accepts a function, so, we call it and pass a function that printsrequest completed!
some time in the near future. It also returns something. When athen
function returns something, that gets passed along to the next function in the chain. - The next function in the chain prints how long the reply was. But it doesn’t return anything, so the next function won’t receive anything as an argument.
- The last function is called with no argument (so
this_will_be_undefined
is, well, undefined).
Wrap Up
Ok! That’s the high level overview of all the big pieces of Angular. The best thing you can do now is to go out and practice. Make a number of different apps, little simple things, in order to solidify your knowledge. The best way to get better at anything is to do it, not read about it. So get to work!
Oh, one last thing! I’d be remiss not to mention that I’ll be posting more articles like this one, with more helpful Angular tips and solutions. If you want more like this, sign up below. Thanks for reading.