MsgBox component with TypeScript, Bootstrap and RequireJS – Part 2

TypeScript

Second and last part of the message box component created with TypeScript, Bootstrap and Require. In part 1 it was explained the front-end part for showing the component in action (index.html), and the RequireJS configuration file, event handler and app.ts.

Now it is turn to show the code for MsgBox.ts, where the action is coded, and Menu.ts for testing the component.

The complet code is available on Github.

Msgbox.ts

El código donde se desarrolla toda la acción está en msgbox.ts is the file where all action is coded, and it’s a complex file because it has been prepared for using HandleBarsJS templates for loading hbs files, so, you need to include the amd references and after that, prepare the variables for load the templates using require (at the beginning of the file).

When the component starts, all forms are loaded to a DIV before use them. If you don’t offer a DIV, the component will create one for us. With all components loaded, everything is ready for action.

The component also included some functions for validating the login form and the password change form, and also, everything is ready for firing alerts in case of errors. Of course, you can add and modify everything in order to fit your needs!!

Last line of the component is export MsgBox.

/// <reference path="../typings/jquery.d.ts" />
/// <reference path="../typings/bootstrap.d.ts" />
/// <reference path="interfaces.ts" />
/// <reference path="LiteEvent.ts" />

/// <amd-dependency path="hbs!templates/msgbox" />
/// <amd-dependency path="hbs!templates/login" />
/// <amd-dependency path="hbs!templates/cambiapass" />
/// <amd-dependency path="hbs!templates/yesno" />

var __UPDATED__ = '2015.11.13';
var __VERSION__ = "1.2.0";
var __AUTHOR__ = 'David Trillo';
var __WEBSITE__ = '';

// http://jschr.github.io/bootstrap-modal/
import $ = require("jquery");
import LiteEvent = require("liteevent");

var MsgBoxTemplate:Function = require('hbs!templates/msgbox');
var LoginTemplate:Function = require('hbs!templates/login');
var CambiaPassTemplate:Function = require('hbs!templates/cambiapass');
var YesNoTemplate:Function = require('hbs!templates/yesno');


class MsgBox {
    private _s = 'show';
    private _h = 'hide';
    private _f = 'form';
    
    private base: JQuery;
    private template:string ;
    private msgbox: JQuery;
    
    private _div_alert: JQuery; // div_alert
    private _div_alert_close_btn: JQuery;
    
    private onAlert = new LiteEvent<boolean>();
    private onLogin = new LiteEvent<string>();   
    private onLogout = new LiteEvent<void>();     
    private onChangePass = new LiteEvent<string>();     
    private onYesNoCancel = new LiteEvent<string>();
    
    public get AlertOK(): ILiteEvent<boolean> { return this.onAlert; } 
    public get LoggedIn(): ILiteEvent<string> { return this.onLogin; } 
    public get LoggedOut(): ILiteEvent<void> { return this.onLogout; }
    public get PassChanged(): ILiteEvent<string> { return this.onChangePass; }
    public get YesNoCancel(): ILiteEvent<string> { return this.onYesNoCancel; }
    
    // Funciones privadas
    private _creadiv(nombre): JQuery {
        var div = "

<div id='" + nombre + "'></div>


";
        this.base.append(div);
        return this.base.find('#'+nombre);
    }
    
    // VALIDACION para Mostrar MsgBOX
    private _valida_opciones_msgbox(opc: IAlert) {
        if (opc.txt_boton_cerrar == undefined) {opc["txt_boton_cerrar"] = "Cerrar";}
        if (opc.boton_cerrar == undefined) {opc["boton_cerrar"] = true;}
        if (opc.timer == undefined) {opc["timer"] = 0; }
        if (opc.modal_header_class == undefined) { opc["modal_header_class"] = "";}
        
        return opc
    }
    
