Tuesday, August 26, 2014

Previous Interview Question Next Interview Question
Home

Why we need controllers?

In the previous post I mentioned how Angular markup inside your pages can quickly get out of control. The day is long past where we just need a few small functions on our page and that’s the end of our JavaScript. The amount of JavaScript required on a page for modern day applications becomes unmanageable when embedded in onlick event handlers or elsewhere in the page. And just like development in any other environment, you want a good separation of concerns.

Even though we are doing front-end development when working with JavaScript, our front-end apps are becoming complex enough that they are applications in their own right. This means that in addition to JavaScript code that is just wire-up to controls on the page (event handlers that just call a function for example), we also have more involved, business logic, that really doesn’t belong directly on the page. We even have data-layer code when you consider ajax calls to be your data-layer. Controllers are essentially the entry-point into your front-end business logic; they should contain all the methods that your super-simple code in your page should call. Controllers also allow you to initialize the scope with the models that your page uses. (I was about to say that controllers hold your models, but that’s not true, models go on the scope, not on the controllers).

Creating and Referencing a Controller
Before we can create a controller, we must first create our first module (that’s “module” not “model”). You can think of a module much like a namespace in C# or Java, except that they are a little more than that. Controllers, services, filters, and directives are all created within a module like you would expect in a namespace. The difference between a module in Angular and a typical namespace is that modules also have methods on them for initialization of the module and for creation of controllers, services, etc. So let’s create a module for our app. We’ll start with this fiddle which is identical to the one we ended with in the last blog post except for one small change: I’ve changed the way I’m loading Angular to be loaded onload instead of just as a separate resource (this is just to make it work better with jsFiddle, so don’t worry about that).

So to create our module, add the following to the JavaScript panel in the fiddle:

    var myModule = angular.module('myModule', []);
Notice that there are two parameters passed into the module method: The name of your module and an array. In this example we’re not passing anything into the array, but we could pass in a list of strings that refer to other module names (this is why you need a name for your module in the first place). Passing in other module names in that array allows your new module to inherit the controllers, services, etc. from those modules.

Ok, so we now have a module that we can use to both create and contain our controller. So let’s create our first controller by adding this code below the line where we added our module:

    myModule.controller('myController', function($scope) {
    });
Notice, here, that we’re using the myModule variable we created above when we created our module. The controller method on a module can be called in two different ways — this is the simplest — if you are going to minify your Angular JavaScript you’ll want to look into the other form, but I’m not going to cover that in this post. Also notice that we are injecting the $scope service into our controller. Yes…I said inject. AngularJS comes with a dependency injection container built in to it; this is one of the simplest (you hardly know it exists, it just works) and most powerful parts of Angular and I’ll talk a bit more about it in the next post on services. We’ll use this $scope service right away.

So, we have a controller with the $scope service injected, but it isn’t really doing anything yet. If you run the fiddle now, you can see the page still works, but we’re not doing anything in our controller yet. Here is the fiddle with the above code added. Lets make our controller do something interesting by getting rid of that ugly initialization code in our html. In the previous post we added the ng-init attribute on the body tag, that really doesn’t belong in our page. So, remove that entire attribute from the html and, instead, add the following inside the controller function:

    $scope.users =
        [
          {firstName: 'Jane', lastName: 'Doe', age:29},
          {firstName: 'John', lastName: 'Doe', age: 32}
        ];
Your fiddle should now look something like this one. The first thing you’ll notice is that the page has stopped working (our list of users is no longer showing up). This is because we have created a controller, but we are not yet using it — in fact, we aren’t using our module either. So, let’s first use our module. An AngularJS app will use a single module (although that module can be composed of other modules). To reference our module, change that ng-app attribute on the body to look like this:

    <body ng-app="myModule">
Now our app is using our new module so let’s tell the section of our page that needs the controller to use our new controller. Note that you can use multiple controllers (either as siblings, or even nested) on a page. Since we’re only going to be using a single controller, lets add it to our body tag, but you could add it to any container tag (like a div). Update the body tag to look like this:

    <body ng-app="myModule" ng-controller="myController">
Look at that, our list of users is back on our page! So, looking at the code in our controller, notice that putting an item on the scope is what is required to make the model accessible to the view. Of course a real app would do something much more interesting than just expose a static array, but you get the idea. Here is the fiddle with the above changes.

Calling Methods on the Controller
So we’ve seen how to initialize data on the scope from the controller, but what if we want to call a method on the controller from our page — like when a button is clicked, for example. Easy-peasy. Let’s change that ng-click handler on our button to call the controller:

     <button ng-click="changeFirstUsersFirstName()">Change Name</button>
I admit a function that changes the first user’s first name is useless, but you get the idea. Notice that this is just a simple method call. It will look for this method on the scope. So let’s add a method to the scope from our controller in order to handle this. Add this inside the controller:

    $scope.changeFirstUsersFirstName = function() {
        $scope.users[0].firstName = 'Jill'
    };
If you did that right, clicking the button will now do the same as it did before. The important code is now in the controller and there is less code in our page. Your final fiddle should look something like this.

For a more complete discussion of controllers and scope, check out the “Controllers and Markup” module in our AngularJS Fundamentals course.

Next Up: Services
Imagine if the code in our controller, instead of just using a static array, made an ajax request to get the users. That sort of logic really doesn’t belong directly in your controller. Pulling it out into a service of it’s own would help us adhere to the Single Responsibility Principle as well as make the service reusable.

In the next post I will be talking about how you can create your own services. If you want to learn about that now, check out the services module in the course. The course also covers a lot that is not covered in these introductory posts, for example, there are a host of built in services that come with Angular and those are all discussed in the course.

Previous Interview Question Next Interview Question
Home

0 comments :

Post a Comment