Aitor Sánchez - Blog - Oct. 27, 2023, 9:01 a.m.
¿Pensando en usar variables de tiempo en tu app de Ionic y no sabes cómo? O, quizás, ya sabes lo que es ion-datetime pero no sabes bien cómo se utiliza alguna función o campo de esta clase ¿verdad?
Mi nombre es Aitor Sánchez, soy desarrollador de apps desde 2014, y cuando termines con este artículo tu aplicación dará la hora mejor que el Big Ben de Londres cuando hagas la integración de Ionic DateTime.
Pero antes de continuar, esta es la Flutter Mafia. Es mi newsletter donde aprenderás desarrollo de apps móviles, aso y monetización junto con otros genietes que ya están dentro. Y si te suscribes te regalo mi ebook "Duplica los ingreso de tus apps en 5 minutos" No es broma, quizás te interese.
Y ahora si, let´s go!
En este caso no tendremos que hacer nada. Al tratarse de un componente de sistema ya está incluido en la instalación. Es más, si quieres comenzar a trastear y no te puedes esperar, la etiqueta que usaremos es “ion-datetime” échale un ojo si eres un poquito impaciente.
Similar al punto anterior, no necesitamos realizar ningún tipo de configuración previa a su uso. Toda la configuración la daremos sobre el código propio como veremos a posteriormente en este mismo artículo.
Antes de continuar explicando, siempre me gusta aportar un ejemplo para que vayamos entrando en materia. Así que aquí va.
<ion-item>
<ion-label>MMMM</ion-label>
<ion-datetime displayFormat="MMMM" value="2012-12-15T13:47:20.789"></ion-datetime>
</ion-item>
<ion-item>
<ion-label>MM DD YY</ion-label>
<ion-datetime displayFormat="MM DD YY" placeholder="Select Date"></ion-datetime>
</ion-item>
<ion-item>
<ion-label>Desactivado</ion-label>
<ion-datetime id="dynamicDisabled" displayFormat="MM DD YY" disabled value="1994-12-15"></ion-datetime>
</ion-item>
<ion-item>
<ion-label>YYYY</ion-label>
<ion-datetime [pickerOptions]="opcionesPersonalizas" placeholder="Custom Options" displayFormat="YYYY" min="1981" max="2002"></ion-datetime>
</ion-item>
<ion-item>
<ion-label position="stacked">MMMM YY</ion-label>
<ion-datetime displayFormat="MMMM YY" min="1989-06-04" max="2004-08-23" value="1994-12-15T13:47:20.789"></ion-datetime>
</ion-item>
<ion-item>
<ion-label position="floating">MM/DD/YYYY</ion-label>
<ion-datetime displayFormat="MM/DD/YYYY" min="1994-03-14" max="2012-12-09" value="2002-09-23T15:03:46.789"></ion-datetime>
</ion-item>
<ion-item>
<ion-label position="floating">MM/DD/YYYY</ion-label>
<ion-datetime displayFormat="MM/DD/YYYY" min="1994-03-14" max="2012-12-09"></ion-datetime>
</ion-item>
<ion-item>
<ion-label>DDD. MMM DD, YY (zona horaria personalizada)</ion-label>
<ion-datetime value="1995-04-15" min="1990-02" max="2000"
[dayShortNames]="nombresDiasCortados"
displayFormat="DDD. MMM DD, YY"
monthShortNames="jan, feb, mar, apr, mai, jun, jul, aug, sep, okt, nov, des"></ion-datetime>
</ion-item>
<ion-item>
<ion-label>D MMM YYYY H:mm</ion-label>
<ion-datetime displayFormat="D MMM YYYY H:mm" min="1997" max="2010" value="2005-06-17T11:06Z"></ion-datetime>
</ion-item>
<ion-item>
<ion-label>DDDD MMM D, YYYY</ion-label>
<ion-datetime displayFormat="DDDD MMM D, YYYY" min="2005" max="2016" value="2008-09-02"></ion-datetime>
</ion-item>
<ion-item>
<ion-label>HH:mm</ion-label>
<ion-datetime displayFormat="HH:mm"></ion-datetime>
</ion-item>
<ion-item>
<ion-label>h:mm a</ion-label>
<ion-datetime displayFormat="h:mm a"></ion-datetime>
</ion-item>
<ion-item>
<ion-label>hh:mm A (pasos / saltos de 15 minutos)</ion-label>
<ion-datetime displayFormat="h:mm A" minuteValues="0,15,30,45"></ion-datetime>
</ion-item>
<ion-item>
<ion-label>Años bisiestos, meses de verano</ion-label>
<ion-datetime displayFormat="MM/YYYY" pickerFormat="MMMM YYYY" monthValues="6,7,8" [yearValues]="agnosPersonalizados"></ion-datetime>
</ion-item>
<ion-item>
<ion-label>Días, meses y años específicos</ion-label>
<ion-datetime monthValues="6,7,8" yearValues="2014,2015" dayValues="01,02,03,04,05,06,08,09,10, 11, 12, 13, 14" displayFormat="DD/MMM/YYYY"></ion-datetime>
</ion-item>
En el ejemplo, si lo tiras sobre una app, verás una lista con varias opciones disponibles de uso. Fácil, sencillo y para toda la familia. Ahora, vamos con la chicha.
Si necesitas mostrar una datetime en una overlay como un modal o un popover, te recomiendo usar ion-datetime-button.
Deberías usar ion-datetime-button cuando el espacio en el layout esté limitado.
Este componente muestra botones que indican los valores actuales de fecha y hora. Al tocar los botones, los selectores de fecha o hora se abren en el overlay.
Veamos un ejemplo que puedas utilizar:
<ion-content>
<!-- Botón para seleccionar la fecha -->
<ion-datetime-button datetime="fecha"></ion-datetime-button>
<ion-datetime id="fecha"></ion-datetime>
<!-- Botón para seleccionar la hora -->
<ion-datetime-button datetime="hora"></ion-datetime-button>
<ion-datetime id="hora" display-format="HH:mm"></ion-datetime>
</ion-content>
Ahora vamos a conocer un poquito los tipos de formato que le podemos dar a la entrada y pintado de datos de este componente.
Formato -> Descripción -> Ejemplo
La verdad que, hasta aquí nada nuevo. Simplemente una lista de opciones de formato de un DateTime. La verdad que son similares al del resto de tecnologías. Pero por si no lo sabías, ya lo sabes :)
Esta propiedad se utiliza en exclusiva para decirle al sistema cómo queremos que pinte la imagen en la pantalla. Si alguna vez has utilizado PHP, espero que sí, sería como hacer un “strtotime()” y aquí vamos a poner algunos ejemplos:
Display Format | Example |
M-YYYY | 6-2005 |
MM/YY | 06/05 |
MMM YYYY | Jun 2005 |
YYYY, MMMM | 2005, Junio |
MMM DD, YYYY HH:mm | Jun 17, 2005 11:06 |
Hay que recordar que Datetime utiliza una fecha relativa sobre la zona horaria del usuario. Si le damos un valor
Siendo el “+1:00” la zona horaria, un usuario que esté en “-04:00” la salida sería la siguiente:
Es muy sencillo, si no lo has entendido bien, lee de nuevo el ejemplo y verás cómo lo pillas rápido.
Vale, parecerá un poco confuso, pero este formato será el que nos aparezca a nosotros, cómo usuarios, cuando pulsemos sobre el componente “ion-datetime” para seleccionar la fecha que queremos ponerle.
En este serán definidas las columnas que queremos que aparezcan, por ejemplo, ‘picker-format="h:mm A"’. Siendo esta una selección de horas y minutos comenzando por 0 y en formato de 12 horas con la diferencia am y pm en mayúsculas.
Pues cómo todos los developers, o la mayoría, tenemos un especial “cariño” a las pu… fechas. Y más concretamente, los desarrolladores de front web lo sabrán, Javascript es un desastre para este fin. El parse de los datos es un poco complejo y tedioso de realizar en este querido lenguaje.
Pero bueno, cómo los desarrolladores de Angular y Ionic sabían de este especial cariño pues han intentado, con bastante éxito, arreglarlo. Han intentado simplificar, y unir, la api para que sea común y estándar y con esto han conseguido darle una mejor experiencia al programador y al usuario.
Ahora vamos a pasar a hablar un poco de los datos.
Y ¿por qué? Pues muy sencillo, veamos el Estándar:
YYYY-MM-DDTHH:mmZ
¿Ahora mejor? ¿No? Vale, pues este formato estándar vale para setear Date en Javascript y para, cómo una cadena, guardar fechas válidas en bases de datos. Es más, también se permite la serialización de la cadena para enviarla en un JSON a un destino http.
Vale, ¿más convencido? ¡Nah!, apuesto a que sí, si no… Pues hay soluciones mejores quizás, pero vamos que yo hasta ahora, esta es la estándar y la que yo uso, sin más J
Por otro lado, cómo es posible que quieras tocarle un poco las narices al componente para diferenciar te de los demás. Aquí te dejo la tabla con las posibilidades de los formatos del estándar:
Description | Format | Datetime Value Example |
Year | YYYY | 1994 |
Year and Month | YYYY-MM | 1994-12 |
Complete Date | YYYY-MM-DD | 1994-12-15 |
Date and Time | YYYY-MM-DDTHH:mm | 1994-12-15T13:47 |
UTC Timezone | YYYY-MM-DDTHH:mm:ssTZD | 1994-12-15T13:47:20.789Z |
Timezone Offset | YYYY-MM-DDTHH:mm:ssTZD | 1994-12-15T13:47:20.789+05:00 |
Hour and Minute | HH:mm | 13:47 |
Hour, Minute, Second | HH:mm:ss | 13:47:20 |
Bien, sigamos. El año es el único formato que contiene 4 dígitos, y en caso de que esté incluido, los milisegundos tienen 3. Todo el resto de los formatos contendrá cómo mucho 2 dígitos.
Por otro lado, para quien no lo sepa, las mayúsculas y las minúsculas se comportan de manera diferente. Por ejemplo “HH” no es lo mismo que “hh” siempre, respectivamente, formato 24 horas y formato 12 horas.
Para terminar con esto, hay comentar que el “displayFormat” no es el “pickerFormat” y tampoco ningunos será el valor que tenga la variable que esté enlazada con el componente mediante la directiva “ngModel” esto tenemos que tenerlo muy en cuenta. Una buena práctica sería, en medida de lo posible, poner el mismo formato a los 3 campos para así facilitar las cosas. La salida siempre será en ISO 8601.
Bueno, cómo has podido apreciar en el artículo, aún no se ha comentado la manera de cómo podemos obtener los datos explícitamente ¿verdad? Si que lo hemos dejado caer, pero no lo hemos hecho directamente.
Básicamente, cómo hemos hecho hasta ahora en versiones previas de Ionic, simplemente tendremos que enlazar el componente con un NgModel. Listo, así de fácil. Cuando sea editado, directamente se guardará en nuestra variable la fecha con el formato que le hayamos definido. Es recomendable usar el ionChange() en conjunto para que podamos validar los datos que nos llegan.
<ion-item>
<ion-label color="primary">Time:</ion-label>
<ion-datetime cancelText="cancel" doneText="done" displayFormat="HH:mm" [(ngModel)]="miVariableHora" (ionChange)="validateButton()"></ion-datetime>
</ion-item>
Cómo es de esperar, el componente tiene que hacer alusión al máximo número de días que puedes leer desde la fecha actual. Date cuenta de que avanzando podríamos llegar al infinito, y eso en programación no mola un pelo.
El sistema pone a nuestra disposición la capacidad de asignarle una fecha de inicio y una fecha de fin en la selección / pick. Siendo la menor, por defecto, la fecha actual menos 100 años y la mayor el fin de año del año actual.
¿Lo pillas mejor así? Nah! Apuesto que sip. Bueno, pues para que puedas cambiar estos valores tenemos los campos / propiedades de clase “min” y “max” que se pueden sobrescribir sobe el código html. Así mucho más accesible en todos los sentidos.
Recordemos que siempre tenemos que hace uso del formato ISO 8601
Buenos, pues seguimos avanzando. Ahora nos toca ver la nomenclatura usada para mostrar los datos.
La verdad sea dicha, aún no hay una manera definida de mostrar la fecha. pero lo que sí que tenemos es un sistema por el que mayoría de los navegadores han optado cómo estándar. No nos vamos a extender en esto dado que ya está explicado aquí (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DatetimeFormat)
Pero aún no está definido cómo un estándar general. Y por esta razón, Ionic aún no lo utiliza por defecto. Así que hasta que no se implementado cómo estándar no podremos usarlo.
Me parecía interesante incluir esto aquí, aunque directamente no tenga nada que ver con el artículo. Pero no te acostarás sin saber una cosa más. A parte, Angular también nos ofrece un servicio de internacionalización en desarrollo. No está verde, pero aún no es 100% fiable así que Ionic no lo ha incluido aún.
Lo más recomendado es que se realice una configuración por instancia dependiendo del lugar donde se encuentre nuestro usuario. Por ejemplo:
Y así ir sucediendo con cada una de las posibles combinaciones dependientes de la zona. A parte, si eres un poco más detallista, puedes cambiar entre número y letra los meses/días/años para que aún sea un poco más legible y cómodo.
Y para poder continuar, comentar que el language por defecto, cómo era de esperar, es el inglés. Si queremos mostrar los datos en el locale en donde estemos tendremos que programar algún tipo de parse al locale. :)
Aunque el sistema interno de ion date time nos facilite mucho la salida de datos. Siempre es recomendable tratar estos datos para validarlos. En ocasiones, por ejemplo, nos gusta tratar las fechas en formato numérico, me incluyo aquí. ¿Qué es esto? Pues que nos gusta trata el tema de las fechas con un número equivalente al número de milisegundos desde la fecha Unix. Más fácil y centralizado, pero menos legible.
Entonces, si lo hiciésemos así, no nos valdría usar la fecha que nos escupe el DateTime. ¿Entiendes? Por eso hacemos hincapié en tratar estos datos.
A parte, si queremos manipular de alguna manera la fecha. Cómo, por ejemplo, agregarle días lo más recomendable es que usemos la siguiente librería:
Es recomendada por los chicos de Ionic, así que tiene que funcionar bien ;)
Al principio del capítulo hemos visto un ejemplo bastante claro de cómo podemos la parte HTML y hemos puesto en ella nombres de variables que no hemos comentado. Ahora veamos un ejemplo un poco más conciso de cómo podemos usarlos en nuestro código TS y las comentamos.
@Component({…})
export class MiComponente {
//Esto se lo pasamos a la vista.
agnosPersonalizados = [2020, 2016, 2008, 2004, 2000, 1996];
nombresDiasCortados = [‘lun’, 'mar', 'mie', 'jue', 'vie', 'sab', 'dom'];
opcionesPersonalizas: any;
constructor() {
this.opcionesPersonalizas = {
buttons: [{
text: 'Save',
handler: () => console.log('Clicked Save!')
}, {
text: 'Log',
handler: () => {
console.log('Clicked Log. Do not Dismiss.');
return false;
}
}]
}
}
}
Cómo se puede apreciar, si nosotros después de tener esto código JS bindeamos desde el HTML las variables que tenemos aquí tendremos un ejemplar bastante interesante de un calendario chulo. Si quieres puedes revisar el código HTML de la parte de arriba para darte cuenta de que si, que eran estas opciones.
Es totalmente normal querer bindear una variable de nuestro código TS con la vista de un calendario. Sería algo así:
<ion-label stacked>Month</ion-label>
<ion-datetime placeholder="Month"
pickerFormat="MMM YYYY"
displayFormat="MMM YYYY"
[(ngModel)]="datetime_ref">
</ion-datetime>
Y en nuestro archivo .ts:
this.Year = this.datepipe.transform(this.datetime_ref,'yyyy')
this.Month = this.datepipe.transform(this.datetime_ref,'MM')
¿A qué es super sencillo?
Y ya, en última instancia vamos a ver cuáles son las propiedades de nuestro options, eventos y campos de clase que tiene Datetime. ¿Listos? Vamos allá.
Cómo era de esperar, la chicha para el final. Aquí vamos a ver toda la configuración de comportamiento y de personalización de nuestro DateTime. Así que prepárate…
cancelText |
|
---|---|
Description |
El texto que se mostrará en el botón de cancelar del selector. |
Attribute | cancel-text |
Type | string |
Default | 'Cancel' |
dayNames |
|
Description |
El nombre completo de las semanas. Esto lo podríamos usar para dar nombres locales a cualquier día de la semana. Por defecto, inglés. |
Attribute | day-names |
Type | string | string[] | undefined |
dayShortNames |
|
Description |
Los nombres cortos de los días de la semana. Esto lo podríamos usar para dar nombres locales a cualquier día de la semana. Por defecto, inglés. |
Attribute | day-short-names |
Type | string | string[] | undefined |
dayValues |
|
Description |
Los valores usados al crear la lista de días. Por defecto, los días se muestran por el mes en que están. Sin embargo, para controlar exactamente qué días del mes mostrar, la entrada de valores de datos puede tomar un número, una matriz de números o una cadena de números separados por comas. Ten en cuenta de que si hay algún error, cómo febrero con 31 días, los datos se mostrarán mal en todo el calendario. |
Attribute | day-values |
Type | number | number[] | string | undefined |
disabled |
|
Description |
Si es verdadero, el usuario no podrá interactuar con el Datetime |
Attribute | disabled |
Type | boolean |
Default | false |
displayFormat |
|
Description |
El formato de fecha con el que se mostrarán los datos que estén dentro del componente. Cuando no se esté usando |
Attribute | display-format |
Type | string |
Default | 'MMM D, YYYY' |
doneText |
|
Description |
El texto que usará el botón de "hecho" en el selector de fecha. |
Attribute | done-text |
Type | string |
Default | 'Done' |
hourValues |
|
Description |
Valores que usará el sistema para la selección de hora. Por defecto lo valores serán de 0 a 23 en formato de 24 horas, o de |
Attribute | hour-values |
Type | number | number[] | string | undefined |
max |
|
Description |
La fecha máxima permitida por el componente para la entrada del usuario. El valor debe de ser una cadena que siga el formato ISO 8601 datetime format standard, |
Attribute | max |
Type | string | undefined |
min |
|
Description |
Similar al campo anterior, max, pero esta vez para la fecha mínima en lugar de la máxima. Por defecto, 100 años atrás de la fecha actual. |
Attribute | min |
Type | string | undefined |
minuteValues |
|
Description |
Los valores usados para crear la lista de minutos que se permitirán seleccionar al usuario. By default the minutes range from |
Attribute | minute-values |
Type | number | number[] | string | undefined |
mode |
|
Description |
Nos permite elegir el diseño con el que queremos que se muestre. |
Attribute | mode |
Type | "ios" | "md" |
monthNames |
|
Description |
Los nombres completos de los nombres para los meses. Esto se usa para darle el nombre dependiendo del idioma. Por defecto: Inglés |
Attribute | month-names |
Type | string | string[] | undefined |
monthShortNames |
|
Description |
Los nombres cortos / abreviaturas de los nombres para los meses. Esto se usa para darle el nombre dependiendo del idioma. Por defecto: inglés |
Attribute | month-short-names |
Type | string | string[] | undefined |
monthValues |
|
Description |
Los valores de la lista de meses seleccionables por el usuario. Por defecto es un rango entre el 1 y el 12. Pero podemos, cómo en los otros campos, tomar control de este para mostrar lo que queramos. El campo |
Attribute | month-values |
Type | number | number[] | string | undefined |
name |
|
Description |
El nombre de del control del Datetime, usado cuando se haga submit del formulario. |
Attribute | name |
Type | string |
Default | this.inputId |
pickerFormat |
|
Description |
El formato que se usará en el selector de fecha y hora. La entrada de un Datetime puede tener diferentes formatos, cada uno tiene su propia columna que le permite la selección individual de los datos. Por ejemplo, la columna de mes y la de año nos dan una ayuda para seleccionar una fecha exacta en un Datetime. Cada columna sigue el formato que se le ha impuesto en la cadena de inicialización. Por defecto usa: |
Attribute | picker-format |
Type | string | undefined |
pickerOptions |
|
Description |
Una lista de opciones adicionales que acepta el sistema de entrada del selector de Datetime. Más adelante incluiré un enlace al tutorial de picker aquí cuando lo tenga terminado para así complementar esta información. |
Type | undefined | { columns?: PickerColumn[] | undefined; buttons?: PickerButton[] | undefined; cssClass?: string | string[] | undefined; backdropDismiss?: boolean | undefined; animated?: boolean | undefined; mode?: "ios" | "md" | undefined; keyboardClose?: boolean | undefined; id?: string | undefined; enterAnimation?: AnimationBuilder | undefined; leaveAnimation?: AnimationBuilder | undefined; } |
placeholder |
|
Description |
El texto que se mostrará cuando no haya una fecha seleccionada. Usar minúsculas para que coincida con el atributo de la entrada. |
Attribute | placeholder |
Type | null | string | undefined |
readonly |
|
Description |
Si es verdadero, la interacción con el Datetime estará desactivada. Solo será visible. |
Attribute | readonly |
Type | boolean |
Default | false |
value |
|
Description |
El valor que queremos asignarle al Datetime basado en una cadena compatible con ISO 8601. |
Attribute | value |
Type | null | string | undefined |
yearValues |
|
Description |
Valores usados para crear la lista de selección de años. Por defecto serán los años que hay entre los valores max y min que hemos visto antes. Pero podemos tomar control exactamente de lo que se muestra. El campo |
Attribute | year-values |
Type | number | number[] | string | undefined |
Name | Description |
---|---|
ionBlur |
Emitido cuando el DateTime pierde el foco |
ionCancel |
Emitido cuando el usuario pulsa sobre el botón cancelar |
ionChange |
Emitido cuando el valor seleccionado del DateTime cambia / change de valor |
ionFocus |
Emitido cuando el DateTime toma el foco |
open |
|
---|---|
Description |
Abre la ventana del Datetime de manera manual |
Signature | open() => Promise<void> |
Name | Description |
---|---|
--padding-bottom |
El padding inferior del DateTime |
--padding-end |
El padding de la derecha del DateTime |
--padding-start |
El padding de la izquierda del DateTime |
--padding-top |
El padding de arriba del DateTime |
--placeholder-color |
El color del placeholder del DateTime |
La única pega es que está en inglés. Pero como se que, por lo menos la mayoría de mis lectores, lo habla, no habrá problema.
Damos por hecho ya que mejorando el logo de nuestra app, conseguimos más descargas ¿verdad? Al final, es un factor que afecta a tu CTR listado > ficha de manera directa... Pues bien, si quieres mejorar tus logos y conseguir más descargas, esta herramienta es para ti. Dentro tienes más detalles, no te quiero espoilear.
Y ahora si, nos despedimos. Nos vemos en el siguiente artículo. Hasta entonces, ¡que vaya bien!