    constructor(div_base?: JQuery) {
        if (div_base == undefined) {
            this.base = $('

<div id="msgbox_container" />').appendTo('body');
        } else { this.base = $(div_base);}
        this.msgbox = this._creadiv('div_msgbox');    // DIV q almacena los .hbs
    }
    
    // Alert MsgBox
    public show_alert(opc: IAlert) {
        var div = "#msgbox";
        var that = this;
        opc = that._valida_opciones_msgbox(opc);
        var tmp = MsgBoxTemplate(opc);
        that.msgbox.html(tmp);
        var div_msg = that.msgbox.find(div);
        
        div_msg.modal(that._s);
        
        if (opc.timer > 0) {
            setTimeout(() => { div_msg.modal(that._h); }, opc.timer);
        }
        div_msg.find('#btn_login').on('click', (e) => { 
            e.preventDefault();
            div_msg.modal(that._h);
            that.onAlert.trigger(true);    
        });
    }
    
    // Login MsgBOX
    show_login(opc): any {
        var div = '#form_login';
        var that = this;
        var tmp = LoginTemplate(opc);
        this.msgbox.html(tmp);
        var form = this.msgbox.find(div);
        this._clear_login(form);
        form.modal(that._s);
        form.find('#btn_login').on('click', (e) => { 
            e.preventDefault();
            form.modal(that._h);
            if (that._valida_login(form)) {
                var cadena = form.find(that._f).serialize();
                that.onLogin.trigger(cadena);    
            }
            else {
                console.log('Sin datos para Login')
            }
        });
    }
    
    private _valida_login(form): boolean {
        var campos = ["user", "password"];
        var tmp: string = '';
        var valores = [];
        var devuelve_cero = false;
        for (var i = 0; i < campos.length; ++i) { tmp = form.find('#' + campos[i]).val(); if (tmp.length == 0) { devuelve_cero = true;} else {valores.push(tmp)} } return (valores.length == campos.length) } private _clear_login(form: JQuery): void { var inputs = form.find(':input'); inputs.each((i) => {
            $(i).val('');
        })
    }
    
    // CambiaPass MsgBox
    show_cambiapass(opc: ICambiaPass) {
        var delay: number = 1000;
        var that = this;
        var tmp = CambiaPassTemplate(opc);
        this.msgbox.html(tmp);
        var form = this.msgbox.find('#form_cambiapass');
        form.modal(that._s);
        form.find('#btn_login').on('click', (e) => { 
            e.preventDefault();
            var subform = form.find(that._f);
            var cadena = subform.serialize();
            form.modal(that._h);
            var que: number = that._valida_cambiapass(subform); // Que devuelve 0 si no hace nada, 1 si es OK y 2 si hay error!
            if (que == 1) {
                // Si todo es correcto!
                that.onChangePass.trigger(cadena);
            }
            else if (que == 2){
                var aler: IAlert = that._valida_opciones_msgbox(opc.alert_change_password_error);
                setTimeout(() => { that.show_alert(aler); }, delay);
            }
        });
        
    }
    
    // Valida los datos de cambiapass
    private _valida_cambiapass(form: JQuery): number {
        var campos = ["oldpass", "newpass", "newpass2"];
        var tmp: string = '';
        var valores = [];
        var devuelve_cero = false;
        for (var i = 0; i < campos.length; ++i) { tmp = form.find('#' + campos[i]).val(); if (tmp.length == 0) { devuelve_cero = true;} valores.push(tmp) } if (devuelve_cero) { return 0 } var bln1: boolean = valores[0] != valores[1]; var bln2: boolean = valores[2] === valores[1]; var bln3: boolean = (valores[1].length > 0);
        var bln4: boolean = (valores[0].length > 0)
        // Para que sea True, debe de ocurrir que oldpass != newpass y que newpass == newpass2
        
        return (bln1 && bln2 && bln3 && bln4) ? 1 : 2;
    }
    
    // Yes-NO MsgBox
    show_yesno(opc: IYesNoCancel) {
        var delay: number = 1000;
        var div = "#msgbox_yesnocancel";
        var that = this;
        var tmp = YesNoTemplate(opc);
        that.msgbox.html(tmp);
        var div_msg = that.msgbox.find(div);
        div_msg.modal(that._s);
        
        div_msg.find('#btn_yes').on('click', (e) => { 
            e.preventDefault();
            div_msg.modal(that._h);
            opc.funcion_click_yes('Pulsado SI');
            setTimeout(() => { that.onYesNoCancel.trigger('yes');; }, delay);
        });
        div_msg.find('#btn_no').on('click', (e) => { 
            e.preventDefault();
            div_msg.modal(that._h);
            opc.funcion_click_no('Pulsado NO');
            setTimeout(() => { that.onYesNoCancel.trigger('no');; }, delay);    
        });
        div_msg.find('#btn_cancel').on('click', (e) => { 
            e.preventDefault();
            div_msg.modal(that._h);
            opc.funcion_click_cancel('Pulsado CANCEL');
            setTimeout(() => { that.onYesNoCancel.trigger('cancel');; }, delay);    
        });
        
    }
    
    set_div_alert(div: string) {
        this._div_alert = $(div);
    }
    div_alert(html: string, timer = 0) {
        this._div_alert.show();
        this._div_alert_close_btn = this._div_alert.find('button');
        var _msg = this._div_alert.find('#mensaje');
        _msg.html(html);
        if (timer > 0) { setTimeout(() => { this._div_alert_close_btn.hide(); }, timer);
        }
    }
    div_alert_stop() {
        // this._div_alert.addClass('hide');
        this._div_alert.hide();
    }
}    
export = MsgBox ;

Menu.ts

The last file to describe is menu.ts, where you can test the component. The constructos of the class calls the method refresca, where all action can be refreshed.

Because of the code is similar for all buttons, I will only comment one of them:


this.div_base.find('#simple').on('click', (e) => {
e.preventDefault();
var opc: IAlert = {
titulo: 'Test',
mensaje: 'Esto es una prueba',
txt_boton_cerrar: 'Cerrar',
btn_class: 'btn-default',
modal_header_class: 'modal-header-success',
boton_cerrar: true,
funcion_click_cerrar: that.test
}
that.msg.show_alert(opc);
that.msg.AlertOK.on((bln) => { that.test('Simple'); });
});

En concreto, analizamos el evento click sobre el botón simple. Es fundamental parar la ejecución de evento con preventDefault(), para que el siguiente paso sea preparar la variable con todas las opciones sobre el mensaje a generar. En este caso, usamos IAlert (definido en interfaces.ts).

La siguiente instrucción es mostrar la alerta, enviando las opciones de configuración.

La última instrucción es la que “espera” a que el evento suceda (trigger) para que ocurra algo. En este caso, se llama a la función test(), que envia un mensaje a la consola.

var that = this

I would like to call your attention for using var that = this, because it is a handy resource within TS. This is a reference for the owner of the functoins and its value depends on the function or object that calls it. That is a variable that allows you to reference a object that can be used later when This moves to a different owner. It is a bit confusing, but if you are used to AJAX, you understand it perfectly. If doesn’t, so, wellcome to Javascript!

Why I create this component

I bet it can be more easy to use of the Bootstrap modal plugins available (there is a bunch of them!), but the fact of creating my own one has made me to improve my TypeScript skills, also learn more about HandleBarsJS, and fo course, use it in several projects.

msgbox component

msgbox component

And that’s all. I hope you read the series carefully and write me if you have any doubt or you have any improvement to add!

Happy coding!