Sei sulla pagina 1di 6

AngularJS - Walkthrough

Setting up Yeoman and its generatorangular


Assuming that you have installed node.js , you can install yeoman as follows:
npm install -g yo
npm install -g generator-angular

Now, we are ready to start working with our application. I will assume that you
created a new directory, called rottenpotatoes , to hold the application code. Let us
create the skeleton of application using the yeoman angular generator. The latter
can be done with the following command:
yo angular --coffee

Please note that we are using the option --coffee because we want to use
coffeescript. Sure enough, if we omit this option, yeomans plugin will generate
javascript code. Additionally, yo uses the name of the current directory as the
name for the application. Thats why you will see some references to
rottenpotatoesApp .
In my case, I was required to clean up npm cache before lauching code
generation with yeoman. The command to do this is the following:
npm cache clean

Yeomans code generator will ask you to customize your project. Lets keep the
default configuration. However, since the code generator install the support to
SASS (i.e., a -simplified- language for specifying CSS stylesheets) you will be
required to install Rubys compass gem. Please be patient because the setup will
take a little while!
You can run the seed application, by means of the following command:

grunt serve

Feature: Listing movies


Lets add our first application feature: support to listing movies. Similar to a Rails
application, that requires adding four elements: a view, a controller, a model and a
route. Please note that the notion of model is blured in the context of front-end
applications, as there is usually no persistence support attached directly to the
application. The above will become clear as we proceed with the implemention of
the Listing movies feature.
Lets add the following:
1. VIEW: An empty file with the name app/views/index.html . Copy the following
snippet to the view:
html
<h1>All movies</h1>
{{movies}}

2. CONTROLLER: A file with the name


app/scripts/controllers/movies_controllers.coffee . Copy the following snippet

to the controller:
'use strict'
app = angular.module('rottenpotatoesApp')
app.controller 'MoviesIndexController', ($scope) ->
$scope.movies = [
{ id: 0, title: 'Aladdin', rating: 'G', release_date: new Date() }
{ id: 1, title: 'Amelie', rating: 'PG', release_date: new Date() }
]

3. MODEL: A file with the name app/scripts/services/movies_services.coffee .


Lets keep this file empty for the moment.
4. ROUTE: We have to manually specify it within the file app/scripts/app.coffee .
Copy the following snipped, just before the line containing the statement
.otherwise :
.when '/movies',
templateUrl: 'views/movies/index.html'
controller: 'MoviesIndexController'

Finally, we have to change the file app/index.html so as to load all the above files
whenever we lauch the application. To this end, you have to add the following lines

by the end of the file:


<script src="scripts/controllers/movies_controllers.js"></script>
<script src="scripts/controllers/movies_services.js"></script>

One important thing to note is the fact that both the Controller and the view share
a reference to the list of movies. In the Controller side, the list of movies is refered
by $scope.movies . In the View side, the list of movies is rendered via the statement
{{movies}} .

Formatting the movies table


Lets now take some time to format the list of movies. To this end, you can copy the
following HTML code in the file views/movies/index.html :
<h2>Listing movies</h2>
<table class="table table-stripped table-bordered">
<thead>
<tr>
<th>Title</th>
<th>Rating</th>
<th>Release code</th>
<th>More Info</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="movie in movies">
<td>{{movie.title}}</td>
<td>{{movie.rating}}</td>
<td>{{movie.release_date}}</td>
<td><a href="">More about {{movie.title}}</a></td>
</tr>
</tbody>
</table>
<a href="#/movies/new">Add a new movie</a>

Of particular interest is the AngularJS directive ng-repeat that allow us to use an


iterator to process the elements in a collection. In our example, we are using
movie in movies to iterate over the list of movies, where movies is once again the
variable declared in the context of MoviesIndexController . Note that for each movie ,
we use a <td></td> element to render each of its properties (e.g., {{movie.title}} ,
etc.).
At the bottom of the web page, there is an <a></a> element that lets us redirect
the application to the web page where we are supposed to provide a form to enter

the information of a new movie.

Feature: Adding a New Movie


