Preparing to separate data from presentation - DEFEATING THE DRAGON - JavaScript in Plain Language (2015)

JavaScript in Plain Language (2015)

PART V: DEFEATING THE DRAGON

5.4 Preparing to separate data from presentation

Creating a controller

Congratulations, we are moving steadily into a practical presentation of AngularJS. Please be patient, we’re almost done.

Up to this point we have been initializing dynamic data with ng-init. This may come handy sometimes but it is not an ideal implementation.

Fig 29 (ng-init will be replaced by a controller at the bottom of the page)

We need to move towards a separation of files residing in properly labeled folders, but we are not there yet. I mean, we should be able to visualize the whole picture in just one page before we start splitting the code into different files, and that’s what we are doing in this course: learning the basic parts of an Angular app. If we split code into folders at this level of understanding it just adds unnecessary complication and we do not want to go there right now.

What we’re going to do next is to stop writing our data on the top of the page with ng-init and move the data object into the bottom of the BODY section (inside of the same script we inserted our module declaration). Our JavaScript code will be written inside of a controller, which is a special AngularJS function, (a method or property of the module we have just created).

So, right now we are going to split the body of the web page into the following conceptual areas:

a) The standard HTML markup and its regular data, the one that does not need any influence from AngularJS. This data could actually be anywhere across the page because dynamic data will be wrapped into code blocks as you will see.

b) The area where we want AngularJS to display some dynamic data. This area is known as a view. AngularJS injects into the page a number of mini dynamic views that blend along with the rest of the HTML presentation. It could be just one view, or many views depending on what you really want to do. Think of it as a script that we embed into the page, just like we do with pictures or with videos from YouTube.

c) The area where we create JavaScript code. We call this area a controller. The area is surrounded by the tag <script></script> just like the link to the framework is. The idea of the controller at this point is to store data and functionality like we did with ng-init. The controller is a function and inside of the function we have our dynamic data. Remember, we use functions to store code that can run when called upon.

We are going to remove the ng-init from our previous exercise and insert the data into the controller but first let’s look at the basic structure of a controller. Let’s start with the image of an empty controller:

Fig 30 (controller is a child of module x. JavaScript code goes in the controller)

The controller function is a child of module which in our example is assigned to variable x in order to make it easy to implement the controller (x becomes a shortcut to the whole module declaration). So, the module myFirstAppModule is assigned to x and from variable x we extract acontroller [line 3]. Inside of the controller method we have two parameters. The first parameter is the name we are giving to the controller and in a string type format (I’m calling it 'myFirstController'); the second parameter is an AngularJS object called $scope. $scope is an Angular object that keeps track of all data and properties we are going to send to the web page as we will see shortly. Notice how all this code is inside of the SCRIPT tag [lines 1 and 7] where the ng-app="myFirstAppModule" module was declared.

AngularJS magically makes available to the HTML view (the view assigned to this controller) whatever code and data we write inside of the controller and assign to object $scope.

We could say that $scope is the glue between the controller and the page view. The controller wants nothing to do with the page view because it is supposed to be generically written. I mean, we could have several different views using the same data from the controller: one view for a desktop screen, another view for a mobile phone, etc. It is up to $scope to interface with the external world of views and AngularJS provides such functionality in the background.

Before we do the exercise let me summarize some concepts to help you further understand what is going on.

$scope

$scope is an object that maps the whole page (the DOM) and creates properties that can be addressed. In a sense it is like the DOM in parallel. Later you will see that we add $scope to our controller and then can add variables and functions into $scope which in turn will be available to the web page app. (Note: The $ looks like an “S”, right? that reminds us that $scope is an AngularJS service).

View

View is the area of the application that will be placed on display. Just like images, videos or other embedded text, we could have several views on one page and they may be hardwired or linked from a different source. Contrary to the HTML standard presentation, view data is not persistent which means that it varies based on the logic provided by $scope (coming from the assigned controller), or the behavior of a user interacting with the page. A view is created by standard HTML elements in combination with AngularJS’ enhancements from data binding and directives and curly braces {{}}.

· Any complex logic should be placed in the controller and let $scope manage the feeding of this logic into the view. This does not mean that a view can’t have any logic; simple expressions can be placed in the view as we have done before during our first exercises.

Controller

The controller is where we program the JavaScript code to be executed when the page runs.

The controller’s purpose is to provide the logic required by AngularJS in order to initialize the object $scope, which is the object that maps the webpage like a DOM.

Before using a controller with $scope, we were using a simple ng-init to save and initialize our data but that was a very limited implementation. $scope is a more powerful object and the most appropriate way to declare our properties and methods.

This next image shows how and where we implement the data we previously assigned to ng-init:

Fig 31

As you can see, all the JavaScript code is treated as a property child of $scope. AngularJS will grab both length and width and saves them as part of $scope (remember, $scope will manage the DOM which is how and where things are displayed).

There is only one thing left. We now have an app name and we have a controller with some JavaScript variables to be processed. But how does AngularJS know where to display those variables in the paragraph when we open the page?

As a reminder, this is what we had in the HTML BODY of the old exercise:

Fig 32

The answer is, we should wrap the whole paragraph in a DIV and assign the DIV to the controller which is called myFirstController:

