Serie · 2013 → hoy

Notas de desarrollo desde 2013

Problemas reales del trabajo, resueltos y documentados. Una serie que recorre lo que fui aprendiendo desde 2013 hasta hoy: qué se usaba, qué quedó viejo y qué sigue sirviendo.

Ver las entradas → Quién escribe esto

jueves, 23 de enero de 2014

Calendarios jQuery / Fecha Inicio - Fecha Fin

Un problema que me llevó bastantes días resolverlo en el trabajo, fue el de implementar una página en la que se mostraran dos calendarios. Uno para seleccionar una "Fecha de inicio", y el otro en la cual seleccionar una "Fecha de Fin", para parametrizar los resultados de una exportación.

Hasta acá no había mayores problemas que el de elegir con qué herramientas iba a afrontar el desafío.

Para esto, noté que existía en la librería "jQuery-UI", un widget bastante utilizado llamado "DatePicker". El widget, por defecto, se presenta como un textbox en el cual al hacer click sobre el mismo despliega un calendario que nos da la posibilidad de elegir una fecha. Mi idea de momento, era el de hacerlo un poco mas estético, mostrando un botón con un calendario dibujado, que solamente al hacer click sobre dicho botón se despliegue dicho calendario.

Existe en la presentación de dicho control un ejemplo llamado "Icon Trigger", el cual agrega un botón junto al textbox, que dispara la apertura del calendario. En el ejemplo puede verse una imágen de un calendario bastante precaria, y la idea era dibujar un calendario sobre los estéticos botones que podemos conseguir hoy en día con Bootstrap.

Revisando la documentación del control en la "API Documentation", encontré mas información de la opción "ShowOn" y cómo utilizarla para lograr mi objetivo. Pude ver que se puede elegir si disparar el calendario desde un click en el textbox, en el botón, o hacerlo desde ambos (como dije anteriormente, mi idea era hacerlo sólamente desde el botón).

Entonces procedí a dibujar los controles sobre una página html:


El código del ejemplo anterior sería el siguiente:
<div class="container well col-md-12">
    <form class="form-horizontal" id="formExportar" method="post">
        
        <div class="row">
            <div class="col-md-6 form-group">
                <label class="col-md-5 control-label" for="txtFechaI" style="color: black;">Fecha Ini:</label>
                <div class="col-md-7 input-group" id="divFechaI">
                    <input class="form-control" id="txtFechaI" placeholder="dd/mm/yyyy" type="text" />
                </div><!-- /input-group -->
            </div><!-- /form-group -->
            <div class="col-md-6 form-group">
                <label class="col-md-5 control-label" for="txtFechaF" style="color: black;">Fecha Fin:</label>
                <div class="col-md-7 input-group" id="divFechaF">
                    <input class="form-control" id="txtFechaF" placeholder="dd/mm/yyyy" type="text" />
                </div><!-- /input-group -->
            </div><!-- /form-group -->          
        </div><!-- /row -->

        <div class="row">
            <div class="col-md-3"></div>
            <div class="col-md-2">
                <button class="btn btn-success" id="btnExportSol">
                    <span class="glyphicon glyphicon-download-alt"></span> Export1</button>
            </div><!-- /col-md-2 -->
            <div class="col-md-1"></div>
            <div class="col-md-2">
                <button class="btn btn-success" id="btnExportSolEst">
                    <span class="glyphicon glyphicon-download-alt"></span> Export2</button>
            </div><!-- /col-md-2 -->
        </div><!-- /row -->

    </form>
</div>

Lo que resta para obtener el control tal y como lo quería, es manipular un poco los DIVs que contienen a los textbox donde se ingresa la fecha, para así añadirles el botón a cada uno, con su correspondiente calendario.

Lo mencionado anteriormente se logra con el siguiente script:
    
