VISITAS:

jueves, 22 de diciembre de 2016

¿Typescript en 30 minutos?

¿Es posible aprender Typescript en 30 minutos o menos? Depende, si ya conoces algo de Javascript y también algún lenguaje de programación orientado a objetos como Java o C#, seguro que sí. Si no, te costará un poco más, pero no mucho. Te lo voy a demostrar en este post. Abre una nueva pestaña en playground Pon el cronómetro en marcha y empieza a leer... Empezamos con el lenguaje de moda y, quizás, el lenguaje del futuro para la web.


Qué es Typescript

Typescript es un lenguaje de programación desarrollado y mantenido por Microsoft. La primera versión pública salió en Octubre de 2012 y la presentó Anders Hejlsberg, el creador de lenguajes tan conocidos como Turbo Pascal, Delphi y C#.
Typescript es un superconjunto de Javascript, que no se utiliza directamente como tal, sino que se convierte a Javascript, que ya se puede utilizar en cualquier navegador o en NodeJS. Cualquier código Javascript válido es también código Typescript válido. Soporta Javascript ES5, ES2015 y ES2016, e incluso está previsto el soporte ES2017.
Typescript proporciona chequeo de tipos, interfaces, clases y módulos, entre otros mecanismos propios de los lenguajes orientados a objetos.
Existe una web que proporciona los ahora tan de moda, playground para aprender y experimentar con el lenguaje Typescript.
Veamos un poco más despacio las nuevas características del lenguaje Typescript.

Tipos

Typescript soporta chequeo de tipos estático que permite detectar muchos errores en la fase de desarrollo.
Los chequeos de tipos se realizan en la fase de compilación (realmente conversión) de Typescript a Javascript.
Las anotaciones de tipos son opcionales. Si no se especifica un tipo, entonces no se efectuará ningún chequeo.
Los tipos soportados son: number, boolean, string y los arrays de estos tipos. También existe el tipo any para cualquier tipo, incluidas las estructuras de datos u objetos.
Ejemplo:

function multiply(a: number, b: number) : number {
    return a * b;
}

Funciones arrow

En Javascript podemos crear funciones anónimas de la siguiente forma:

setTimeout(function() {
    console.log("Timed out function");
}, 500);

Typescript permite una sintaxis, denominada arrow, para definir de una forma más sencilla las funciones anónimas:

setTimeout( () => { console.log("Timed out function"); }, 500);

Variables con ámbito de bloque

En Javascript, las variables declaradas con var tienen ámbito de la función donde se declaran. De hecho, aunque se declare la función en un punto intermedio de la función, Javascript traslada esa declaración al principio de la función. Esto puede llevar a situaciones extrañas, como por ejemplo:

var x = 3;
function show(param) {
    if (param == true) {
        return x;
    } else {
        var x = 4;
        return x;
    }
}
console.log(show(true));

Aunque podríamos pensar que el resultado en la consola es 3, realmente veremos "undefined". ¿Por qué? Porque el motor Javascript traslada la declaración var x al principio de la función. Es como si fuera:

var x = 3;
function show(param) {
    var x;
    if (param == true) {
        return x;
    } else {
        x = 4;
        return x;
    }
}
console.log(show(true));

Así se entiende porqué el valor retornado es undefined.

Typescript introduce la declaración de variables con let y con const.
let hace que la variable tenga ámbito de bloque y const es similar pero sólo se puede inicializar el valor y luego no se puede modificar.

Cambiemos el ejemplo anterior utilizando let y quedará mucho más claro:

let x = 3;
function show(param) {
    if (param == true) {
        return x;
    } else {
        let x = 4;
        return x;
    }
}
console.log(show(true));

En este caso, el valor mostrado en consola es 3, como era de esperar.

Se recomienda utilizar siempre let y const en lugar de var.

Patrones de cadenas

Muchas veces necesitamos concatenar cadenas con valores de variables:

let str : string = "( " + a + ", " + b +" )";

Con Typescript podemos conseguir el mismo efecto mediante patrones de cadenas, utilizando la comilla que va hacia la izquierda:

let str : string = `(  {a} , {b} )`;

Además, los patrones de cadenas en Typescript también permiten cadenas multilínea:

let str : string = `
    Esta cadena tiene
    múltiples líneas
    y así se guardará`;

Parámetros por defecto y opcionales

En Typescript podemos especificar valores por defecto para los parámetros de una función:

function add( a : number, b : number = 0): number  {
    return a + b;
}

add(3, 4);     // 7
add(3);        // 3

