En Java y C# es muy común utilizar la sobrecarga de operadores para, por ejemplo, tener un método con el mismo nombre pero distintos parámetros.

    public String sayHi(String name){
        return "hi " + name +"!";
    }

    public String sayHi(String first, String last){
        return sayHi(first + " " + last);
    }

    public String sayHi(String first, String last, String city){
        return sayHi(first, last) + "from " + city;
    }

En javascript no existe la sobrecarga de operadores. Pero podemos hacer algo parecido si pasamos un objeto como parámetro a nuestra función, por ejemplo

$scope.sayHi = function(options){
    var greeting = 'hi '
    if(typeof options.first !== 'undefined' && typeof options.last !== 'undefined' && typeof options.city !== 'undefined'){
        greeting += options.first + ' ' + options.last + ' from ' + options.city
    }else if(typeof options.first !== 'undefined' && typeof options.last !== 'undefined'){
        greeting += options.first + ' ' + options.last
    }
    else if(typeof options.name !== 'undefined'){
        greeting += options.name
    }
    return greeting + '!'
}

Fuentes:
http://stackoverflow.com/questions/456177/function-overloading-in-javascript-best-practices
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Default_parameters

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/

chrome memory profiler

chrome memory profiler

Después de tener una aplicación web abierta por un tiempo prolongado, empecé a notar que esta incrementaba mucho el uso de memoria. Este problema comenzó a presentarse cuando introduje un ‘timer’ para cambiar de vistas cada cierto tiempo. Al parecer no se estaba liberando la memoria en algún lado.
Crear y desplegar vistas en backbone es algo aparentemente sencillo, pero –como ya fue analizado– realmente no lo es.

Al principio recreaba las vistas de la siguiente manera

app.Router = Backbone.Router.extend({
    routes: {
        "": "audiencias",
        "audiencias": "audiencias",
        "screensaver": "screensaver"
    },
    initialize: function(){
        this.$content = $('#content')
    },

audiencias: function(){
    app.views.audiencias = new app.views.Audiencias({model: new app.models.AudienciaCollection()})
    this.$content.html(app.views.audiencias.render().el)
},

screensaver: function(){
    app.views.screensaver = new app.views.Screensaver()
    this.$content.html(app.views.screensaver.render().el)
}
})

Para evitar recrearlas y sólo hacer un ‘render’ cada que cambiaba de vista, lo modifiqué de la siguiente manera y la fuga de memoria ya no se presentó

app.Router = Backbone.Router.extend({
    routes: {
        "": "audiencias",
        "audiencias": "audiencias",
        "screensaver": "screensaver"
    },

initialize: function(){
    this.$content = $('#content')
    app.views.audiencias = new app.views.Audiencias({model: new app.models.AudienciaCollection()})
    app.views.screensaver = new app.views.Screensaver()
},

audiencias: function(){
    app.views.audiencias.setElement(this.$content).render()
},

screensaver: function(){

    app.views.screensaver.setElement(this.$content).render()
}
})

Fuentes:
http://ianstormtaylor.com/rendering-views-in-backbonejs-isnt-always-simple/
http://lostechies.com/derickbailey/2011/09/15/zombies-run-managing-page-transitions-in-backbone-apps/

screensaver

screensaver

Ahora que las Smart TV’s son más comunes, existe un número mayor de aplicaciones y usos que les podemos dar. Desde hace ya bastante tiempo, se han utilizado pantallas para desplegar la información de los vuelos en los aeropuertos.

Desarrollé una aplicación parecida al tablero del aeropuerto y estaba preocupado por el efecto “burn-in”, así que decidí utilizar un salvapantallas (screensaver). Afortunadamente un equipo de Adobe desarrolló un proyecto (git repo) que hace precisamente esto, y lo hace muy bien.

Para agregarlo en mi proyecto, solo copie los archivos en una carpeta ‘screensaver’ e inserte un iframe en la página que lo quería mostrar

<iframe id="saver" src="screensaver/saver.html"></ iframe>

y para que iniciara inmediatamente, sin la necesidad de la intervención del usuario

$(document).ready(function(){
this.$('#saver').load(function(){
this.contentWindow.eve('menu.instagram')
this.contentWindow.eve('go')
})
})

con esto es como si el usuario hubiera dado clic en el botón de instagram y luego en el botón de play 🙂

Fuentes:
http://stackoverflow.com/questions/251420/invoking-javascript-code-in-an-iframe-from-the-parent-page
http://stackoverflow.com/questions/12199797/why-is-iframe-contentwindow-null

Para acceder a los objetos de una colección en una plantilla de Handlebars tienes que pasar dichos objetos como un parámetro a la función template. Si utilizas Backbone y tu colección de objetos es una colección de modelos, puedes utilizar la función toJSON para convertirlos en objetos javascript y que puedan ser interpretados por Handlebars.

Supongamos que tenemos los siguientes objetos (modelos)

app.models.Equipo = Backbone.Model.extend({

    initialize: function () {

    },

    sync: function (method, model, options) {
    if (method === 'read') {
        app.adapters.equipos.findById(parseInt(this.id)).done(function (data) {
            options.success(data)
        })
    }
    }
})

app.models.EquipoCollection = Backbone.Collection.extend({

    model: app.models.Equipo,

    sync: function (method, model, options) {
    if (method === 'read') {
        app.adapters.equipos.findAll().done(function(data){
            options.success(data)
        })
    }
    }
})