<script>
        $(document).ready(function() {
            
            $("#txtFechaI").datepicker({
                showOn: "button",
                dateFormat: "dd/mm/yy",
                maxDate: "+0d",
                onClose: function( selectedDate ) {
                    $( "#txtFechaF" ).datepicker( "option", "minDate", selectedDate );
                    var x = $("#divFechaF button");
                    x.text("");
                    x.addClass("btn btn-success");
                    x.append("<span class='glyphicon glyphicon-calendar'></span>");
                    var c = $("#spFechaF");
                    c.append(x);
                }
            });
            
            $("#txtFechaF").datepicker({
                showOn: "button",
                dateFormat: "dd/mm/yy",
                maxDate: "+0d",
                onClose: function( selectedDate ) {
                    $( "#txtFechaI" ).datepicker( "option", "maxDate", selectedDate );
                    var y = $("#divFechaI button");
                    y.text("");
                    y.addClass("btn btn-success");
                    y.append("<span class='glyphicon glyphicon-calendar'></span>");
                    var d = $("#spFechaI");
                    d.append(y);
                }
            });
            
            var divFechaI = $("#divFechaI");
            divFechaI.append("<span id='spFechaI' class='input-group-btn'>");
            
            var divFechaF = $("#divFechaF");
            divFechaF.append("<span id='spFechaF' class='input-group-btn'>");
            
            var btnFechaI = $("#divFechaI button");
            btnFechaI.text("");
            btnFechaI.addClass("btn btn-success");
            btnFechaI.append("<span class='glyphicon glyphicon-calendar'></span>");
            
            var btnFechaF = $("#divFechaF button");
            btnFechaF.text("");
            btnFechaF.addClass("btn btn-success");
            btnFechaF.append("<span class='glyphicon glyphicon-calendar'></span>");
            
            var spFechaI = $("#spFechaI");
            spFechaI.append(btnFechaI);
            
            var spFechaF = $("#spFechaF");
            spFechaF.append(btnFechaF);
            
        });//end doc ready
    </script>

Un saludo,
Juan Daniel.

jueves, 19 de diciembre de 2013

Funciones sobre fechas en Javascript / jQuery

Hace unos días estuve renegando un poco en el trabajo teniendo que validar algunas fechas, y calcular un intervalo de tiempo para una fecha dada, desde el lado cliente.
Para esto decidí utilizar Javascript y un poco de jQuery.

Por este motivo decidí realizar mi primer post con funciones que creé, y que me facilitaron dicha manipulación de fechas.

Como primera función, tenemos la que calcula si un año es bisiesto o no:
// Función que valida si un año es bisiesto o no.
// Parámetros: anio = el año a validar.
// Retorno: true o false.
function esBisiesto(anio) {
    return ((anio % 4 == 0 && anio % 100 != 0) || anio % 400 == 0) ? true : false;
};
Esta función es bastante simple, y la encontré en algún lugar en la web.

Luego, creé una función para validar una fecha ingresada, en el formato que se utiliza en Argentina (dd/mm/yyyy):
// Función que valida si una fecha es válida en formato dd/mm/yyyy.
// Parámetros: fecha a validar en formato dd/mm/yyyy.
// Retorno: true o false.
function validaFecha(fecha) {
    var arreglo = fecha.split("/");

    if (arreglo.length != 3)
        return false;

    if (!parseInt(arreglo[0]) || !parseInt(arreglo[1]) || !parseInt(arreglo[2]))
        return false;

    var dia = parseInt(arreglo[0]);
    var mes = parseInt(arreglo[1]);
    var anio = parseInt(arreglo[2]);

    if (dia < 1 || dia > 31 || mes < 1 || mes > 12 || anio < 1)
        return false;

    switch (mes) {
        case 4:
        case 6:
        case 9:
        case 11:
            if (dia > 30)
                return false;
            break;
        case 2:
            if (esBisiesto(anio)) {
                if (dia > 29)
                    return false;
            }
            else {
                if (dia > 28)
                    return false;
            }
    }
    return true;
};
Para esta función, termino utilizando la primera del post (esBisiesto()).