Los parámetros con valores por defecto se aplican cuando se pasa un valor undefined (no cuando se pasa null, y siempre son los últimos.
En Javascript todos los parámetros de una función son opcionales. Si un parámetro no se pasa al llamar a la función, la función recibe undefinedSin embargo, en Typescript se deben especificar los parámetros que son opcionales poniendo ? después del nombre del parámetro:

function add( a : number, b? : number): number  {
    if (b) {
        return a + b;
    } else {
        return a;
    }
}

add(3, 4);     // 7
add(3);        // 3

En Javascript se puede pasar un número indeterminado de argumentos a una función y ésta los recoge utilizando la variable llamada arguments. En Typescript esto se puede hacer con la siguiente sintaxis:

function add( a : number, ...other: number[] ) {
    let total = a;
    for (int i = 0; i < other.length; i++) {
        a += other[i];
    }
}

add(3, 4, 5, 6);    // 18

Interfaces

Los interfaces son una forma de definir contratos:

interface Graphic {
    toString() : string;
    getX() : number;
    getY() : number;
    setX(x:number) : void;
    setY(y:number) : void;
    draw() : void;
}

Este interfaz define las operaciones que tendrán los objetos gráficos.

Utilización de este interfaz:

function move(obj : Graphic, x : number, y : number) : void {
    obj.setX(x);
    obj.setY(y);
}

Para obligar a una clase a implementar la interfaz:

class Point implements Graphic {
    ...
}

Clases

La sintaxis para la definición de clases en Typescript es muy similar a la de los lenguajes orientados a objetos tradicionales:

class Rectangle {
    protected x : number;
    protected y : number;
    protected w : number;
    protected h : number;

    constructor(x: number, y: number, w: number, h: number) {
        this.x = x;
        this.y = y;
        this.w = w;
        this.h = h;
    }

    scale(factor: number): void {
        this.w = this.w * factor;
        this.h = this.h * factor;
    }
}

Podemos crear objetos de una clase utilizando new:

let r = new Rectangle(3, 2, 60, 50);
r.scale(1.3);

Se puede extender una clase mediante herencia:

class Square extends Rectangle {
    private name : string;
    constructor(x: number, y: number, s: number, name: string) {
        super(x, y, s, s);
        this.name = name;
    }
    getName() {
        return this.name;
    }
}

Los miembros de una clase (atributos y métodos) soportan los modificadores private, protected y public para definir el acceso. Por defecto, todos los miembros son public, y se puede acceder a ellos desde cualquier sitio. Los miembros protected sólo se pueden acceder desde la propia clase y desde sus clases derivadas. Por último, los miembros private sólo se pueden acceder desde la propia clase.

En Typescript se pueden definir métodos get y set para interceptar el acceso a atributos de una clase:

class Point {
    private _x : number;
    private _y : number;
    constructor(x: number, y: number) {
        this._x = x;
        this._y = y;
    }
    public get x() : number {
        console.log("getting x value");
        return this._x;
    }
    public set x(v: number) {
        console.log("setting x value");
        this._x = v;
    }
    public get y() : number {
        return this._y;
    }
    public set y(v: number) {
        this._y = v;
    }
}

let point = new Point(3, 5);
point.x = 9;
console.log(point.x);

En la consola de Javascript veremos lo siguiente:

setting x value
getting x value
9

Se pueden definir miembros estáticos en las clases (tanto atributos como métodos):

class Types {
    static VALUE_1: number = 1;
    static VALUE_2: number = 2;
    static VALUE_3: number = 3;
    static add(a: number, b: number): number {
        return a + b;
    }
}

Types.add(Types.VALUE_1, 5);   // 6

Por último, en Typescript podemos definir clases abstractas, las cuales no se pueden instanciar, pero podemos definir clases que heredan de ellas:

abstract class BaseClass {
    protected _name: string;
    abstract toString(): string;
    public set name(n: string) {
        _name = n;
    }
}

Módulos

Cuando un proyecto empieza a crecer, suele ser una buena idea dividir nuestro código en ficheros diferentes, también llamados módulos. En Typescript cada módulo lo pondremos en un fichero.
Si queremos exportar algo desde un módulo, ya sea un interfaz, una clase, una función, etc, utilizamos la palabra clave export:
Módulo para validación de strings:

// stringvalidator.ts
export interface StringValidator {
    isValid(s: string) : boolean;
}

Otro módulo para validación de código postal:

// zipvalidator.ts
export class ZipValidator implements StringValidator {
    isValid(n: string) : boolean {
        return .......
    }
}

Podemos hacer un módulo de validaciones que exporte a los otros dos:

// validators.ts
export * from "./stringvalidator";
export * from "./zipvalidator";

Para importar desde otro módulo:

import { ZipValidator } from "./validators"
let v = new ZipValidator();
v.isValid("28041");

Se puede importar cambiando el nombre (una especie de alias):

import { ZipValidator as Zip } from "./validators"

Finalmente, podemos importar todo el contenido de un módulo y meterlo en una variable:

import * as validators from "./validators"
let s = new validators.StringValidator();

Genéricos

Las clases genéricas permiten:

  • Mayor seguridad de tipos: un Array sólo puede contener cadenas
  • El compilador chequea los tipos de los genéricos
  • Son más rápidas que utilizar cualquier tipo libremente
  • Podemos escribir código que es aplicable a muchos tipos
Ejemplo:

class Pair {
    _left: T;
    _right: T;
    set left(v:T) { this.left = v; }
    set right(v:T) { this.right = v; }
    get left(): T { return this._left; }
    get right(): T { return this._right; }
}

let pair = new Pair();
pair.left = "left part";


Enumerados

Los enumerados son tipos que definen una serie de valores.
Ejemplo:

enumeration RecordType {
    text = 1,
    image,
    audio,
    video
}

let type: RecordType = RecordType.text;























lunes, 12 de diciembre de 2016

NativeScript y múltiples tamaños de pantalla

NativeScript: múltiples tamaños de pantalla

Hoy voy a escribir sobre NativeScript y sobre el soporte a múltiples tamaños de pantalla.

Cuando definimos la UI con NativeScript, situamos los objetos y los dimensionamos utilizando puntos (dp). Estos puntos no se corresponden directamente con pixels de la pantalla. Cuando el móvil o tablet ejecuta nuestro programa, convierte estos puntos en pixels reales de la pantalla (px).

Cada dispositivo tiene una densidad de pixels (pixels por pulgada, dpi) diferente. Hay dispositivos con 90 dpi y hay dispositivos con 320 dpi.

Se establece el siguiente criterio: suponemos una densidad de pixels de 160 dpi, y sobre esa densidad definimos los puntos. O sea, los puntos se corresponden exactamente con pixels en pantallas con 160 dpi. Pero en pantallas de otra densidad, los puntos hay que transformarlos en pixels (si tenemos más de 160 dpi, se pintarán varios pixels por cada punto, y si tenemos menos de 160 dpi, necesitaremos varios puntos para completar un pixel).

En un dispositivo con densidad dpi, la conversión a pixels del dispositivo se hace con la siguiente fórmula:

    px = dp * dpi / 160

Al programar, se posiciona y se dimensiona todo en puntos, y luego esto se convierte a pixels en cada dispositivo según la fórmula anterior.

Por ejemplo, si creamos un círculo en la posición (300, 200) con 100 puntos de radio, se dibujará de la siguiente forma:

    - en un dispositivo de 160 dpi: radio 100 pixels, posición (300, 200)
    - en un dispositivo de 320 dpi: radio 200 pixels, posición (600, 400)
    - en un dispositivo de 220 dpi: radio 137 pixels, posición (412, 275)


el tamaño y posición del círculo será exactamente igual en ambos dispositivos.

Esto resuelve el problema de que cada dispositivo tiene una densidad de pixels distinta, y los objetos gráficos tendrán el mismo tamaño en todos los dispositivos.

Sin embargo, esto no resuelve el problema de los distintos tamaños de pantalla. Tenemos pantallas desde 3.5" hasta 5.5" y más. Aunque el círculo del ejemplo anterior se mostraría en la misma posición y con igual tamaño en cualquier dispositivo, sin embargo, en función del tamaño físico de la pantalla, el círculo estaría en el centro, o hacia la izquierda o hacia la derecha.

Este problema se resuelve con los layouts que posicionan los objetos y los redimensionan en función del tamaño de la pantalla.

Se considera que un dispositivo es una tablet cuando su dimensión más pequeña es superior a 3,75", o sea, cuando hay más de 600 dp en su dimensión más pequeña.

    Si la dimensión más pequeña es mayor que 3,75" (600 dp) => es una tablet

NativeScript sigue el siguiente criterio de nombrado para los ficheros:

    filename.qualifier*.extension

Por ejemplo: main_page.android.xml

Los cualificadores pueden ser:

  • cualificadores de orientación: port, land
  • cualificadores de plataforma: android, ios
  • cualificadores de tamaño de pantalla: minWHxxx la dimensión más pequeña debe ser al menos xxx dp's, minWxxx el ancho debe ser al menos xxx dp's, minHxxx el alto debe ser al menos xxx dp's


Con este criterio, podemos definir diseños de pantalla y estilos específicos para una orientación, para un tamaño o para un sistema operativo.