y nuestra plantilla es la siguiente

    <div class="row">
        {{#each equipos}}
        <div class="col-6">
            <a href="equipos/#{{name}}" class="thumbnail text-center">
                <img src="{{logoUrl}}"/>
            </a>
        </div>
        {{/each}}
    </div>

Entonces para pasar nuestros objetos a la plantilla, lo haríamos de la siguiente manera

app.views.EquiposListView = Backbone.View.extend({
    initialize: function(){
        this.equipos = new app.models.EquipoCollection()
        this.equipos.on('reset', this.render, this)
        this.equipos.fetch({reset: true})
    },

    render: function(){
        this.$el.html(this.template({equipos: this.equipos.toJSON()}))
        return this
    }

})

Notesé el uso de la función toJSON

Fuentes:
http://stackoverflow.com/questions/12439471/rendering-handlebars-template-with-backbone
http://stackoverflow.com/questions/14170298/how-to-access-backbone-model-properties-in-a-handlebar-template

Cuando tienes varios reportes, es buena idea crear una librería para reutilizar elementos en los reportes. Un ejemplo común es cuando necesitamos usar el mismo encabezado y pie de página para los reportes.

BIRT nos permite agregar algunos elementos dinámicos -tales como el número de páginas, la fecha e incluso un valor obtenido mediante código javascript en un DynamicText- El problema es que esto dependerá de cada reporte, así que ¿cómo agregamos elementos dinámicos desde una librería a nuestro reporte?

Pues habrá que crear una “master page” en la librería donde pongamos elementos “DynamicText” en el encabezado y que estos desplieguen el valor de un parámetro. Después podemos modificar el valor del parámetro en cada reporte, y así, tener un título personalizado pero manteniendo el formato.

Si además, queremos obtener el título mediante una consulta a la base de datos. Lo que tenemos que hacer es crear el DataSource a la base de datos y un DataSet con la consulta que obtiene el título, por ejemplo


SELECT DESCRIPCION
FROM OFICINAS
WHERE ID=:OFICINA

Luego, debemos poner el siguiente código en el evento “beforeFactory” de nuestro reporte


//public final static int MODE_GENERATION = 1; //This mode is for generate the report document. Typically used in Report Engine run task
//public final static int MODE_PRESENTATION = 2; //This mode is for present data that saved in report document, without further data manipulation operation. Typically used in report engine render task
//public final static int DIRECT_PRESENTATION = 3;//This mode is to execute the data query without create report document. It is used in report engine runAndRender task.
//public final static int MODE_UPDATE = 4; //This mode is used to update the existing report document with new queries. Typicially used in IV.
importPackage( Packages.org.eclipse.birt.report.model.api );
importPackage(Packages.java.lang);
importPackage(Packages.java.util);
importPackage(Packages.org.eclipse.birt.report.data.adapter.api);
importPackage(Packages.org.eclipse.birt.report.model.api);
importPackage(Packages.org.eclipse.birt.data.engine.api.querydefn);
var myconfig = reportContext.getReportRunnable().getReportEngine().getConfig();
var des = DataRequestSession.newSession(myconfig, new DataSessionContext(3));
var dsrc = reportContext.getDesignHandle().findDataSource("dsrcOficina");
var dset = reportContext.getDesignHandle().findDataSet("dsetOficina");
des.defineDataSource(des.getModelAdaptor().adaptDataSource(dsrc));
des.defineDataSet(des.getModelAdaptor().adaptDataSet(dset));
var paramBinding = new InputParameterBinding("param_1", new ScriptExpression("\""+ params['IDOFICINA'].value +"\"") );
queryDefinition = new QueryDefinition( );
queryDefinition.setDataSetName( "dsetOficina" );
queryDefinition.addInputParamBinding( paramBinding );
queryDefinition.setAutoBinding(true);
var pq = des.prepare( queryDefinition );
var qr = pq.execute( null );
var ri = qr.getResultIterator( );
if ( ri.next( ) )
{
params["Titulo"].value = ri.getString("DESCRIPCION");
}
ri.close( );
qr.close( );
des.shutdown( );

Es importante poner los nombres exactamente igual que en el diseñador.
En nuestro caso, el parámetro “IDOFICINA” es de tipo String, así que lo ingresamos de la siguiente manera


var paramBinding = new InputParameterBinding( "param_1",new ScriptExpression("\""+ params['IDOFICINA'].value +"\"") );

si fuera un Integer tendría que ser de la siguiente manera


var paramBinding = new InputParameterBinding( "param_1",new ScriptExpression(params['IDOFICINA'].value) );

El valor de ScriptExpression debe ser un bloque de javascript válido, cómo si lo ingresaramos en la ventana “ExpressionBuilder” 🙂

Fuentes:
eclipse forums
birt exchange

Si necesitas modificar el query de un dataset, en tiempo de ejecución, por cualquier motivo. Puedes utilizar la sección “Property Binding” en el editor o modificar la consulta en el evento “beforeOpen” del DataSet. Recuerda que el código que utilices en BIRT es javascript.

Por ejemplo, podemos utilizar un esquema dinámico de la siguiente forma


var query="SELECT FRECEPCION  FROM @ESQUEMA.DOCUMENTOS WHERE ID = ?";

query.replace(/@ESQUEMA/g,params["Esquema"].value);

El esquema lo recibimos como parámetro. En la función “replace” utilizamos la forma global para que reemplace todas las ocurrencias de “@ESQUEMA”, de lo contrario sólo sustituiría la primera.

Fuentes:
http://vhspiceros.blogspot.mx/2008/12/replace-all-en-javascript.html
http://www.w3schools.com/jsref/jsref_replace.asp