Fig 33 (we wrap the paragraph in a div assigned to myFirstController)

Your turn: Add a controller to your exercise

At this time let’s modify our previous exercise to add a controller to it.

Here’s the original raw file as we left it after adding the module myFirstAppModule.

1- Add the controller to the script container, one line below the module declaration as seen on figure 30.

2- Assign length and width to $scope as seen on figure 31 and then remove the ng-init from your page.

3- Wrap your paragraph in a DIV and assign the DIV to the controller as seen on figure 33.

4- Test your HTML Angular app. Here’s my own version of it:
raw file | html.

We now have a complete simple Angular app.

Alternate way to code length and width

Another way to include the length and width on our exercise is by making them properties of some object like for example myPerimeter and then write them inside of the controller as follows:

$scope.myPerimeter = {
"length": 9,
"width": 3
};

Instead of using $scope.length =9; and $scope.width =3;

In the HTML view we would write them as

{{myPerimeter .length}},{{ myPerimeter .width}}
and the formula: {{myPerimeter.length * 2 + myPerimeter.width * 2}}

Let’s keep practicing by creating a few cool mini projects.

Create a program to convert Fahrenheit to Celsius

We are not going to create this program from scratch because we have done earlier on Lab work 6 when learning JavaScript.

See forum | bit.ly/1Dp2SKk
raw file | bit.ly/1ruKrzi.

What we are going to do is to create an app to display the result on an HTML page.

The idea is to practice setting up AngularJS on the page as well as practicing ways to include code implementations in the controller.

Here’s the recipe menu for my sample:

Module name: myAppModule

Controller’s name: myController

Variable to display the result: result (hint: it will be $scope.result in the controller).

Sentence to be dynamically displayed:

100 degrees Fahrenheit is 37.778 Celsius.

NOTE: make sure you change the original f2c(212) to f2c(100) when calling the function.

Are you ready for the challenge?

Please do the exercise yourself. Here’s a summarized guideline:

1- Create your HTML skeleton.
Here’s our starter file: raw file.

2- Link to AngularJS (if you’re using the starter file this is already done).

3- Add the ng-app directive (already done) and name it myAppModule.

4- Below the link to AngularJS add a new Script container and declare your module in there. (I’m assigning mine to variable x).

5- Add your controller below the module declaration. The name of the controller is myController and it belongs to variable x. Also in its parameters it shows a string with the name of the controller and after separating with a comma, a second parameter introduces a function which passes in $scope.

6- Add the Fahrenheit to Celsius conversion function to the controller.
Here we have a few alternatives. Let me explain:

a) First get a copy of the original JavaScript script here: raw file.

b) We need to replace console.log with return because we are catching the result and sending it to $scope so that it can be displayed.

c) Now we have two options (see steps c through f):
We could assign function f2c as a property of $scope this way:
$scope.f2c = function(f){return ((f-32) * 5 / 9);}

d) Then, when we get to the HTML page view, we can implement the result this way:
{{f2c(100)}}

e) OR (and this is a better way because it avoids using JavaScript in the HTML view), we can insert the original function inside of the controller (except for the console.log), and then assign to $scope a variable such as for example result, with the result of a function call.
x.controller('myController', function($scope){
function f2c(f){
return (f-32) * 5 / 9;
}
$scope.result = f2c(100);

f) If we use option (e), we then apply variable result in the view like this:
{{result}}

7- In the BODY of the HTML page create a view by adding a DIV container.
Link the opening DIV tag to ng-controller="myController"

8- Inside of the DIV container add the paragraph with the following message:
<p>100 degrees Fahrenheit is {{YOUR CODE HERE}} Celsius.</p>

9- Where it says YOUR CODE HERE, replace it with option (d) or option (f) based on the way you programmed your controller’s JavaScript as stated in (c) or (e).

10- Save and test your app. The display result should be
100 degrees Fahrenheit is 37.77777777777778 Celsius

When you’re done, compare it with my result here:
forum | bit.ly/1ruKCKT
raw file |
html |

Suggestion: Try to convert some of the other JavaScript exercises into Angular. You may not get a perfect solution but you will get plenty of practice.

Placing the controller on a separate file

Now that we have logically separated the controller and view units (both part of the module), we can safely place the controller and module configuration on a separate file. This is by no means the end of the story, perhaps just the beginning.

On simple applications we can just leave it on the same page as we have done earlier. However, as versatile developers we need to practice separation of concerns in order to get ready for bigger things.

It’s pretty easy. Let’s take for example the exercise we just finished (Fahrenheit to Celsius):
raw file |

1- Save it under a different name, like for example f2cfinal.html

Now we can edit the file:

2- Copy and cut (delete) all the contents inside of the <script></script> where the controller and module configuration are. Just leave the SCRIPT tags by themselves so that we can add a link to the separate file.

3- Paste the contents on a new file and save it as mycontroller.js (or any other name).
Notice the .js extension. Make sure the file is on the same folder as f2cfinal.html.

4- Go back to f2cfinal.html and in the opening SCRIPT tag that belonged to the controller, type the path to the new file which should be the following including the script tags:
<script src="mycontroller.js"></script>

5- Save and test.
Compare with my result here:
raw file |
.js file |
html |