miércoles, 8 de marzo de 2017

SAPUI5, oData y filtros: Consultar sólo los datos que nos interesan (I)

Nos habíamos creado una aplicación SAPUI5 para poder consultar una colección de un servicio oData, y lo habíamos hecho de dos formas: Asignando la colección a un elemento de la vista, como hacen las plantilllas del Web IDE, y mediante el método read del objeto oDataModel.

Pero hasta ahora lo único que hemos conseguido es obtener toooooodo el listado de la colección. Sin embargo, ¿qué ocurre si lo que queremos obtener es sólo una parte de ese listado? Que tenemos que aplicar filtros.

Pues venga, vamos a ver cómo podemos aplicar esos filtros.

Qué vamos a hacer


En este post veremos cómo se crean los filtros y como cargar los datos de una selección unívoca (vamos, algo similar a un "select single" de campos clave). En otro post entenderemos cómo lo hace el Web IDE de forma automática.

El objetivo es crear una aplicación que muestre una tabla con los resultados de una colección y nos permita filtrar por Id. Además, cuando seleccionemos una entrada, nos mostrará el Id de dicha entrada debajo de la tabla. Así que:

  • Retocaremos el servicio oData que ya creamos en este post, para añadir el Id al tratamiento de filtros.
  • Crearemos una vista con una tabla para poder ver los datos, dos entradas y un botón para actualizar el filtro y refrescar los resultados y un texto para mostrar la selección hecha.
  • En el controlador, cargaremos la tabla añadiendo el filtro, y leeremos una entrada unívoca cuando seleccionemos un valor en la tabla.

Cómo se hace la llamada


Cuando queremos obtener unos datos de una determinada entidad de un servicio oData, podemos hacer dos cosas:

  • Invocar una entrada unívoca. Esto implica pasarle un valor a todas las propiedades claves. No nos podemos olvidar ninguna. Las propiedades clave se pasan entre paréntesis, separadas por comas y sin espacios en blanco. Como lo mejor es verlo con un ejemplo, sería algo así: /sap/opu/odata/sap/ZMIS_TEXTOS_SRV/TextosGeneralesSet(Id='00000001',Idioma='ES')

  • Si lo que queremos es obtener más de una entrada, o queremos filtrar por un campo que no sea clave, entonces usaremos el parámetro de URL filter. En ese parámetro podemos añadir tantas selecciones como queramos. Podemos ver las distintas opciones aquí, pero vamos a poner el ejemplo de lo que realmente nos interesa: /sap/opu/odata/sap/ZMIS_TEXTOS_SRV/TextosGeneralesSet?$filter=Id le '00000001' and Id ge '00000002'.

Retocando el servicio oData


Lo primero que tenemos que hacer es conseguir que nuestro servicio oData filtre los datos correctamente. No tenemos que añadir un filtro para todas y cada una de las propiedades de cada entidad, pero sí al menos de aquellas que nos interesen.

¿Dónde hacíamos esto? En la clase proveedora de datos, la Data Provider Class. Al crearnos el servicio oData, si usamos los datos que nos proporciona SAP por defecto, nos lo habremos creado con un nombre con formato ZCL_NOMBRE_DE_SERVICIO_DPC_EXT.

Si buscamos una entidad mediante campos clave, estaremos invocando al método  NOMBRECOLECCION_GET_ENTITY. Uno de los parámetros de entrada, IT_KEY_TAB, nos trae los valores de las propiedades clave. Recuperando esos valores, ya podemos hacer el tratamiento que queramos.


Si el Idioma se lo paso en blanco, cojo el sy-langu y punto.

Si lo que buscamos es una colección con filtro, entonces llamamos al método NOMBRECOLECCION_GET_ENTITYSET. En este caso, lo que vamos a usar es el parámetro IT_FILTER_SELECT_OPTIONS para recuperar los distintos valores del filtro.

La diferencia con el post anterior en el que habíamos creado este servicio, es que aquí hemos añadido el filtro para leer el Id.

Tenemos que recordar que los filtros no se calculan automáticamente sólo porque alguien llame a la URL directamente. Todo lo que queramos filtrar lo tenemos que programar, y si no lo hacemos entonces ese filtro, simplemente, no se va a tratar.

El tratamiento podía estar más currado, pero oye, que es un ejemplo

