Ng2Emulation – Código Angular 1.4+ en TypeScript usando el estilo de Angular 2

You can read this post in english here.

He parido una “cosa” que he llamado: Ng2Emulation, vamos al lío.

Antecendentes

Después de dos días mirándome Angular 2 ya me voy coscando de como va el asunto y la verdad es que lo que veo me gusta, ya había visto algo parecido con Aurelia y no se si me gusta por que es nuevo, como todo lo nuevo. Lo que si he recordado es que no estaba tan equivocado hace ya un año cuando escribí mi artículo Rip controllers en este blog.

Bien, tengo claro es que Angular es un buen compañero de viaje en el proyecto principal de la empresa donde trabajo. Nos ahorra mucho tiempo de desarrollo y en cierto modo me ayuda a controlar a la gente y que no se salgan de un estilo de programación.
La cosa viene por que estoy empezando un nuevo proyecto consistente en cambiar la interfaz de usuario de nuestro ERP, que ahora mismo es un monstruo no pequeño en ASP.NET WebForms y hay una pequeña parte en MVC, la tarea nos llevará mucho trabajo y el proyecto no tiene fin, esto sigue y seguirá creciendo, el ERP tiene ya 8 años y pinta que va a seguir así, pero le toca un lavado de cara.

Sin entrar en detalles de porqué, dimes y diretes, nos hemos decidido por Angular en el browser. Por supuesto Angular 1.x, el Angular 2 está muy verde y cambiará mucho, pero si viene bien en un proyecto tan longevo mirar al futuro para estar preparado para cualquier cosa que pueda venir, así pues he decidido introducir técnicas descritas en muchas guías de migración de Angular 1.x a 2 para preparar la llegada de Angular 2; lo que viene siendo usar la notación “as” para los controladores, usar “controllerAs”, “bindToController” e isolated scopes en las directivas, …
Como he tenido buenos maestros en Angular, jeje, se que estos cambios no me va a costar mucho por que ya trabajaba de una forma parecida con Angular, pero mirando como se implementan los decoradores en TypeScript se me ocurrió una idea mejor, emular Angular 2.

En busca de diferencias

Lo primero que le choca a la gente cuando ve Angular 2 son los imports, decorators, tipos y demás cosas de TypeScript, pero en realidad la cosa no ha cambiado tanto. Yo hace unos 3 años que desarrollo en TypeScript en vez de JavaScript y al conocerlo y saber las diferencias con JavaScript no me sorprende tanto.
Mirando como se definían los componentes en Angular 2 y como se definen las directivas en Angular 1 te puedes dar cuenta de que hay equivalencias, es decir, la lógica está en un sitio y la definición, el DDO, en otra. Entonces mirando esto:

var app = angular.module("app", []);;
 
export class DirectiveController {
    static $inject = [];
    texto: string;
    constructor() {
        this.texto = "Saludos desde el controlador de la directiva.";
    }
}
 
app.directive("miDirectiva", () => {
    return {
        scope: {},
        template: "<div>{{vm.texto}}<div>",
        controllerAs: "vm",
        bindToController: {
            texto: "="
        },
        controller: DirectiveController
    };
});

y mirando esto:

@Component({
    selector: "mi-directiva",
    template: "<div>{{vm.texto}}<div>",
    controllerAs: "vm",
    bindToController: {
        texto: "="
    }
})
export class myComponent {
    static $inject = [];
    texto: string;
    constructor() {
        this.texto = "Saludos desde el controlador de la directiva.";
    }
}
 
bootstrap(MyComponent)

Se me ocurre que lo que marca la diferencia son los @decoradores, así pues me hago preguntas … ¿como funcionan los decoradores? ¿podría programarme decoradores propios para almacenar meta información (meta-data) sobre una clase y luego acceder a estos decoradores a través de la clase para obtener esa metadata?

google(“typescript custom decorators”) =>

De puta madre, entonces lo que podemos hacer es crearnos unos decoradores para guardar esos meta-datos, pero … ¿y si llamo a esos @decorators igual que los llama Angular 2 e intento seguir el mismo estilo? podría programar en Angular 1 al estilo de Angular 2 ??

