jhipster – change ngb-datepicker date format

Screenshot from 2018-01-11 16-16-39

When you generate an entity with jhipster entity and add a LocalDate the UI generated uses ng-bootstrap Datepicker. To change the format you need to add an implementation of the class NgbDateParserFormatter. To change the language you need to provide an implementation of the class NgbDatepickerI18n (as shown in the examples).

First create the class files, i.e.

/webapp/src/main/webapp/app/blocks/config/ng-bootstrap.date-parser-formatter.ts

import { Injectable } from '@angular/core';
import { NgbDateParserFormatter, NgbDateStruct } from '@ng-bootstrap/ng-bootstrap';

function padNumber(value: number) {
    if (isNumber(value)) {
        return `0${value}`.slice(-2);
    } else {
        return "";
    }
}

function isNumber(value: any): boolean {
    return !isNaN(toInteger(value));
}

function toInteger(value: any): number {
    return parseInt(`${value}`, 10);
}


@Injectable()
export class NgbDateParserFormatterEsMX extends NgbDateParserFormatter {
    parse(value: string): NgbDateStruct {
        if (value) {
            const dateParts = value.trim().split('/');
            if (dateParts.length === 1 && isNumber(dateParts[0])) {
                return {year: toInteger(dateParts[0]), month: null, day: null};
            } else if (dateParts.length === 2 && isNumber(dateParts[0]) && isNumber(dateParts[1])) {
                return {year: toInteger(dateParts[1]), month: toInteger(dateParts[0]), day: null};
            } else if (dateParts.length === 3 && isNumber(dateParts[0]) && isNumber(dateParts[1]) && isNumber(dateParts[2])) {
                return {year: toInteger(dateParts[2]), month: toInteger(dateParts[1]), day: toInteger(dateParts[0])};
            }
        }
        return null;
    }

    format(date: NgbDateStruct): string {
        let stringDate: string = "";
        if(date) {
            stringDate += isNumber(date.day) ? padNumber(date.day) + "/" : "";
            stringDate += isNumber(date.month) ? padNumber(date.month) + "/" : "";
            stringDate += date.year;
        }
        return stringDate;
    }
}

<project>/direccion-juridica/webapp/src/main/webapp/app/blocks/config/ng-bootstrap-datepicker-i18n.ts

import {Component, Injectable} from '@angular/core';
import {NgbDatepickerI18n} from '@ng-bootstrap/ng-bootstrap';

const I18N_VALUES = {
    'es-MX': {
        weekdays: ['Lun', 'Mar', 'Mie', 'Jue', 'Vie', 'Sab', 'Dom'],
        months: ['Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', 'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dic'],
    }
    // other languages you would support
};

// Define a service holding the language. You probably already have one if your app is i18ned. Or you could also
// use the Angular LOCALE_ID value
@Injectable()
export class I18n {
    language = 'es-MX';
}

// Define custom service providing the months and weekdays translations
@Injectable()
export class CustomDatepickerI18n extends NgbDatepickerI18n {

    constructor(private _i18n:I18n) {
        super();
    }

    getWeekdayShortName(weekday:number):string {
        return I18N_VALUES[this._i18n.language].weekdays[weekday - 1];
    }

    getMonthShortName(month:number):string {
        return I18N_VALUES[this._i18n.language].months[month - 1];
    }

    getMonthFullName(month:number):string {
        return this.getMonthShortName(month);
    }
}

finally define the provider in <project>/direccion-juridica/webapp/src/main/webapp/app/app.module.ts

import ...

...
    providers: [
        ProfileService,
        customHttpProvider(),
        PaginationConfig,
        UserRouteAccessService,
        I18n,
        {provide: NgbDatepickerI18n, useClass: CustomDatepickerI18n},
        {provide: NgbDateParserFormatter, useClass: NgbDateParserFormatterEsMX}
    ],
    bootstrap: [ JhiMainComponent ]
})
...

to change the displayed format of dates in tables you can replace in files date:'mediumDate' with date:'dd/MM/yyyy' and date:'medium' with date:'dd/MM/yyyy HH:mm' for example.

for ZonedDateTime it uses a datetime-local input which tries to use browser configuration -if supported-
Due to, and I’m quoting this from Mozilla

Date/time inputs sound convenient at first glance; they provide an easy UI 
for choosing dates and times, and they normalize the data format sent to the 
server, regardless of the user's locale. However, there are issues with 
 because of the **limited browser support.**

So I would recommend breaking DateTimes into a Datepicker and a Timepicker from ng-bootstrap and configuring as above.

sources:
https://stackoverflow.com/questions/40664523/angular2-ngbdatepicker-how-to-format-date-in-inputfield

https://github.com/ng-bootstrap/ng-bootstrap/issues/754#issuecomment-247767027

Anuncios

mac os – como ajustar la fecha y hora EXIF de tus fotografías

25478_D5100_left