Lo siguiente, es una función para validar el intervalo en días a sumar o restar a mi fecha previamente validada. Este intervalo debe respetar un formato definido por un signo (+ o -) dependiendo de la operación a realizar, y seguido inmediatamente el número de días a sumar o restar (p.e. +10 o -35).
// Función que valida si un intervalo a sumar o restar a una fecha es válido, en formato +d o -d.
// Parámetros: intervalo = intervalo a validar en formato +d o -d (d = cantidad de días).
// Retorno: true o false.
function validaIntervalo(intervalo) {
    var primero = intervalo.substring(0, 1);
    var valor = intervalo.substring(1, intervalo.length);

    if (primero != "+" && primero != "-")
        return false;

    if (!parseInt(valor))
        return false;

    return true;
}

La última función es la que calcula la nueva fecha, a partir de la fecha inicial y el intervalo indicado.
// Función que calcula una nueva fecha a partir de una inicial, y un intervalo.
// Parámetros: fecha = fecha inicial en formato dd/mm/yyyy.
//             intervalo = intervalo a sumar o restar a la fecha inicial, en formato +d o -d (d = cantidad de días).
// Retorno: true o false.
function nuevaFecha(fecha, intervalo) {
    var arrayFecha = fecha.split('/');
    var interv = intervalo.substring(1, intervalo.length);
    var operacion = intervalo.substring(0, 1);
    var dia = arrayFecha[0];
    var mes = arrayFecha[1];
    var anio = arrayFecha[2];
    var fechaInicial = new Date(anio, mes - 1, dia);
    var fechaFinal = fechaInicial;
    
    if (operacion == "+")
        fechaFinal.setDate(fechaInicial.getDate() + parseInt(intervalo));
    else 
        fechaFinal.setDate(fechaInicial.getDate() - parseInt(intervalo));
    
    dia = fechaFinal.getDate();
    mes = fechaFinal.getMonth() + 1;
    anio = fechaFinal.getFullYear();

    dia = (dia.toString().length == 1) ? "0" + dia.toString() : dia;
    mes = (mes.toString().length == 1) ? "0" + mes.toString() : mes;

    return dia + "/" + mes + "/" + anio;
};
Esta función retorna un string con la nueva fecha, luego de aplicarle la suma o resta del intervalo indicado, en formato "dd/mm/yyyy".

Por último dejo un ejemplo de un archivo HTML, donde pueden probarse las funciones mostradas anteriormente.
<html>
    <head>
        <title>Fechas en Javascript / jQuery</title>
        <style>
            div {
                padding: 5px;}
        </style>
        <script src="./Scripts/jquery-1.10.2.js"></script>
        <script src="./Scripts/fechas.js"></script>
    </head>
    <body>
        <div>
            <label>Fecha inicial: </label>
            <input id="txtFechaInicial" style="width: 100px;" type="text" />
        </div>
        <div>
            <label>Intervalo: </label>
            <input id="txtIntervalo" style="width: 50px;" type="text" />
        </div>
        <div>
            <button id="btnCalcular">Clacular</button>
        </div>
        <div>
            <label id="lblFechaFinal">Fecha final: </label>
        </div>
        <script>
            $(document).ready(function() {
                
                $("#btnCalcular").click(function () {
                    var fecha = $("#txtFechaInicial").val();
                    var intervalo = $("#txtIntervalo").val();

                    if (validaFecha(fecha)) {
                        if (validaIntervalo(intervalo)) {
                            var newFecha = nuevaFecha(fecha, intervalo);
                        }
                        else {
                            alert("Error en el formato del intervalo!");
                        }
                    }
                    else {
                        alert("Error en el formato de la fecha!");
                    }

                    $("#lblFechaFinal").html("Fecha final: " + newFecha);
                });

            });
        </script>
    </body>
</html>

Un saludo,
Juan Daniel.