Una vista sencillita


La vista es facilona, que no nos queremos complicar la vida, pero comprensible: Una tabla para mostrar los datos de la colección, que enlazaremos al modelo de datos genérico (sin nombre). Si se selecciona una fila de la tabla (evento itemPress), se llamará a la función cargarEntrada para cargar el Id seleccionado en el texto que hay debajo de la tabla. Para cargar ese dato usaremos un modelo de datos llamado select.

Pero, además, añadimos en la página una cabecera para poder filtrar el ID a buscar, con dos opciones: "Desde" y "Hasta". Cuando el pulse el botón "Buscar" (evento press) la tabla recargará los valores mediante la función cargarTabla. Para poder enlazar los datos usaremos un modelo de datos llamado filterID.

En la tabla, como items, pasamos /. Ya asignaremos el modelo correctamente en el controller.

Y ahora vamos al meollo, el controlador


Lo primero que hacemos en el controlador, en el onInit, es cargar los valores iniciales:

  • Creamos una instancia de tipo oDataModel para apuntar al servicio oData que tenemos. Ya lo usaremos más adelante.
  • También nos creamos un modelo de datos de tipo JSONModel para poder guardar los datos del filtro. Si miramos el nombre del modelo, filterID, podemos ver en la vista que está enlazado con el filtro. Cuando refresquemos el filtro en la vista, este modelo se alimentará automáticamente. Que guay, oye.
  • Finalmente llamamos a la función cargarTabla para poder recuperar los datos.




La función cargarTabla es el encargado de leer los datos mediante filtro. Se llama al inicio, con el filtro en blanco, así que realmente recuperará todo. Pero cuando pulsemos el botón "Buscar" de la vista, vemos que ese botón también llama a esta función. De esta manera podemos volver a cargar la colección con el filtro que hayamos indicado.

¿Y cómo lo hacemos para pasar el filtro? Pues en el método read del oDataModel. Uno de los parámetros de este método es filters, donde podemos indicar los filtros.

El filtro lo podemos crear con la clase sap.ui.model.Filter. En este ejemplo, estamos construyendo un filtro con formato $filter=Id ge <valor_desde> and Id le <valor_hasta>

Si el filtro viene en blanco, pues nada, es como si no hubiésemos usado filtro

Después hacemos la llamada al método read, al que pasamos la url de la entidad y el filtro que hemos creado. Si la llamada tiene éxito, el valor obtenido lo guardamos en el modelo de datos general (sin nombre). Así lo estaremos enlazando con la tabla (que tenía items={/}).

Ya tenemos el código para cargar la tabla.

Lo siguiente es buscar una entrada específica (unívoca) de la colección. Es decir, el GET_ENTITY. Eso lo vamos a hacer con la función cargarEntrada, que hemos enlazado al evento itemPress de la tabla.

Cuando enlazamos una función al evento itemPress, recibiremos un parámetro que nos va a permitir identificar la selección hecha. Es el parámetro que he llamado evt.

Mediante evt.getParameter("listItem").getBindingContext() podemos obtener el contexto enlazado a la selección hecha. Es decir, la entidad que se está mostrando en la fila de la tabla. Gracias a getProperty conseguiremos el valor de la propiedad que queramos de dicha entidad.

Con el Id obtenido mediante las dos primeras líneas ya podemos generar la URL a consultar, añadiendo así las propiedades clave entre paréntesis.

El resultado que obtenemos tras llamar al read, lo pasamos al modelo de datos select, que está enlazado con el texto donde mostramos la selección hecha. ¡Listo, ya obtenemos la selección y la mostramos en pantalla!

El idioma es un campo clave y obligatorio... pero nada nos impide pasarlo en blanco.

Y el resultado es...


Tenemos una tabla que se carga con todos los valores de la colección al iniciarse la aplicación.


Podemos filtrar los datos buscando por el Id.


Y si seleccionamos una entrada, debajo nos muestra el Id de la selección hecha.


Sí, sí, ya lo sé, la aplicación podría ser mucho más bonita y adaptativa (responsive), la tabla podía tener más columnas, el filtro podía ser más chulo y el resultado se podría mostrar en un formulario como el MEV manda. Pero lo suyo era ver cómo funciona el read con filtros. Y lo hemos conseguido, ¿no? ;)

