ui5

Buenas prácticas con javascript en UI5

21 noviembre 2022 · 7 minutos de lectura

Escribir buen código es importante, tanto para uno mismo como para el resto de compañeros. Si escribimos buen código, ganarás enteros para que tu carrera profesional prospere 🚀. El resto de compañeros tendrán una buena referencia tuya y preferirán trabajar en proyectos en los que hayas participado. Por el contrario, si no sigues buenas prácticas, los que vengan detrás sufrirán con los evolutivos, empezará una espiral donde el código irá a peor y la complejidad irá en aumento.

Es muy importante reducir el coste de entrada a los siguientes evolutivos. Típicamente, este coste se mide en 🍿 wtf por minuto. Cuanto menor sea este coste, mayor calidad mantendrá el código con el paso de los evolutivos.

Soy de la opinión de que tenemos el deber de dejar huella en tu proyecto por la simplicidad y no por la complejidad. Hay un poco de mito en que el programador bueno es el que hace código que solo él mismo puede entender. Es una forma de intentar ser necesario en evolutivos futuros. Evidentemente, no suele ser una buena señal. Todavía menos desde el punto de vista empresarial…

En este artículo vamos a repasar los aspectos más básicos de la programación en javascript con UI5.

¡const y let!

Muchos de los proyectos que se hacen en UI5 han tenido que dar soporte a grandes navegadores como Internet Explorer. Dado que históricamente los proyectos UI5 no empaquetaban ni nada similar (sic), pues el código que llegaba a producción era literalmente el mismo que se escribía. Por lo tanto, si tu aplicación funcionaba en Internet Explorer, no te podías plantear Babel. Como resultado, te veías forzado a utilizar var para declarar variables sin posibilidad de utilizar let y const… A día de hoy, aunque quedan proyectos que siguen funcionando en Internet Explorer (hay empresas que todavía están en proceso de migrar 😅), las apps deberían hacerse con miras al “empaquetado”. Así podemos pasarle Babel durante el proceso y listos.

Dicho rápidamente, la diferencia radica en que el ámbito/scope de las variables no es el mismo. Además, con var podemos redeclarar la variable sin tener ningún error:

var sFramework = 'sapui5';
const sFramework2 = 'sapui5';

if (sFramework === sFramework2) {
  var sFramework = 'openui5';
  let sFramework2 = 'openui5';
  console.log(sFramework); // "openui5"
  console.log(sFramework2); // "openui5"
}

console.log(sFramework); // "openui5"
console.log(sFramework2); // "sapui5"

También considero importante utilizar lo máximo posible las const. Así evitaremos mutar de forma involuntaria variables que probablemente no queremos modificar.

Notación húngara

Dado que javascript es un lenguaje de tipado débil, de primeras no vamos a saber el tipo contenido en una variable. Una forma muy sencilla de dar pistas es utilizar la notación húngara:

const sText = 'Hola';
const iNumber = 8;
const fNumber = 8.25;
const bBoolean = true;
const oObject = { nombre: 'Carlos' };
const aArray = [1, 2, 3];
const dDate = new Date();
const mMap = new Map();
const rPattern = new RegExp();
const $Object = jQuery();
const fnFunction = () => {};

Camelcase y semántica

Para facilitar la lectura, una buena idea sería utilizar camelcase. De la misma manera, los nombres de las variables y funciones ha de ser claro y conciso. No pasa nada si no queda un nombre corto, siempre y cuando se entienda bien:

const nomusur = 'Hola';
const sNombreUsuario = 'Hola';

