ui5

Estilando UI5 dinámicamente: CustomData

23 diciembre 2022 · 4 minutos de lectura

Pues sí. En UI5 se puede tocar el CSS de la aplicación. Otro asunto es discernir qué cosas podemos o debemos cambiar. Es muy fácil venirse arriba y empezar a escribir muchas líneas de CSS, ya que puede resultar sencillo obtener un cambio en la interfaz rápidamente. El problema generado de hacerlo sin control probablemente no lo viva el mismo desarrollador que lo escribe. Lo sufrirá (o no, si se hace bien) el futuro encargado de hacer el upgrade a una versión superior de las librerías de UI5.

En este artículo no voy a entrar a ver qué cosas se deben o no hacer, ni que cosas están bien o mal. Me voy a centrar en dar una posible solución a un problema o limitación que tiene UI5:

La propiedad class de los controles UI5 no es bindable

Esto implica que no podemos utilizar clases CSS dinámicamente. Lo podemos ver con un ejemplo: un listado de materiales con sus respectivos stocks:

  • 20 Ordenadores
  • 40 Teclados
  • 10 Ratones

Partiendo de la base de que utilizaremos un derivado de sap.m.ListBase, si necesitamos marcar en rojo los materiales con stock < 15, en UI5 tenemos distintas posibilidades:

  1. Utilizar la propiedad highlight de cada ListItem, bindándola y utilizando Error en los casos que cumplan la condición.
  2. Añadir un sap.m.Icon en rojo en cada ListItem que cumpla la condición.
  3. Utilizar CustomData en el número y ponerle un valor dinámico.

En este artículo vamos a comentar la tercera opción, pero: ¿qué es un CustomData?

Los controles UI5 pueden guardar datos internamente en n parejas de clave/valor. Podemos hacer que estas parejas se pinten como un atributo del elemento del DOM. Si la combinamos con una clase css, podemos hacer distintos selectores css para una misma clase

Tienes mas informacion sobre CustomData en la api de UI5.

CustomData en HTML

Bien, esto con HTML + CSS lo podemos ver de la siguiente manera:

<span
  data-status="error"
  class="stock"
>
  10
</span>
.stock[data-status='error'] {
  color: red;
}

CustomData en UI5

Pues en una vista XML de UI5 vendría a ser así (el ejemplo obvia partes del código de la Table):

<Table items="{MATERIALES>/}">
  <ColumnListItem>
    <Text text="{MATERIALES>stock}">
      <customData>
        <core:CustomData key="status"
          value="{= ${MATERIALES>stock} &lt; 15 ? 'success' : 'error' }"
          writeToDom="true">
      </customData>
    </Text>
    </cells>
  </ColumnListItem>
</Table>

Con key indicamos el nombre del atributo y en value podemos bindar el valor como en cualquier otra propiedad. Con esto sería suficiente para almacenar el valor en ese control, pero lo que necesitamos es que exponga un atributo en el DOM. Para ello marcamos el flag writeToDom.

Por otro lado, quizás te has fijado que no he utilizado directamente el carácter < en la condición. Hay que ir con cuidado en los XML ya que hay ciertos carácteres que se han de utilizar codificados:

  • < ➡️ &lt;
  • & ➡️ &amp;

La sap.m.Table con los materiales que hemos mencionado y el CustomData en el stock quedaría así:

Responsive Table con CustomData

También podemos hacerlo con javascript de la siguiente manera. Utilizaremos una función factory para generar los items de la tabla (en el ejemplo también estoy obviando el resto de celdas):

factoryMateriales() {
  const oText = new Text({
    text: '{MATERIALES>stock}'
  }).addStyleClass('stock'):
 
  // Podemos usar un binding con string
  oText.data('status', "{= ${MATERIALES>stock} < 15 ? 'error' : 'success' }", true);
 
  // O hacer un binding con objeto
  oText.data('status', {
    path:'MATERIALES>stock',
    formatter: (stock) => {
      return stock < 15 ? 'error' : 'success'
    }
  }, true);
 
  return new ColumnListItem({
    cells: [
      oText
    ]
  });
}

Si en algún momento necesitamos leer el valor de un CustomData de cualquier control, podemos utilizar la misma función data(), indicando únicamente en el primer parámetro la clave a recuperar:

checkStatus(oControl) {
  const status = oControl.data('status');
  if (status === 'error') {
    // error
  } else {
    // success
  }
}

En conclusión, se puede sortear la limitación que tiene UI5 en la propiedad class de una manera mas o menos elegante. Ahora bien, no hay que abusar de esto ni llenar las aplicaciones UI5 con CSS 😂.

Si te ha parecido interesante el artículo, agradecería que lo compartieras. ¡Hasta el próximo!