By RAONI BOAVENTURA - Javascript Engineer
#AngularJS #JavaScript #MVC
<body ng-app="F1FeederApp" ng-controller="driversController">
<table>
<thead>
<tr><th colspan="4">Drivers Championship Standings</th></tr>
</thead>
<tbody>
<tr ng-repeat="driver in driversList">
<td>{{$index + 1}}</td>
<td>
<img src="img/flags/{{driver.Driver.nationality}}.png" />
{{driver.Driver.givenName}} {{driver.Driver.familyName}}
</td>
<td>{{driver.Constructors[0].name}}</td>
<td>{{driver.points}}</td>
</tr>
</tbody>
</table>
</body>
{{ 1 + 1 }}
{{ 946757880 | date }}
{{ user.name }}
La directriz ng-app
es responsable de hacer bootstrapping a tu aplicación, para definir
el ámbito de ésta. En AngularJS, puedes tener múltiples aplicaciones dentro de la misma página, por lo
que esta directriz define el lugar donde comienza y termina cada aplicación.
La directriz ng-controller
define qué controlador estará a cargo de tu vista. En este
caso, la denotamos driversController
, la cual proporcionará nuestra lista de conductores
(driversList
).
La directriz ng-repeat
es una de las más utilizadas, y sirve para definir tu alcance de
plantilla al pasar a través de colecciones. En el ejemplo anterior, repite una línea en la tabla por
cada conductor en driversList
.
controllers.js
:angular.module('F1FeederApp.controllers', []).
controller('driversController', function($scope) {
$scope.driversList = [
{
Driver: {
givenName: 'Sebastian',
familyName: 'Vettel'
},
points: 322,
nationality: "German",
Constructors: [
{name: "Red Bull"}
]
},
{
Driver: {
givenName: 'Fernando',
familyName: 'Alonso'
},
points: 207,
nationality: "Spanish",
Constructors: [
{name: "Ferrari"}
]
}
];
});
$scope
se supone que debe enlazar tu controlador y vistas. En particular, lleva todos los
datos que se utilizarán dentro de la plantilla.
Todo lo que se agrega a ella (como la driversList
del ejemplo anterior) será
directamente accesible en tus vistas. Por ahora, vamos a trabajar con una matriz de datos ficticios
(estática), que vamos a sustituir más tarde con nuestro servicio API.angular.module('F1FeederApp', [
'F1FeederApp.controllers'
]);
app.js
) más adelante.
index.html
:<!DOCTYPE html>
<html>
<head>
<title>F-1 Feeder</title>
</head>
<body ng-app="F1FeederApp" ng-controller="driversController">
<table>
<thead>
<tr><th colspan="4">Drivers Championship Standings</th></tr>
</thead>
<tbody>
<tr ng-repeat="driver in driversList">
<td>{{$index + 1}}</td>
<td>
<img src="img/flags/{{driver.Driver.nationality}}.png" />
{{driver.Driver.givenName}} {{driver.Driver.familyName}}
</td>
<td>{{driver.Constructors[0].name}}</td>
<td>{{driver.points}}</td>
</tr>
</tbody>
</table>
<script src="bower_components/angular/angular.js"></script>
<script src="bower_components/angular-route/angular-route.js"></script>
<script src="js/app.js"></script>
<script src="js/services.js"></script>
<script src="js/controllers.js"></script>
</body>
</html>
$http
y $resource
.
El primero es una capa en la parte superior de XMLHttpRequest o
JSONP, mientras que el último
proporciona un mayor nivel de abstracción. Vamos a utilizar $http
.
$http
al añadirlo a nuestro services.js
:angular.module('F1FeederApp.services', []).
factory('ergastAPIservice', function($http) {
var ergastAPI = {};
ergastAPI.getDrivers = function() {
return $http({
method: 'JSONP',
url: 'http://ergast.com/api/f1/2013/driverStandings.json?callback=JSON_CALLBACK'
});
}
return ergastAPI;
});
F1FeederApp.services
)
y registramos un servicio dentro de ese módulo (F1FeederApp.services
). Nótese
que pasamos $http como parámetro a ese servicio.
Esto le dice al motor de inyección de
dependenciade Angular, que nuestro nuevo servicio
requiere (o depende) del servicio $http
.app.js
,
reemplazando nuestro código existente con:angular.module('F1FeederApp', [
'F1FeederApp.controllers',
'F1FeederApp.services'
]);
controller.js
un poco,
integrar ergastAPIservice
como una dependencia, y estaremos listos para continuar:angular.module('F1FeederApp.controllers', []).
controller('driversController', function($scope, ergastAPIservice) {
$scope.nameFilter = null;
$scope.driversList = [];
ergastAPIservice.getDrivers().success(function (response) {
//Dig into the responde to get the relevant data
$scope.driversList = response.MRData.StandingsTable.StandingsLists[0].DriverStandings;
});
});
nameFilter
a nuestro alcance. Vamos a poner esta variable en uso.index.html
,
justo debajo de la etiqueta <body>
:<input type="text" ng-model="nameFilter" placeholder="Search..."/>
ng-model
. Esta directriz une nuestro campo de
texto a la variable $scope.nameFilter
y se asegura de que su valor esté siempre al día con el valor de entrada. Ahora, vamos a
visitar index.html
una vez más y hagamos un pequeño ajuste en la línea que contiene la directriz
ng-repeat
:<tr ng-repeat="driver in driversList | filter: nameFilter">
ng-repeat
que, antes de dar salida a los datos, la matriz
driversList
debe ser filtrada por el valor almacenado en nameFilter
.$scope.nameFilter
que asociamos a él se actualice con el nuevo valor. Dado que binding
funciona en ambos sentidos, el momento en el que el valor
nameFilter
se actualiza, la segunda directriz asociada a la misma (es decir,
ng-repeat
) también recibe el nuevo valor y la vista se
actualiza inmediatamente.
Driver.givenName
y Driver.familyName
: En primer lugar,
añadimos a driversController
, justo por debajo de la línea
$scope.driversList =[];
:
$scope.searchFilter = function (driver) {
var keyword = new RegExp($scope.nameFilter, 'i');
return !$scope.nameFilter || keyword.test(driver.Driver.givenName) || keyword.test(driver.Driver.familyName);
};
ng-repeat
:<tr ng-repeat="driver in driversList | filter: searchFilter">
$routeProvider
(en app.js
) lo que
nos ayudará a lidiar con estas variadas rutas de aplicación. A continuación,
añadiremos dos de estas rutas: una para la tabla del campeonato y otro para los datos del
conductor. Aquí est´ nuestra nueva app.js
:angular.module('F1FeederApp', [
'F1FeederApp.services',
'F1FeederApp.controllers',
'ngRoute'
]).
config(['$routeProvider', function($routeProvider) {
$routeProvider.
when("/drivers", {templateUrl: "partials/drivers.html", controller: "driversController"}).
when("/drivers/:id", {templateUrl: "partials/driver.html", controller: "driverController"}).
otherwise({redirectTo: '/drivers'});
}]);
http://domain/#/drivers
cargará
el driversController
y buscará la vista parcial que se va a renderizar en
partials/drivers.html
.
¡Pero espera! No tenemos ninguna vista parcial todavía, ¿verdad? Vamos a tener
que crearlas también.ng-view
, modificando nuestra index.html
para reflejar lo
siguiente:<!DOCTYPE html>
<html>
<head>
<title>F-1 Feeder</title>
</head>
<body ng-app="F1FeederApp">
<ng-view></ng-view>
<script src="bower_components/angular/angular.js"></script>
<script src="bower_components/angular-route/angular-route.js"></script>
<script src="js/app.js"></script>
<script src="js/services.js"></script>
<script src="js/controllers.js"></script>
</body>
</html>
<ng-view>
.
Lo &ucute;nico que tenemos que hacer es crear un archivo con el nombre partials/drivers.html
,
y poner nuestra tabla de campeonato HTML allí. También vamos a utilizar esta
oportunidad para vincular el nombre del conductor a nuestra ruta de los detalles del conductor:<input type="text" ng-model="nameFilter" placeholder="Search..."/>
<table>
<thead>
<tr><th colspan="4">Drivers Championship Standings</th></tr>
</thead>
<tbody>
<tr ng-repeat="driver in driversList | filter: searchFilter">
<td>{{$index + 1}}</td>
<td>
<img src="img/flags/{{driver.Driver.nationality}}.png" />
<a href="#/drivers/{{driver.Driver.driverId}}">
{{driver.Driver.givenName}} {{driver.Driver.familyName}}
</a>
</td>
<td>{{driver.Constructors[0].name}}</td>
<td>{{driver.points}}</td>
</tr>
</tbody>
</table>
services.js
, lo siguiente:angular.module('F1FeederApp.services', [])
.factory('ergastAPIservice', function($http) {
var ergastAPI = {};
ergastAPI.getDrivers = function() {
return $http({
method: 'JSONP',
url: 'http://ergast.com/api/f1/2013/driverStandings.json?callback=JSON_CALLBACK'
});
}
ergastAPI.getDriverDetails = function(id) {
return $http({
method: 'JSONP',
url: 'http://ergast.com/api/f1/2013/drivers/'+ id +'/driverStandings.json?callback=JSON_CALLBACK'
});
}
ergastAPI.getDriverRaces = function(id) {
return $http({
method: 'JSONP',
url: 'http://ergast.com/api/f1/2013/drivers/'+ id +'/results.json?callback=JSON_CALLBACK'
});
}
return ergastAPI;
});
controllers.js
:angular.module('F1FeederApp.controllers', []).
/* Drivers controller */
controller('driversController', function($scope, ergastAPIservice) {
$scope.nameFilter = null;
$scope.driversList = [];
$scope.searchFilter = function (driver) {
var re = new RegExp($scope.nameFilter, 'i');
return !$scope.nameFilter || re.test(driver.Driver.givenName) || re.test(driver.Driver.familyName);
};
ergastAPIservice.getDrivers().success(function (response) {
//Digging into the response to get the relevant data
$scope.driversList = response.MRData.StandingsTable.StandingsLists[0].DriverStandings;
});
}).
/* Driver controller */
controller('driverController', function($scope, $routeParams, ergastAPIservice) {
$scope.id = $routeParams.id;
$scope.races = [];
$scope.driver = null;
ergastAPIservice.getDriverDetails($scope.id).success(function (response) {
$scope.driver = response.MRData.StandingsTable.StandingsLists[0].DriverStandings[0];
});
ergastAPIservice.getDriverRaces($scope.id).success(function (response) {
$scope.races = response.MRData.RaceTable.Races;
});
});
$routeParams
en el
controlador del conductor.
Este servicio nos permitirá acceder a nuestros parámetros de URL (para el :id, en este caso)
utilizando $routeParams.id
.partials/driver.html
y agregamos:<section id="main">
<a href="./#/drivers"><- Back to drivers list</a>
<nav id="secondary" class="main-nav">
<div class="driver-picture">
<div class="avatar">
<img ng-show="driver" src="img/drivers/{{driver.Driver.driverId}}.png" />
<img ng-show="driver" src="img/flags/{{driver.Driver.nationality}}.png" /><br/>
{{driver.Driver.givenName}} {{driver.Driver.familyName}}
</div>
</div>
<div class="driver-status">
Country: {{driver.Driver.nationality}} <br/>
Team: {{driver.Constructors[0].name}}<br/>
Birth: {{driver.Driver.dateOfBirth}}<br/>
<a href="{{driver.Driver.url}}" target="_blank">Biography</a>
</div>
</nav>
<div class="main-content">
<table class="result-table">
<thead>
<tr><th colspan="5">Formula 1 2013 Results</th></tr>
</thead>
<tbody>
<tr>
<td>Round</td> <td>Grand Prix</td> <td>Team</td> <td>Grid</td> <td>Race</td>
</tr>
<tr ng-repeat="race in races">
<td>{{race.round}}</td>
<td><img src="img/flags/{{race.Circuit.Location.country}}.png" />{{race.raceName}}</td>
<td>{{race.Results[0].Constructor.name}}</td>
<td>{{race.Results[0].grid}}</td>
<td>{{race.Results[0].position}}</td>
</tr>
</tbody>
</table>
</div>
</section>
ng-show
. Esta directriz
sólo mostrará el elemento HTML si la expresión proporcionada es true
(es
decir, ni false
, ni null
).
En este caso, el avatar sólo aparecerá una vez que el objeto conductor ha sido cargado en el
alcance, por el controlador.index.html
,
para mejorar las capacidades de navegación del usuario. Las posibilidades son infinitas.Encuentralo Aquí