11 comentarios:

  1. Hola de nuevo chicos, como siempre excelente material, queria hacer una consulta que va un poco mas allá de este articulo, hice esta aplicación en eclipse y me funciono todo correcto! hasta la pasé al servidor para ejecutarla desde allí y practicar un poco los catálogos. Ahora quise hacer lo mismo pero haciendo la aplicación desde el web ide y funciona igual,el problema es al deployar al servidor no me muestra la aplicación, abro la consola del navegador y me muestra errores de html. Mi consulta en concreto sería como deployar desde el web ide al servidor ya que con eclipse no se presentan problemas. Muchas gracias si me pueden orientar. Saludos,

    ResponderEliminar
    Respuestas
    1. Hola, Joel

      Tengo un post en el tintero para explicar como desplegar una app en SAP, ¡parece que me has leído la mente!

      En principio la operación es muy sencilla y deberías poder configurarla como la del eclipse sin problemas,así que a bote pronto se me ocurre que hayas creado la app en el WebIDE con una versión superior de SAPUI5 que la de tu servidor SAP. Si tu sapui5 en sap es una 1.28 y la del webide una 1.32 o superior, ni siquiera te arrancará. En ese caso tendrías que volver a crearte la app de nuevo.

      Si no es el caso, habría que ver el error de javascript a ver si dice algo interesante.

      Eliminar
    2. Efectivamente, mi webide está en la versión 1.40 y sap en la 1.28, pensé que al hacer la aplicación en modo 1.28 iba a funcionar pero hasta ahora no he podido mostrar la primera, voy a tratar de conseguir el webide 1.28 a ver si asi no tengo problemas. Muchas Gracias!

      Eliminar
    3. ¿Estás usando el web ide local? En el del Cloud Platform, debería valer con poner que la app es de la 1.28 cuando la creas. El principal problema es que la 1.28 no entiende el manifest,además de otros problemas de compatibilidad con el component.

      En su momento a mí me dio problemas al crear una app en la 1.28 para subir a un sap que también era 1.28 con una subversion mayor, al final lo tuve que crear en la 1.26, pero esa versión creo que ya no está disponible en el webide. Pero más tarde si que pude subir otra app 1.28... meigas :(

      Eliminar
    4. Si, el web ide local ya que con el de cloud plataform si pude crear el destino a mi sap, lo pruebo y me dice que funciona, pero cuando creo la aplicación no me puedo conectar a él y no me muestra el catalogo :(

      Eliminar
  2. ¿Quizá necesitas instalar el SAP Cloud Connector porque el servidor SAP está detrás de un firewall?

    ResponderEliminar
    Respuestas
    1. Mi maquina sap no está detras de un firawall y consultando a los basis se supone que el puerto 8000 está libre. De hecho el demo que hice desde eclipse se conecta perfectamente desde fuera de la red :S

      Eliminar
  3. muy buen post amigo, especialmente para los que estamos empezando con esto de SAPUI 5 recien , pero seria bueno que compartas el codigo de las aplicaciones que realizas , para un mejor entendimiento, codigo en mano es otra cosa , saludos y gracias

    ResponderEliminar
    Respuestas
    1. ¡Gracias!

      La mayor parte del código se genera de forma automática, con el propio editor, y sólo pongo las partes que toco, intentanto dejar siempre claro lo que modifico. En alguna ocasión se me ha pasado por la cabeza la idea de crearme una cuenta en GitHub, pero tampoco he tenido tiempo para pelearme con ello.

      Eliminar
  4. Hola jorge como estas, estoy con un problema, esta vez necesito ayuda para exportar los datos de una tabla sap.m.table aformato excel o CSV pero no toda la coleccion del Odata sino por ejemplo en la barra de busqueda pongo filtrar por usuarios que pertenescan al dep de ventas ,y eso exportar, osea el resultado despues de aplicar el filtro , alguna idea xfavor , he probado con la clase ExportCSV de sapui5 peor me exporta toda la collecion completa del Odata , alguna sugernecia o ayuda de como hacerlo , xfa te lo agradeceria bastante, que tengas buenas tardes ,estare atento a tu respuesta

    ResponderEliminar
    Respuestas
    1. Mira si este ejemplo te vale https://answers.sap.com/questions/383124/exportdata-method-of-sapuitabletable-exporting-exc.html

      Eliminar