May 08, 2021 AngularJS
It's time to create a dynamic web page with AngelJS. We'll add a test to verify the code for the controller, and we'll add the controller.
There are many ways to construct code for an application. F or Angular applications, we encourage the use of module-view-controller (MVC) design patterns to decouple code and separate concerns. With this in in perspective, we use small Angular and JavaScript to add modules, views, and controller components to our applications.
Reset the workspace to the second step
git checkout -f step-2
Refresh your browser or check this step online: Step 2 Live Demo
The most important differences between the first and second steps are listed below. You can see the full difference in GitHub.
In Angular, the view is a map of the module through an HTML template. This means that whenever the module changes, Angular refreshes the appropriate binding point and updates the view.
Angular structured the view components using the following code as a template:
app/index.html
:
<html ng-app="phonecatApp">
<head>
...
<script src="/attachments/image/wk/angularjs/angular.js"></script>
<script src="/attachments/image/wk/angularjs/controllers.js"></script>
</head>
<body ng-controller="PhoneListCtrl">
<ul>
<li ng-repeat="phone in phones">
<span>{{phone.name}}</span>
<p>{{phone.snippet}}</p>
</li>
</ul>
</body>
</html>
We replace the hard-coded list of phones with the ngRepeat instruction and two Angular expressions:
<li>
ng-repeat="phone in phones"
on the element label is an Angular transponder instruction.
The transponder tells Angel to create an element for each phone in the list that uses the element tag
<li>
as a template.
<li>
{{phone.name}}
{{phone.snippet}}
will be replaced with the value of the expression.
We've added a
ng-controller
that adds a
PhoneListCtrl
controller to the element
tag.
At this point:
{{phone.name}}
and
{{phone.snippet}}
represent bindings, and they are cited in our application modules, and they are set
PhoneListCtrl
controller.
The
Data
module (a simple series of phones, expressed literally as object notation) is now instantiated
in
the
PhoneListCtrl
controller.
The
controller
is just a constructor function and requires a
$scope
parameter:
app/js/controllers.js
:
var phonecatApp = angular.module('phonecatApp', []);
phonecatApp.controller('PhoneListCtrl', function ($scope) {
$scope.phones = [
{'name': 'Nexus S',
'snippet': 'Fast just got faster with Nexus S.'},
{'name': 'Motorola XOOM? with Wi-Fi',
'snippet': 'The Next, Next Generation tablet.'},
{'name': 'MOTOROLA XOOM?',
'snippet': 'The Next, Next Generation tablet.'}
];
});
Here, we declare a controller called
PhoneListCtrl
and register it in an
PhonecatApp
Note that our
ng-app
instruction (on the element tag
<html>
now
phonecatApp
module name as the mounted module, which is loaded when booting the application of the Angulal application.
Although the controller doesn't do much, it plays a vital role. B y providing data modules for our context, the controller allows us to establish data binding between modules and views. Let's add dot dashed lines between the presentation, data, and logical components, as follows:
<body>
label, refers to the name of our controller,
PhoneListCtrl
(placed on the JavaScript file
controllers.js
PhoneListCtrl
$scope
data to our controller function by attaching it to the computer. T
he
scope is a
prototyped descendant of
the root scope, which was created when the application was defined.
The controller scope can be used at all binding locations within the element label
<body ng-controller="PhoneListCtrl">
The concept of a scope is crucial in Angular. S copes can be considered adhesives, allowing templates, modules, and controllers to work together. A ngular uses scopes, as well as information contained in templates, data modules, and controllers, to keep modules and views separate, but synchronized. Any changes to the module affect the view;
To learn more about the Angle scope, see the angular scope documentation.
Separating the controller's "Angular method" from the view makes it easy to test the code as if it were being developed.
If your controller is available in the global namespace, we can simply instantiate it with a simulated
scope
object:
test/e2e/scenarios.js
:
describe('PhoneListCtrl', function(){
it('should create "phones" model with 3 phones', function() {
var scope = {},
ctrl = new PhoneListCtrl(scope);
expect(scope.phones.length).toBe(3);
});
});
Test the
PhoneListCtrl
verify the phone series properties on a scope that contains three records. T
his example demonstrates how easy it is to create a unit test for code in Angular.
Because testing is such a critical part of software development, we make it easy to create tests in Angular so that developers can be encouraged to write them.
In practice, you should not want your controller functions to be in the global namespace.
Instead, you can see that we have registered the controller with an anonymous constructor function on a
phonecatApp
module.
In this case, Angular provides a service that
$controller
which can receive your controller by name.
Here's
$controller
using the code:
test/unit/controllersSpec.js
:
describe('PhoneListCtrl', function(){
beforeEach(module('phonecatApp'));
it('should create "phones" model with 3 phones', inject(function($controller) {
var scope = {},
ctrl = $controller('PhoneListCtrl', {$scope:scope});
expect(scope.phones.length).toBe(3);
}));
});
phonecatApp
module.
$controller
the
inject
into our test function.
$controller
to create an
PhoneListCtrl
Angular likes to use Jasmine's behavior-driven development (BCC) synth. A lthough Angel doesn't ask you to use Jasmine, in this tutorial we wrote all the tests with Jasmine v1.3. You can learn about Jasmine in jasmine's official home page and Jasmine documentation.
The angular-seed project is preprocessed to run unit
tests using Karma,
but you will need to make sure that Karma and its necessary plug-ins are installed.
You can do
rpm install
To run the test, run
rpm test
see what happens to the file.
If you already have one of these browsers installed on your machine, be sure to update Karma's profile before you run the tests.
The local profile
test/karma.conf.js
browsers
For example, if you only have Chrome installed:
... browsers: ['Chrome'], ...
You will see the following or similar output at the terminal:
info: Karma server started at http://localhost:9876/ info (launcher): Starting browser "Chrome" info (Chrome 22.0): Connected on socket id tPUm9DXcLHtZTKbAEO-n Chrome 22.0: Executed 1 of 1 SUCCESS (0.093 secs / 0.004 secs)
Yes! T he test passed! Or failed to pass...
Add another .html the
index.html
For example:
<p>Total number of phones: {{phones.length}}</p>
Create a new module property in the controller and bind it to the module from the template. For example:
$scope.name = "World";
Then add a .html binding to the
index.html
<p>Hello, {{name}}!</p>
Refresh your browser to see if it says "Hello, World!"
Update
./test/unit/controllersSpec.js
to reflect previous changes.
For example, add:
expect(scope.name).toBe('World');
Create
index.html
in index, which structures a simple table:
<table>
<tr><th>row number</th></tr>
<tr ng-repeat="i in [0, 1, 2, 3, 4, 5, 6, 7]"><td>{{i}}</td></tr>
</table>
Now let this 1-based list of
i
to the binding 1.
<table>
<tr><th>row number</th></tr>
<tr ng-repeat="i in [0, 1, 2, 3, 4, 5, 6, 7]"><td>{{i+1}}</td></tr>
</table>
Also note: Try and make an 8x8 table use an additional
ng-repeat
expect(scope.phones.length).toBe(3)
into
toBe(4)
Now you have a dynamic app that splits the modules, vision, and controller components, and you tested them. Now, let's go to Step 3 Filter Iterator to learn how to add a full-text search to your app.