function mensaje(sNombre) {
function saludoBienvenidaUsuario(sNombre) {
  console.log(`¡Hola ${sNombre}!`)
}

Truthy y Falsy

En la mayoría de los casos, hacer comparativas explícitas suele ser redundante. Vamos a verlo con un ejemplo que a simple vista es evidente:

const bVisible = true;
if (bVisible === true) {
if (bVisible) {
  // 🤝
}

Pues con los truthy o valor verdadero y falsy o valor falso, viene a ser algo similar:

const sTexto = 'Hola mundo';

// Arráncame los ojos 🤒
if (sTexto === '' || sText === null || sText === undefined) {
// Por favor 🤣
if (sTexto) {
  return 🌍;
} else {
  return 🌙;
}
// Evidentemente, esto lo podríamos hacer directamente con una ternaria 😎
return sText ? 🌍 : 🌙;

Aquí tienes más información sobre truthy y falsy. Esto pasa porque javascript utiliza una conversión de tipos en los contextos boleanos llamada coerción de tipos. Veamos algún ejemplo de manera aislada:

const sTexto = '5';
const iNumero = 3;
const sConcatenado = sTexto + iNumero; // "53"
const sConcatenado2 = iNumero + sTexto; // "35"
const iResta = sTexto - iNumero; // 2
const iResta2 = iNumero - sTexto; // -2

Conversiones de tipos

Un código de menos longitud no significa que sea mejor. Para convertir los tipos, intenta que el código sea lo más claro posible en lo que está haciendo. Para ello, queda más claro si utilizas los métodos nativos en vez de los operadores:

const sTexto = '2';
const iNumero = 8;

+sTexto; // 2
Number(sTexto); // 2

!!iNumero; // true
Boolean(iNumero); // true

'' + iNumero; // "8"
String(iNumero); // "8"

Doble igualdad y triple igualdad

Por norma general, hay que evitar el uso de la == doble igualdad. Lo correcto es utilizar ===. La diferencia reside precisamente en la coerción de tipos de la que hemos hablado. Con la doble igualdad la comparación hará una conversión de tipos que probablemente no sea la que esperamos:

const nullable = null;
const undefinable = undefined;

nullable == undefinable; // true
nullable === undefinable; // false

const bFalso = false;
const iCero = 0;
bFalso == iCero; // true
bFalso === iCero; // false

// Ojo, que este ultimo lo podemos transformar en
const bVerdadero = true;
const iUno = 1;
bVerdadero == iUno; // true
bVerdadero === iUno; // false

Concatenación de métodos

Este punto puede ser algo controvertido y habrá defensores de lo opuesto de lo que voy a explicar. Bajo mi experiencia, es mejor no concatenar expresiones. Hacerlo tiende a que se repita la misma concatenación y según como, se haga un código ilegible. Es más sencillo de ver en un ejemplo:

const sPropiedad = oEvent.getSource().getBindingContext().getObject().sPropiedad;

const oSource = oEvent.getSource();
const oContext = oSource.getBindingContext();
const oObject = oContext.getObject();
console.log(oObject.sPropiedad); // "valor"

Bueno, vale, ahora son 4 líneas de código en vez de una. Ahora bien, cuando entras en un proyecto a hacer un evolutivo, puede ser que te encuentres algo así:

// 💀 Mátame
if (oEvent.getSource().getBindingContext().getObject().sPropiedad === 'valor') {
  oEvent.getSource().getBindingContext().getObject().sPropiedad2 = 'valor por defecto';
  oEvent.getSource().getBindingContext().getObject().sPropiedad3 = oEvent
    .getSource()
    .getBindingContext()
    .getObject().sPropiedad4;
  oEvent.getSource().getBindingContext().getObject().sPropiedad5 = oEvent.getSource().getText();

  oEvent.getSource().getBindingContext().getModel().updateBindings();
}

// 🎉 En cambio si lo haces así la cosa cambia
const oSource = oEvent.getSource();
const oContext = oSource.getBindingContext();
const oObject = oContext.getObject();

if (oObject.sPropiedad === 'valor') {
  oObject.sPropiedad2 = 'valor por defecto';
  oObject.sPropiedad3 = oObject.sPropiedad4;
  oObject.sPropiedad4 = oSource.getText();

  const oModel = oContext.getModel();
  oModel.updateBindings();
}

Y no, esto no es una exageración. Puedes imaginarte proyectos que tienen controllers con miles de líneas así. Entonces llegas tú y tienes que plantearte que hacer: refactorizar, arreglarlo un poco, seguir la línea marcada… Lamentablemente, la respuesta más cuerda suele ser la última, pues el tiempo es limitado y querrás evitar a toda costa provocar errores que estén fuera del ámbito de tu propio evolutivo.

Por otro lado, si te preocupa que el código que llegue a producción ocupe mucho, piensa que durante el proceso de empaquetado de la aplicación probablemente haya un uglificado en el que básicamente se reducirá el código:

const oSource = oEvent.getSource();
const oContext = oSource.getBindingContext();
const oObject = oContext.getObject();
oObject.sPropiedad1 = 'nuevo valor';

// Una vez empaquetado y uglificado
t.getSource().getBindingContext().getObject().sPropiedad1 = 'nuevo valor';

Aunque hay muchísimas cosas más a tener en cuenta, lo dejaré para más artículos futuros que iré añadiendo a esta serie. Hablaremos tanto de las cosas que me he dejado en este artículo, como en el ámbito del proyecto: estructura, herramientas de desarrollo, empaquetado, servidor, etc.

Espero que este artículo te haya sido útil y ameno. Si te lo ha parecido, agradecería que consideraras compartirlo.