Vistas de página en total

jueves, 27 de julio de 2017

Uso de Typescript con Nativescript

Typescript es un lenguaje de programación que se "transpila" a Javascript, es decir, se convierte a código Javascript. En este enlace se pueden aprender rápidamente los fundamentos de Typescript.
Programar en Typescript tiene muchas ventajas sobre Javascript. Las más importantes son:

  • chequeo de tipos estático (aunque mantiene la posibilidad de tipos dinámicos de Javascript)
  • uso de interfaces, clases, herencia y genéricos
  • funciones cursor (arrow)
  • parámetros por defecto y opcionales
El hecho de que el código Typescript pueda convertirse a Javascript hace que sea posible utilizar este lenguaje en nuestros proyectos Nativescript. Veamos cómo.
El objetivo de este artículo es demostrar lo sencillo que es crear una aplicación Nativescript desde cero, con datos enlazados al modelo de una forma mucho más sencilla que en Javascript. No entraré en muchas explicaciones sobre los detalles del código (se irán viendo en posteriores artículos), pero es bastante sencillo de seguir.
Es importante hacer notar que el uso de Typescript no implica utilizar Angular. Ni mucho menos, en el ejemplo que hago a continuación no se utiliza Angular en ningún momento, simplemente se utiliza Typescript en sustitución de Javascript.

Definición del proyecto

El proyecto que vamos a crear es muy sencillo. El prototipo en papel (como a mí me gusta) se muestra a continuación:
En la primera pantalla (a la que llamaremos main) se muestra la oferta del día y un botón para navegar a la pantalla donde se muestran las ofertas para los próximos días. Llamaremos next a esta segunda pantalla.

Instalar Typescript

Instalemos Typescript en nuestro equipo:

    npm install -g typescript

Si ya estuviera instalado, este comando actualizará a la última versión.
Comprobemos que está correctamente instalado y la versión:

    tsc --version

Crear el proyecto Nativescript

Ir al directorio base y crear el proyecto usando la plantilla typescript:

    tns create OfertaDelDia --template typescript

Lo primero que vemos es que el proyecto no tiene un fichero app/app.js como ocurre en los proyectos Nativescript con Javascript, sino que el fichero se llama app/app.ts, o sea, un fichero Typescript.
El contenido de este fichero es:

import * as app from 'application';

app.start({
    moduleName: 'main/main'
});