We will repeat a process that is similar to the one above:
1. VIEW: Add a file with the name app/views/new.html and copy the following
code:
<h1>New Movie</h1>
<form>
<div>
<label>Title</label>
<input type="text" ng-model="title"/>
</div>
<div>
<label>Rating</label>
<select ng-model="rating" ng-options="r for r in ['G', 'PG', 'PG
-13', 'R']"></select>
</div>
<div>
<label>Release date</label>
<input type="date" ng-model="release_date"/>
</div>
<button ng-click="addMovie()">Save</button>
</form>

2. CONTROLLER: You can add another file for the new controller. However, it is
possible to add the code for the controller in the existing file (i.e.,
movies_controllers.js ). If you opt for adding a new file, then do not forget to
modify index.html to include the new file. The code for the controller will be
introduced a bit later.
3. ROUTE: Add the information about the route to the file scripts/app.coffee , as
follows:
.when '/movies/new',
templateUrl: 'views/movies/new.html'
controller: 'MoviesNewController'

In this case, the view pushes information back to the controller. To this end, the
input elements are annotated with the directive ng-model . For instance, the first
text field captures the information of the movie title via the annotation
ng-model="title" . In the controller side, we will access to that information via
$scope.title .

The directive ng-options="r for r in ['G', 'PG', 'PG-13', 'R'] allows us to specify
a set of options on the drop-down menu that serves to specify the rating of a
movie.
Finally, the directive ng-click="addMovie()" serves a specifying that the method
$scope.addMovie() on the controller will be executed when the button is clicked.

Movies Service
The information held by controllers is transient and is lost whenever we change
from one web page to another. If we use an analogy with Rails, at the beginning of
every controller action, we have to query the Model with the information provided
via the hash params, and only afterwards we would be able to process a request.
The persistence of information is therefore delegated to the model. In Angular, we
will delegate a sort of persistence to services. Note that there a several classes of
services and I will introduce first a very simple one, which will allow us to hold all
the information in main memory. Now, lets take a look at the code (please copy
the snippet into the file scripts/services/movies_services.coffee ).
'use strict'
app = angular.module('rottenpotatoesApp')
app.service 'MoviesService', ->
movies = []
all: ->
movies
add: (movie) ->
movies.push(movie)

As you can see, the service uses an array (i.e., movies ) on which will store all the
movies. The service also provides two methods: all() that returns all the movies,
and add() that adds a new movie at the end of the array.
Now we have to wire up the service with the controllers that we have defined.
First, lets change the implementation of MoviesIndexController (in the file
scripts/controller/movies_controllers.coffee ).
'use strict'
app = angular.module('rottenpotatoesApp')
app.controller 'MoviesIndexController', ['$scope', 'MoviesService', ($scope, M
oviesService) ->
$scope.movies = MoviesService.all()
]

Note the changes in the syntax. First, all the declaration of the controller is now

Note the changes in the syntax. First, all the declaration of the controller is now
packaged as an array. Within this array, we can find a list of strings corresponding
with the components we expect that the angularjs dependency injector will provide
(namely, $scope and MoviesService ). Note that we are repeating the same list now
as the list of parameters of the anonymous function that defines the behavior of
the controller. Finally, you can see that now we replaced the literal array that we
were using for initializing $scope.movies . The same variable is now set to the value
returned by MoviesService.all() .
Let us now introduce the implementation of MoviesNewController . Note that I
decided to put both of the controllers in the same file (but you can certainly
separate them). Copy the following code at the end of the file
scripts/controllers/movies_controllers.coffee .
app.controller 'MoviesNewController', ['$scope', '$location', 'MoviesService',
($scope, $location, MoviesService) ->
$scope.addMovie = ->
MoviesService.add {title: $scope.title, rating: $scope.rating, release_da
te: $scope.release_date}
$location.path '/movies'
]

As you can see, this controller also relies on MoviesService . This service as well as
the references to $scope and $location are by dependency injection. This
controller provides only the implementation to the method addMovie() which is
launched by clicking the Save button in the new.html view. Please note that all the
information captured by the form is available via $scope related variables (e.g.,
$scope.title , etc.).
The last addition is the call $location.path '/movies' that redirects the control to
the path /movies .

Potrebbero piacerti anche