Usando ControllerAs en AngularJS

Estaba leyendo a John Papa, particularmente su guia de estilo para AngularJS. Según dicen algunos seguir esta guía hará que el paso a Angular 2.0 no sea tan difícil. Después de leerlo completamente ademas de eso considero que seguir esta guía nos devuelve un código mucho mas entendible. Uno de los puntos que mas me gusta es el de usar Controller As para evitar el uso de $scope. Así que agarre el ejemplo de upload de imágenes en angular y me puse manos a la obra para modificar el controlador.

Lo primero fue definir el controllerAs en el routeProvider y luego definir la función del controlador, no dejarla como una funciona anónima. Para ello solo debíamos hacer algunos cambios a la forma en que llamábamos al controlador, aproveche y cambie también la forma en que se hace la inyección de dependencias siguiendo lo que se recomienda John, de esta forma me quedo algo asi:

La definición de la ruta:

$routeProvider.when('/', {
 templateUrl: 'view1/view1.html',
 controller: 'VistaCtrl',
 controllerAs:'vista'
 })

Notese el controllerAs… con esto y con algunos cambios en el controlador vamos a acceder a las variables mediante {{vista.loEsteDefinido}} quedando, creo yo bastante bien. Ademas nos ayuda a evitar colisiones cuando se superponen controladores en una vista.

En donde definí el controlador solo tengo lo siguiente:

.controller('VistaCtrl', VistaCtrl);

Básicamente le estoy diciendo que el controlador VistaCtrl sera la funcion VistaCtrl. Ahora solo nos queda definir la función y hacer la inyección de dependencias:

VistaCtrl.$inject=['$scope', '$http'];
function VistaCtrl($scope, $http) {
 var vm = this;
/* Mas codigo */
};

La primer linea es la inyección y la segunda la definición de la función, como seguramente ya notaron. Entonces en todos los lugares en donde yo definia $scope.variable lo cambie por vm.variable (vm es por viewModel)…  Por ejemplo la función que envía la imagen quedo definida así:

vm.enviar=function() { /*Codigo*/}

Por ultimo en la vista ya no se accede con el nombre definido con $scope… ahora para acceder a la función o a cualquier otra variable definida dentro del controller utilizo vm.variable…

El uso de vm=this; es interesante, ya que se utiliza para evitar colisiones cuando definimos funciones dentro del controller lo que puede hacer que se pise el this…

Pueden ver el código en el repositorio de github… Allí voy a seguir subiendo algunas cosas según valla aprendiendo y teniendo tiempo de aplicar los cambios.

¿Que sigue?

Bueno… separar cada cosa en su propio archivo, no me terminaba de gustar la forma en que armaba todo angular seed y luego de leer la guia creo que me inclino mas por un archivo para cada cosa.

Modificar la directiva para que siga la misma convención de definir la función correctamente. Y hacer lo mismo con los servicios y factorys, que, de hecho, no se utilizaron la primer versión del upload de imágenes, pero que pretendo usarlas para dejar los controllers mas claros.

Upload de imagenes o archivos con AngularJS

Uno de los últimos proyectos en los que estuve involucrado fue una aplicación MEAN (MongoDB, Express, AngularJS y NodeJS). Funcionaba bastante bien. Pero nos encontramos con el problema de que debíamos subir una imagen… AngularJS no es muy amigo de los post Multipart ni de subir archivos. Ya sabemos que intenta serializar todo generar un json.

La solución que se encontró en un principio fue hacer un POST normal, de esos de toda la vida, no usar Angular para realizarlo, funcionaba, pero no era del todo de mi agrado, al final habíamos hecho una aplicación REST y fallábamos en una simple subida de archivos.

Después de mucho navegar, y con ayuda de estos posts pude entender en que nos estábamos equivocando y que era lo que se debía hacer para poder subir archivos con AngularJS… Veamos en detalle.

En primer lugar a un input tipo file si le asignamos un ng-model angular no va a detectar el cambio y por lo tanto nuestro ng-model siempre queda vacio (o por lo menos eso es lo que me pareció a mi); no vamos a poder hacer el POST de la imagen o el archivo que estamos intentando subir. Para hacerlo la mejor solución que encontré fue la de usar una directiva (benditas directivas, solo debemos aprender a utilizarlas para aumentar exponencialmente nuestra productividad con Angular). La directiva dice:

.directive('fileModel', ['$parse', function ($parse) {
    return {
        restrict: 'A',
        link: function(scope, element, attrs) {
            var model = $parse(attrs.fileModel);
            var modelSetter = model.assign;

            element.bind('change', function(){
                scope.$apply(function(){
                    modelSetter(scope, element[0].files[0]);
                });
            });
        }
    };
}])

Allí básicamente le estamos diciendo a Angular que se fije en los elementos con “atributo” file-model y que ante un cambio le setee el archivo como atributo de modelo con el primer archivo que se selecciono (Porque interpreta que tenemos un array de archivos).

