Segundo ejercicio con controller en AngularJS

  • Por
Crearemos una aplicación sencilla con la intención de practicar con controller en AngularJS, inicializando datos en el Scope, creando manejadores de eventos, etc.

Vamos a hacer un ejercicio práctico de todo lo que venimos aprendiendo con AngularJS y donde afianzar particularmente lo aprendido sobre la creación de controladores .

El ejercicio es muy sencillo en realidad. Es un "acumulador" en el que tenemos un campo de texto para escribir una cantidad y un par de botones con operaciones de sumar y restar. Luego tenemos un contador que se va incrementando con esas operaciones de sumas y restas sobre un total. En si no sirve para mucho lo que vamos a construir, lo hacemos más bien con fines didácticos.

Puedes ver el ejercicio en marcha en Codepen antes de pasar a estudiar y explicar el código con el que se ha hecho.

Código HTML

Veamos primero la parte del HTML usada para resolver este ejercicio.

<div ng-app="acumuladorApp" ng-controller="acumuladorAppCtrl as vm">
  <h1>Acumulador</h1>
  <h2>Control de operación:</h2>
  ¿Cuánto? <input type="text" ng-model="vm.cuanto" size="4" />
  <br />
  <input type="button" value="+" ng-click="vm.sumar()"/>
  <input type="button" value="-" ng-click="vm.restar()"/>
  <h2>Totales:</h2>
  En el acumulador llevamos <span>{{vm.total}}</span>
</div>

Voy describiendo los detalles más importantes que debes apreciar.

  • Al hacer el bootstrap (arranque) de la aplicación (directiva ngApp) se indica el nombre del módulo: "acumuladorApp".
  • Se indica el nombre del controlador con la directiva ngController y el valor "acumuladorAppCtrl".
  • Con la sintaxis de "acumuladorAppCtrl as vm" indicamos que el scope dentro del espacio de etiquetas marcado para este controlador, se conocerá por "vm". Podríamos llamar como deseásemos al scope, en lugar "vm", en definitiva es como una variable donde tendremos propiedades y métodos. En otras palabras, se ha creado un namespace (espacio de nombres) para los datos que nos vienen del modelo (scope) gracias al controlador "acumuladorAppCtrl".
  • Puedes llamar de cualquier manera también tanto a módulo como a controlador, pero se usan esos por convención.
  • El campo INPUT de texto tiene una directiva ngModel para decirle a AngularJS que ese es un dato del modelo. Fítate que el nombre del dato en el modelo se accede a través del espacio de nombres definido en el controlador: "vm.cuanto".
  • Luego encuentras dos INPUT tipo button que me sirven para realizar la acción de acumular, positiva o negativamente. Ambos tienen una directiva ngClick que nos sirve para expresar lo que debe ocurrir con un clic. Lo interesante aquí es que llamamos a dos funciones que están definidas en el scope, mediante el espacio de nombres "vm", nuestro modelo. El código de esos métodos (funciones que hacen en este caso de manejadores de eventos) ha sido definido en el controlador, lo veremos más adelante.
  • Por último encontramos un {{vm.total}} que es un dato que estará en el scope y en el que llevamos la cuenta de todo lo que se ha sumado o restado con el acumulador.

Nota: Por si no te has dado cuenta, el espacio de nombres donde tendremos al scope se ha nombrado como "vm". Ya dijimos que se puede usar cualquier nombre de varialble, eso es lo de menos, lo que queremos que se note es que "vm" son las siglas de "View Model", osea, el "modelo de la vista". Es algo que tiene que ver con el paradigma MVC en la variante en la que trabajamos con Angular. Hablaremos de eso más adelante.

Código Javascript

Ahora pasemos a la parte donde codificamos nuestro Javascript para darle vida a este ejercicio.

angular
  .module('acumuladorApp', [])
  .controller("acumuladorAppCtrl", controladorPrincipal);

function controladorPrincipal(){
  //esta función es mi controlador
  var scope = this;
  scope.total = 0;
  scope.cuanto = 0;

  scope.sumar = function(){
    scope.total += parseInt(scope.cuanto);
  }
  scope.restar = function(){
    scope.total -= parseInt(scope.cuanto);
  }
};

Vamos describiendo las principales partes del código.

  • Con "angular" en la primera línea accedo a la variable que me crea angular, disponible para acceder a las funcionalidades del framework.
  • En la segunda línea creamos el módulo, indicando el nombre (que es igual a lo que se puso como valor en la directiva ngApp del HTML) y el array de las dependencias, de momento vacío.
  • En la tercera línea, con un encadenamiento (chaining) definimos el controlador. Indicamos como primer parámetro el nombre del controlador, definido en la directiva ngController del HTML, y como segundo parámetro colocamos la función que se encargará de construir el controlador. Indicamos el nombre de la función simplemente, sin los paréntesis, pues no es una llamada a la función sino simplemente su nombre.
  • Luego se define la función del controlador. Esa función es capaz de escribir datos en el scope, así como métodos.
  • En la función accedemos al scope por medio de "this". Fíjate que en la primera línea de la función tienes var scope = this; esto es simplemente opcional y se puede hacer para mantener la terminología de AngularJS de llamar scope a lo que vas generando en el controlador, pero podría perfectamente referirme a él todo el tiempo con "this".
  • En el scope inicializo dos valores, total y cuanto, mediante scope.total=0 y scope.cuanto=0.
  • Luego genero dos métodos que usaremos para los manejadores de eventos de los botones de sumar y restar. Esas funciones tienen el scope disponible también y en su código se accede y modifica a los datos del scope.