Tengo una cámara Nikon D5100 y nunca he podido establecer correctamente la fecha de la cámara, lo intenté algunas veces y siempre me daba una diferencia al ver las propiedades de la imagen en finder o en photos. No sé si por remover la batería, la zona horaria, horario de verano, al formatear la memoria o a que se deba.
Por ello, y debido a la importancia para el correcto ordenamiento en las aplicaciones de fotografía, necesitaba corregir las propiedades de las fotografías antes de importar los archivos a cualquier programa. Para ello existe una herramienta de línea de comandos (CLI) llamada ‘exiftool’. Para instalarla mediante brew simplemente ejecuta

brew install exiftool

después de ello posicionate en la carpeta donde tengas las fotografías y ejecuta el siguiente comando para corregir la hora de las fotografías (agregar 5 horas a la hora original, preservar las fechas de sistema y modificar directamente sin crear una copia)

cd /tmp/cancun
exiftool -alldates+=5 -P -overwrite_original_in_place *

nota: primero copie las fotografías de la memoria de la cámara a una carpeta temporal, por ello utilicé la opción -overwrite_original_in_place, si no estas seguro no la uses y por default creará un respaldo de las imágenes originales.

fuentes:

https://petapixel.com/2012/11/05/how-to-fix-your-timestamps-if-you-forgot-to-update-your-camera-for-daylight-savings/
https://sno.phy.queensu.ca/~phil/exiftool/faq.html#Q24

js – bootstrap datetimepicker in modal window

I’ve had a lot of troubles to display a datetimepicker inside a bootstrap modal window. I’ve tried several ways to display the datetimepicker but none of them worked (delegate, z-index, etc.)

The problem I think, is we tried to initialize the datetimepicker before it gets added to the DOM, because of the animation on the modal window.
I tried also to initialize the datetimepicker on the ‘opened’ event of the modal window like so

<div class="row">
            <div class="'col-sm-3'">
                <div class="form-group">
                    <div class="'input-group" id="'datetimepicker'">

                        <span class="input-group-addon">
                            <span class="glyphicon glyphicon-time"></span>
                        </span>
                    </div>
                </div>
            </div>
        </div>
var modal = $modal.open({
      templateUrl: ...,
      controller: ...
});
modal.result.then(function (result) {
     $state.go('avisos', null, {reload: true});
}, function () {
    $state.go('^');
 });

modal.opened.then(function(){
     console.log('modal opened');
     $('#datetimepicker').datetimepicker({ format: 'DD/MM/YYYY'});
});

the event gets fired before the datetimepicker is in place and therefore not initialized correctly. So, the solution for me was to initialize the datetimepicker in a timeout function inside my angular controller

setTimeout(function () {
     $('#datetimepicker').datetimepicker({
         format: 'DD/MM/YYYY'
     });
}, 1000);

SmartGWT – Seleccionar la fecha de un Calendario posterior a Diciembre de 2015

Screenshot from 2015-11-20 09:10:35.png

Dependiendo la versión de SmartGWT que estes utilizando, puede que los selectores de fecha (calendarios) no te permitan seleccionar más allá de 2015. Una forma de evitar esta limitante es ir a Diciembre de 2015 y -aunque este en gris y aparentemente desactivado- dar clic en el día 1 de Enero de 2016 que es visible. Una vez hecho esto, el calendario da el salto a 2016 y podemos seleccionar cualquier mes de ese año. Obviamente no debemos obligar al usuario a tener que hacer esto cada que quiera seleccionar una fecha posterior.

Para establecer por default -para todos los selectores de fecha- un rango de fechas más amplio podemos hacer lo siguiente en nuestro punto de entrada a la aplicación.

DateItem defaultDateItemProperties = new DateItem();
defaultDateItemProperties.setStartDate(new Date(2000-1900, 01, 01));
defaultDateItemProperties.setEndDate(new Date(2025-1900, 12, 31));
DateItem.setDefaultProperties(defaultDateItemProperties);
DateChooser defaultDateChooserProperties = new DateChooser();
defaultDateChooserProperties.setStartYear(2000);
defaultDateChooserProperties.setEndYear(2025);
DateChooser.setDefaultProperties(defaultDateChooserProperties);

Fuentes:
http://forums.smartclient.com/forum/smart-gwt-technical-q-a/22639-datepicker-not-allowing-before-1995-and-2015

SmartGWT – Validación dependiente (dependant validation)

La validación de los datos de entrada es indispensable en cualquier aplicación. En las aplicaciones web tenemos que validar del lado del servidor y, es recomendable, validar del lado del cliente.

A continuación veremos como podemos validar del lado del cliente los campos de fecha de un formulario en SmartGWT.

En nuestro caso, tenemos un formulario sencillo donde se capturan dos fechas. Dado que estas fechas nos representan un rango, la fecha de fin siempre debe ser posterior a la fecha de inicio. En este caso se debe validar cuando cambie cualquiera de los dos campos. En SmartGWT lo podríamos realizar de la siguiente manera:

DynamicForm frmAudiencia = new DynamicForm();
DateTimeItem itmInicio = new DateTimeItem("inicio", "Inicio");
itmInicio.setType("datetime");
itmInicio.setShowPickerTimeItem(true);
itmInicio.setValidateOnChange(true);
DateTimeItem itmFin = new DateTimeItem("fin", "Fin");
itmFin.setType("datetime");
itmFin.setShowPickerTimeItem(true);
itmFin.setValidators(new CustomValidator() {
    {
        setDependentFields(new String[]{"inicio"});
    }
    @Override
    protected boolean condition(Object value) {
        return getRecord().getAttributeAsDate("inicio").before((Date) value);
    }
});
itmFin.setValidateOnChange(true);
frmAudiencia.setItems(itmNuc, itmTipoAudiencia, itmInicio, itmFin, itmJuez, itmSala);

De esta forma, cuando se modifique cualquiera de las dos fechas, se ejecuta el proceso de validación.

Fuentes:
http://smartclientexperience.wordpress.com/2011/10/19/introduction-to-smartclient-8-1smartgwt-2-5-validation-part-3/
http://www.smartclient.com/smartgwt/javadoc/com/smartgwt/client/widgets/form/validator/CustomValidator.html

AngularJS – Ejecutar una función de javascript en una plantilla (template)

Screenshot from 2014-04-16 12:11:30

Para dar formato a una fecha, y muchas cosas más, existe una librería llamada Moment.js. Para utilizar sus métodos desde una plantilla encontré este post que nos explica cómo crear un filtro para lograrlo.

El filtro quedaría de la siguiente manera

angular.module('myModule').
  filter('fromNow', function() {
    return function(dateString) {
      return moment(dateString).fromNow()
    }
  })

y el template

{{ reply.createdDate | fromNow }}

Funciona muy bien, pero tendríamos que crear un filtro para cada función de la librería que quisieramos utilizar. La ventaja es que la implementación del filtro queda independiente de la vista (template).

Otra opción sería poner el objeto moment disponible en la vista de la siguiente manera

angular.module('myModule', []).
controller('myController', ['$scope', function($scope){
 $scope.moment = moment
}])

y en la plantilla

{{ moment(reply.createdDate).fromNow() }}

Fuentes:

http://www.34m0.com/2012/07/angularjs-user-friendly-date-display.html
http://stackoverflow.com/questions/12466661/using-helper-methods-while-templating-with-angular-js
http://momentjs.com/

SmartGwt – Personalizar el editor de Eventos de un Calendario

SmartGwt tiene el widget Calendar para mostrar, crear y editar eventos. Por default, tiene un editor con controles para modificar las propiedades básicas de un evento: startDate, endDate, name y description.
Si quieres agregar más propiedades a tus Eventos, puedes utilizar el método setEventEditorFields)
Pero que pasa cuando quieres modificar por completo, o mejor dicho, sustituir el Editor por defecto. Desde la versión 2.5 de SmartGwt lo puedes hacer de manera fácil utilizando el evento BackgroundClick) de la siguiente manera

Para crear un evento cuando el usuario selecciona un rango de tiempo mediante un click o arrastrando el mouse

    final Calendar calendar = new Calendar();
    calendar.setShowQuickEventDialog(false);
    calendar.setShowAddEventButton(false);
    calendar.addBackgroundClickHandler(new BackgroundClickHandler() {
        @Override
    public void onBackgroundClick(BackgroundClickEvent event) {
        event.cancel();
        clearCalendarSelection(calendar);
        CalendarEvent evnt = new CalendarEvent();
        evnt.setStartDate(event.getStartDate());
        evnt.setEndDate(event.getEndDate());
        editEvent(evnt); // show custom Event Editor
    }
    };);

Para crear un evento cuando se da clic en una fecha en la vista de “Mes”

    calendar.addDayBodyClickHandler(new DayBodyClickHandler() {
        @Override
        public void onDayBodyClick(DayBodyClickEvent event) {
            event.cancel();
            CalendarEvent evnt = new CalendarEvent();
            evnt.setStartDate(event.getDate());
            evnt.setEndDate(event.getDate());
            editEvent(evnt);
        }
    });

Para modificar un Evento existente

    calendar.addEventClickHandler(new EventClickHandler() {
        @Override
        public void onEventClick(CalendarEventClick event) {
            event.cancel();
            clearCalendarSelection(calendar);
            editEvent(event.getEvent());
        }
    });

Notesé que es necesario llamar el método clearCalendarSelection para evitar que al dar clic en un Evento después de seleccionar un rango se dispare también el evento BackgroundClick, además del evento EventClick

private native final void clearCalendarSelection(Calendar calendar)/*-{
    obj = calendar.@com.smartgwt.client.widgets.BaseWidget::getJsObj()();
    obj.clearTimeSelection();
}-*/;

Fuentes:
http://forums.smartclient.com/showthread.php?p=102142#post102142
https://code.google.com/p/smartgwt/issues/detail?id=267
http://www.smartclient.com/smartgwt/javadoc/com/smartgwt/client/widgets/calendar/events/BackgroundClickHandler.html
http://www.smartclient.com/smartgwt/javadoc/com/smartgwt/client/widgets/calendar/Calendar.html
http://forums.smartclient.com/showthread.php?p=102139#post102139