En primer lugar, se importa el módulo application, para poder lanzar la página principal de la aplicación. Utilizando el objeto application, llamamos al método start() pasando como parámetro un objeto con el atributo moduleName que contiene el nombre de la página principal (en este caso la página main (main.xml, main.js y main.css) en el directorio app/main. Esta página la vamos a crear a continuación.

Página main

Creamos una carpeta app/main para los ficheros de la página main.

Creamos el fichero app/main/main.xml:

<Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="onPage">
    <StackLayout>
        <Label text="{{ today }}" class="today" />
        <Label text="{{ dayOffer }}" class="day-offer" textWrap="true" />
        <Label text="{{ todayPrice }}" class="today-price" />
        <Label text="{{ yesterdayPrice }}" class="yesterday-price" />
        <Button text="Próximos días..." tap="onNextDays" />
    </StackLayout>
</Page>

El atributo Page.navigatingTo define la rutina que será invocada cada vez que entremos en la página. Esta rutina se define en el fichero main.ts
Esta página simplemente contiene un StackLayout con cuatro Labels y un Button. Los labels muestran texto (atributo text) procedente del modelo, es decir, datos del objeto Observable que definiremos en el fichero main-view-model.ts Este objeto modelo tendrá al menos las propiedades today, dayOffer, price y yesterdayPrice.
El atributo class indica el estilo aplicable. Estos estilos se definen en el fichero main.css

Creamos el fichero app/main/main.ts:

import { EventData } from "data/observable";
import { Page } from "ui/page";
import { MainViewModel } from "./main-view-model";
import frame = require('ui/frame');

export function onPage(args: EventData) {
    var page = <Page>args.object;
    page.bindingContext = new MainViewModel();
    setTimeout(
        function() {
            console.log('timeout');
            page.bindingContext.set('todayPrice', 'Hoy 49€');
        },
        5000
    );
}

export function onNextDays() {
    frame.topmost().navigate('next/next');
}

En este fichero se define la función que se llamará cada vez que entremos en la página: onPage(). Esta función recibe entre sus argumentos el objeto Page, al cual enlazamos un bindingContext, el cual será un nuevo objeto de la clase MainViewModel. Este objeto sirve para enlazar datos de ese modelo con controles de la interfaz de usuario.
Por último, ponemos una ejecución diferida de Javascript (setTimeout) para cambiar el modelo después de 5 segundos. Cuando se ejecute esta función diferida, se actualiza el objeto modelo y por consiguiente se actualizará automáticamente la etiqueta asociada.

Creamos el fichero app/main/main.css:

.day-offer {
    font-size: 40px;
    font-weight: bold;
    text-align: center;
}

.today-price {
    font-size: 30px;
    font-weight: bold;
    text-align: center;
}

.yesterday-price {
    font-size: 20px;
    text-align: center;
}

.today {
    font-size: 12px;
    text-align: center;
}

Button {
    margin: 20px;
}

Creamos el fichero app/main/main-view-model.ts:

import observable = require("data/observable");

export class MainViewModel extends observable.Observable {
    constructor() {
        super();
        this.set('dayOffer', 'Maleta de viaje Samsonite L3R');
        this.set('todayPrice', 'Hoy 58€');
        this.set('yesterdayPrice', 'Ayer 96€');
        this.set('today', 'Lunes, 27 de julio');
    }
}

Este fichero contiene el modelo de la página main. Se trata de una clase llamada MainViewModel (que hereda de Observable) y que tiene una serie de propiedades que asignamos utilizando el método set() heredado de la clase Observable. Estas propiedades se podrán utilizar en controles de la página.

Página next

Creamos una carpeta app/next para los ficheros de la página next.

Creamos el fichero app/next/next.xml:

<Page xmlns="http://schemas.nativescript.org/tns.xsd"
navigatingTo="onPage">
    <Page.actionBar>
        <ActionBar title="Ofertas próximos días" class="header">
            <NavigationButton text="Back" android.systemIcon="ic_menu_back" tap="onBack" />
        </ActionBar>
    </Page.actionBar>
    <DockLayout stretchLastChild="true">
        <DockLayout dock="bottom">
            <Button dock="right" text="Add" tap="onAdd" />
            <TextField id="newText" class="input input-border" hint="New text" />
        </DockLayout>
        <ScrollView>
            <ListView items="{{ items }}">
                <ListView.itemTemplate>
                    <StackLayout>
                        <Label text="{{ $value.date }}" class="date" />
                        <Label text="{{ $value.offer }}" class="offer" />
                    </StackLayout>
                </ListView.itemTemplate>
            </ListView>
        </ScrollView>
    </DockLayout>
</Page>

Esta página es un poco más compleja que la anterior. En primer lugar contiene un DockLayout que incluye una lista de ofertas en la parte central, y un campo de texto junto con botón para añadir elementos, en la parte inferior. La lista de ofertas está enlazada al modelo, que será un array de objetos con dos campos: date y offer.

Creamos el fichero app/next/next.ts:


import { EventData } from "data/observable";
import { Page } from "ui/page";
import { NextViewModel } from "./next-view-model";
import frame = require('ui/frame');
import view = require("ui/core/view");

var page:Page = null;
var model:NextViewModel = null;

export function onPage(args: EventData) {
    console.log('next.onPage');
    page = <Page>args.object;
    model = new NextViewModel();
    page.bindingContext = model;
}

export function onBack() {
    frame.topmost().goBack();
}

export function onAdd() {
    let newTextTxt:any = view.getViewById(page, "newText");
    console.log('Text=' + newTextTxt.text);
    model.addItem('Hoy', newTextTxt.text);
    newTextTxt.text = "";
}

Creamos el fichero app/next/next.css:

.offer {
    font-size: 25px;
    font-weight: bold;
    text-align: center;
}

.date {
    font-size: 14px;
    text-align: left;
}

.header {
    background-color: #333;
    color: #fff;
}


Creamos el fichero app/next/next-view-model.ts:

import observable = require("data/observable");
import {ObservableArray} from 'data/observable-array';


export class NextViewModel extends observable.Observable {
    public items:ObservableArray<object> = null;

    constructor() {
        super();
        this.items = new ObservableArray();
        this.items.push({date: 'Martes 28', offer: 'Portátil HP 400X' });
        this.items.push({date: 'Miércoles 29', offer: 'Mochila Thule 3' });
        this.items.push({date: 'Jueves 30', offer: 'iPhone 6S 32GB' });
        this.set('items', this.items);
    }

    addItem(d:string, o:string) {
        console.log('Add: ' + d + ',' + o);
        if (o.length > 0) {
            this.items.push({date:d, offer:o});
        } else {
            console.log("Empty string");
        }
    }
}

Ejecutar la aplicación

Arrancar el emulador Android o el simulador iOS, o bien Genymotion.

    tns platform add android
    tns platform add ios
    tns run android --emulator
    tns run ios --emulator







No hay comentarios:

Publicar un comentario