Nota: También hay varias aproximaciones para definir este Javascript con los modulos y controladores de Angular. Es recomendable la creación de una envoltura para tu Javascript, por medio de una función que se autoinvoca. Esto lo veremos más adelante, puesto que no queremos complicar más el código todavía.

Con eso tenemos el ejercicio completo, todo lo demás para que esto funcione es tarea de AngularJS. De manera declarativa en el HTML hemos dicho qué son las cosas con las que se va a trabajar en la aplicación y luego hemos terminado de definir e inicializar los datos en el controlador, así como escribir en código las funcionalidades necesarias para que el ejercicio tome vida.

Variante de este mismo ejercicio pero sin "controller as"

En la resolución, que hemos comentado antes, a este ejercicio hemos usado una alternativa de la directiva de ngController en la que se le asigna un espacio de nombres al scope "acumuladorAppCtrl as vm". Esto se conoce habitualmente como "controller as" y ya comentamos en el artículo anterior dedicado a los controladores que es algo relativamente nuevo y que muchas veces la codificación que encontrarás en otros textos es un poco diferente.

Solo a modo de guía para quien está acostumbrado a trabajar de otra manera, y para que entiendas otros códigos antiguos que podrás encontrar en otras guías de Angular, pongo aquí el código de este mismo ejercicio pero sin el "controller as".

El código HTML

<div ng-app="acumuladorApp" ng-controller="acumuladorAppCtrl">
  <h1>Acumulador</h1>
  <h2>Control de operación:</h2>
  ¿Cuánto? <input type="text" ng-model="cuanto" size="4" />
  <br />
  <input type="button" value="+" ng-click="sumar()"/>
  <input type="button" value="-" ng-click="restar()"/>
  <h2>Totales:</h2>
  En el acumulador llevamos <span>{{total}}</span>
</div>

El código Javascript

var acumuladorApp = angular.module('acumuladorApp', []);
acumuladorApp.controller("acumuladorAppCtrl", ["$scope", function($scope){
  //esta función es mi controlador
  //var $scope = this;
  $scope.total = 0;
  $scope.cuanto = 0;
  
  $scope.sumar = function(){
    $scope.total += parseInt($scope.cuanto);
  }
  $scope.restar = function(){
    $scope.total -= parseInt($scope.cuanto);
  }
}]);

No comento el código más, pues es muy parecido al anterior, simplemente se deja de usar el espacio de nombres y al definir la función del controlador se inyecta el $scope de otra manera. Te dejo a ti la tarea de encontrar todas las diferencias.

Autor

Miguel Angel Alvarez

Miguel es fundador de DesarrolloWeb.com y la plataforma de formación online EscuelaIT. Comenzó en el mundo del desarrollo web en el año 1997, transformando su hobby en su trabajo.

Compartir

Comentarios

Manolete

08/10/2014
Good
Que bueno que viniste pibe!!

sergio_razza

24/10/2014
Consulta sobre $scope
Estoy empezando con angular y en varios lados vi que se inyecta el $scope y en otros que se usa como en el primer ejemplo haciendo var scope = this, o bien usando directamente this. Quería saber cual de estas dos formas de trabajo es decir inyectar el $scope o usar el this es la recomendada? muchas gracias. Saludos

Andres Lara

25/9/2015
DUDAS
Hola respecto al ejercicio y explicación muy bueno, pero tengo unas dudas, la función del controlador no debería devolver el valor de total?? como return total o algo similar.

Gabriel

06/1/2016
Pregunta Respecto al Ejercicio
Excelente ejemplo, anda de maravilla!
Una consulta, como se podria hacer que el ejercicio cumpla la misma funcion de "acumular" pero que en vez de pulsar un boton de suma o resta, se acumule automaticamente, sin pulsar ningun boton, que se actualize a medidad que se van ingresando nuevos numeros. Se podria realizar? Muchas Gracias desde ya.

Hernan Rivero

27/7/2016
Error
Hola, estoy aprendiendo AngularJs siguiendo su manual, estoy en la sección 12 del tutorial. Hice el ejercicio por mi cuenta y me daba un error, al no poder resolverlo, decidi crear un nuevo proyecto y copiar los dos pedazos de código que acá proveen (los dos ejemplos) y en ambos me da el mismo error!!! puede que me este faltando algo???? cuando ejecuto la aplicación, esta sección me da error
<h2>Totales:</h2>
En el acumulador llevamos <span>{{vm.total}}</span>
el campo vm.total se muetra en el html con las dos llaves, es decir lo muestra como un texto del html, que podrá ser?
Muchas gracias.