Exactamente eso he hecho en mi proyecto Ng2Emulation

Intentar emular el estilo de construcción de los componentes de Angular 2 para poder usarlos en Angular 1 y dar así un paso de gigante a la hora de migrar a Angular 2.

Por supuesto que las vistas seguirán funcionando como en Angular 1, intentar dar también ese paso sería ya liarse mucho, pero con lo poco que nos ha costado pensar y hacer Ng2Emulation nos ahorramos tener que migrar muchos controladores, directivas y servicios.

Emulación de Angular 2

Ha fecha de hoy (03/11/2015) tan solo hay creados y funcionando los decorators @Component y @Inject, el bootstrap y un wrapper de Angular1 donde meter los accesos al modulo Angular 1.x, pero ya se puede hacer esto:

import {bootstrap, Component} from "Ng2Emulation/Ng2Emulation"
 
import {DataStore} from "App/Services/DataStore"
import {TodoListComponent} from "App/Components/TodoList"
 
@Component({
    template: `
        <h1>Ng2 Emulator - Todo list sample</h1>
        <todo-list></todo-list>
`,
    selector: "my-app",
    components: [TodoListComponent]
})
export class AppComponent {
    constructor() {
        // Configuration section.
        DataStore.setInitialValues(["A sopesteque", "Picha liebre", "Zurre mierdas", "Chupa candaos", "Cascoporro"]);
    }
}
 
bootstrap(AppComponent);

Es decir, creamos una clase, que será el controlador, y la decoramos con los datos que serán el DDO para llamar a “angular.directive”, el nombre de la directiva se saca del “selector” pasándolo a “camelCase” y siempre usamos “bindToController”, “controllerAs” e isolated scopes, el template/templateUrl se pasa tal cual al DDO, además cualquier cosa que se ponga en el decorador se pasará al DDO, por ejemplo si tenemos una directiva que hace cosas en el “link” o en el “compile” del DDO podemos poner el código en el decorator Component.

Por supuesto todo lo que nos salgamos del estándar de Angular 2 luego habría que pasarlo a mano … o dicho de otro modo, si usamos cosas especificas de Angular 1 cuando demos el salto a Angular 2 habrá que reimplementarlas. También es cierto que Angular 2 está muy verde y que supongo que habrán cosas que cambien, pero este método de hacer las cosas nos podría evitar trabajo en la migración.

Aquí pongo también otro ejemplo donde se usa el @Inject:

import {Component, Inject} from "Ng2Emulation/Ng2Emulation"
 
import {TodoService} from "App/Services/TodoService"
 
@Component({
    templateUrl: "TypeScript/App/Components/TodoList.html",
    selector: "todo-list",
    componentAs: "vm"
})
export class TodoListComponent {
    upper(text) {
        if (text)
            return text.toUpperCase();
        return undefined;
    }
 
    constructor(@Inject(TodoService) public service: TodoService) {
        this.todoList = service.todoList;
    }
 
    text: string;
    todoList: string[];
 
    addTodo() {
        if (!this.text)
            return;
        this.service.addTodo(this.text);
        delete this.text;
    }
}

Ahora me he puesto a integrar el router, voy a intentar meter el ngComponentRouter (el que se llamaba ngNewRouter que se supone que venía con Angular 1.4) que dicen que vendrá con Angular 1.5, y por supuesto meter el decorator @RouteConfig para que en un componente se puedan definir las rutas.

Mientras vosotros decirme que tal lo veis 😉

One comment

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

To create code blocks or other preformatted text, indent by four spaces:

    This will be displayed in a monospaced font. The first four 
    spaces will be stripped off, but all other whitespace
    will be preserved.
    
    Markdown is turned off in code blocks:
     [This is not a link](http://example.com)

To create not a block, but an inline code span, use backticks:

Here is some inline `code`.

For more help see http://daringfireball.net/projects/markdown/syntax

Demuestra que no eres un bot *