De esta forma mi formulario para subida de imágenes quedo así:

<form action="">
    <input type="file" file-model="imagen">
    <button type="button" ng-click="enviar()">Enviar</button>
</form>

No tiene ninguna ciencia ¿no? ahora ya podemos asociar un archivo a un modelo para luego trabajar con angularjs.

El siguiente problema con el que me encontré fue con el subir el archivo propiamente dicho al servidor. Como dije antes el framework intenta armar un json con cualquier cosa que le pasemos, por lo tanto debemos hacer una transformación del Request para que no intente serializar nuestra imagen. A su vez para hacer el post con $http.post necesitamos los datos de un formulario, para ello usamos FormData…. El código que me quedo es así:

    $scope.enviar=function() {
        var file = $scope.imagen;
        var fd = new FormData();
        fd.append('file', file);
        $http.post('post.php', fd, {
            transformRequest: angular.identity, 
            headers: {'Content-Type': undefined}
            })
            .success(function(response){
                //Guardamos la url de la imagen y hacemos que la muestre.
                $scope.imagen=response;
                $scope.img=true;
            })
            .error(function(response){

        });
        }

Tiene algunas cosas de mas, ya lo se, y algunas de menos también, pero la funcionalidad básica es esa. Creamos un nuevo FormData y hacemos el $http.post pasandole la url a la que le vamos a pegar y los datos del formulario luego hacemos el  “transformRequest: angular.identity” que básicamente le dice a AngularJS que deje los datos tal cual se los estamos pasando. Lo demás es la gestión después de haber intentado subir la imagen al servidor. Incluso tenemos una pequeña gestión de errores (que ahora no hace nada).

Pueden ver el código, que seguramente voy a retocar un poco, ya que ahora es solo una prueba de concepto y no tienen ningún tipo de seguridad ni manejo de errores, en github: https://github.com/Ferticidio/UploadAngular . Acepto ideas, comentarios y criticas que nos lleven a mejorar el código, creo que mas de uno se habrá encontrado con el mismo problema que yo…

Buscando trabajo

Ya comenzó el 2015. Es hora de ponerse las pilas y buscar nuevos horizontes laborales. En realidad para mi el 2015 empieza dentro de un par de días… Hoy sigo disfrutando de los beneficios de trabajar en el estado. Espero que el próximo cambio de año me encuentre extrañando los feriados y los asuetos, pero feliz de haber hecho un cambio tan significativo en mi vida.

Ya estoy recibido, lo que en mi rubro no significa demasiado, hay que pagar el derecho de piso, como dicen, demostrar que lo que uno estudio lo puede aplicar…. y aprender, aprender mucho, porque tres años de carrera son solo el punta pie inicial y luego todos los días uno debe aprender cosas nuevas.

Desde chico las cosas monótonas y que no sean un reto me aburren… en parte por eso creo que el desarrollo de software es lo mio. Cada día un problema nuevo que resolver algo nuevo que estudiar y sentirte al final del día que hice algo significativo.

Así que a partir de hoy empieza mi bus queda laboral… no tengo urgencia, por suerte tengo trabajo un trabajo estable, pero ya cumplí mi ciclo dentro del registro (8 años), así que despacio pero sin pausa busco algo nuevo para mi.

Me recibi!

Amigos… Estoy recibido… El viernes rendi mi ultimo final, me saque una 10. Con ese final termina una epoca muy linda de mi vida. Conoci gente muy interesante, hice un puñado de amigos que espero mantener y se me abrieron las puertas del futuro….

Fueron 3 años muy duros… de casi no ver a mi familia, de levantarme temprano y acostarme tarde. Pero valio la pena. Hoy estoy recibido y puedo volver a dedicarle el tiempo que se merecen tanto a mi hija como a mi señora.

Estos 15 dias que quedan del año los voy a dedicar a evaluar como voy a seguir, tengo mucho en que pensar, por un lado ver si voy a seguir jugando a ser un emprendedor web o dedicarme de lleno a la programacion o, incluso, resignarme a ser un empleado mas…

Usando Callbacks en JavaScript

Cuando usamos JavaScript muchas veces necesitamos coordinar la ejecución de ciertas partes del código. JS al ser un lenguaje asincronico no espera a que se termine de ejecutar una sentencia para comenzar con la otra.

Cuando el éxito de nuestro programa depende del resultado de una sentencia previa debemos utilizar métodos para sincronizar la ejecución. Los callbacks se pueden utilizar para eso. Básicamente con ellos le decimos a una función que cuando termine de ejecutarse ejecute otra. Les dejo un ejemplo que subí a Gist para poder verlo en acción.

Como pueden ver es una forma muy simple y a la vez muy poderosa para la sincronizar la ejecución de diferentes funciones. ¿Usaron callbacks alguna vez?