miércoles, 18 de enero de 2017

Crear una app SAPUI5 con Web IDE y servicio oData (y II)

Gracias a las plantillas del SAP Web IDE, nos habíamos creado rápidamente una aplicación que tiraba de un servicio oData, otra gran creación nuestra. ¡Estamos que nos salimos!

Pero para poder adaptar un poco la aplicación y hacer algo más bonito, necesitamos algo más que aprovecharnos de las bondades del Web IDE. Nosotros tenemos que poner un poco de nuestra parte.

En este tutorial, vamos a ver elementos básicos que se generan en la aplicación para adaptarla un poco a nuestras necesidades. Nada complejo, sólo para entender conceptos básicos.


Con la plantilla que habíamos utilizado, habíamos conseguido un montón de código sin necesidad de picar ni una única líneacódigo. ¿Y dónde está todo ese código tirado por la cara? Repartido entre multitud de ficheros.

Dónde está el servicio oData

El principal componente que vamos a tener es el Component.js. Aquí tenemos lo primero que más nos interesa, ¡el servicio oData!

En el Component.js, existe un contenido "metadata" donde se guarda la configuración de la aplicación, presentado en formato JSON.

Dentro del metadata, existe un atributo "config" y, dentro de éste, el atributo "serviceConfig". Es aquí donde se indica la ubicación del servicio oData. Si quisiésemos cambiar dicha ubicación, lo cambiaríamos aquí.

¿Pero cómo sabe el Web IDE dónde esta ese dichoso servicio oData? Porque lo que se ha guardado es una ruta relativa, /sap/opu/odata/sap/ZMIS_TEXTOS_SRV... ¿pero de qué servidor?

Ahí entran en juego dos cosas: el destination y el fichero neo-app.json.

Este fichero se usa sólo en SAP HCP (en el Web IDE o en el portal). Cuando lo transportemos a nuestro Front-End, ya no tendremos que preocuparnos por él porque la url relativa se referirá al propio servidor SAP. Sin embargo, mientras lo ejecutemos en SAP HCP el neo-app se usará para poder redireccionar las llamadas que se hagan al servicio hacia el front-end, sin que el navegador web del usuario se entere de que está cambiando de dominio. Hará las veces de un proxy inverso (como si fuese un SAP Web Dispatcher).

El fichero neo-app.json contiene esta información:


Esto SAP HCP lo interpreta así: "Si se hace una llamada a una ruta que comienza por /sap/opu/odata (lo que pone en el atributo path), entonces voy a usar un destination (especificado en type) que se llama UXSAP (especificado en name). Además, a la url que me proporcione el destination añadiré /sap/opu/odata (que es lo que pone en entryPath)".

Así que cuando se llama al servicio oData que se especifica en el Component.js, el neo-app y el destination en combinación se encargan de traducir la dirección. Trabajo en equipo.

¿Y por qué es necesario indicar el entryPath si va a ser igual que el path? Podríamos pensar (erróneamente) que al llamar al destination, la aplicación va a coger la url del destination y añadir lo que pone en el path, pero no lo hace así. Simplemente entiende algo así: "si en algún sitio pone /sap/opu/odata, uso el destination X". Así que con el entryPath nosotros le decimos "para que no te olvides de agregar /sap/opu/odata, te lo pongo en el entryPath".

Parece una chorrada redundante, pero esto nos puede dar más versatilidad, sobre todo para enmascarar urls si no vamos a subir la aplicación a un front-end sino a dejarla en el portal del SAP HCP. Ahí podríamos decirle "si tienes una cadena que pone supercalifragi, en lugar de eso me vas a usar el destination X y añadir /sap/opu/odata". Nadie que visite la página verá que estamos llamando a una url con el texto /sap/opu/odata, sino con el texto supercalifragi. 

Claro que, a nosotros, que nuestro objetivo es subir la aplicación a nuestro SAP front-end, nos interesa mantenerlo así. Y cuando subamos la aplicación, no tendremos que cambiar nada.

Pantalla inicial: Sección Master

La aplicación de tipo full screen tiene dos bloques importantes, una Master (la página inicial, que te muestra el listado completo en una tabla) y una Detail (la página que se muestra cuando seleccionas algo en la Master).

La Master es la primera página que el usuario va a ver cuando ejecute la aplicación. Internamente la aplicación habrá pasado por otros elementos (onInit del Component.js, el createContent, el Main.view.xml), pero para entender la funcionalidad nos vale con saber que lo primero que verá el usuario será el Master.

Pero el master no es un único fichero, sino dos, la vista (Master.view.xml, donde está el fichero xml, que sería equivalente a un html) y el controlador (Master.controller.js, donde está el código javascript). Cumpliendo toda aquella historia del MVC, o Modelo-Vista-Controlador.

Es aquí (en la vista Master.view.xml) donde tenemos que meterle mano para modificar lo que se verá en la tabla de inicio. El código es algo tal que así:



Se trata de una tabla donde vamos a mostrar una colección del servicio oData. ¿Qué colección? La que se ve en el atributo items. Cuando se cargue este XML, se invocará automáticamente a la colección para luego pintar los valores que recibamos mediante binding (en unas pocas líneas veremos que es eso).

Esa tabla tiene un elemento llamado "columns", y dentro de él varios elementos llamados Column. Cada elemento Column es una de las columnas que tendremos en la tabla. Estos elementos nos sirven para definir la estructura de la tabla y asignarle una cabecera a cada columna, aunque aún no mostrarán datos. En nuestro caso, como sólo mostrábamos un campo, podríamos borrar todos los elementos Column menos el primero.

El contenido de la tabla (las filas) se carga más adelante. Cuando acaba el elemento columns (con la etiqueta </columns>) tenemos el elemento "ColumnListItem".


Dentro de ese elemento, tenemos otro elemento, cells, que es donde estarán las celdas que componen la fila. 

Habrá una celda por cada columna, y cada una tendrá asociado un texto (guardado en el atributo text, recuadrado en la imagen), que es lo que se mostrará al ejecutar la aplicación. En nuestro caso, la primera celda es un elemento de tipo Text 

Si en el atributo text hubiésemos puesto algo como text="hola", al ejecutar la aplicación se verá "hola" (sin comillas). Pero cuando le ponemos unas llaves {}, le estamos indicando que nos muestre una información dinámica (como si fuese una variable en cualquier lenguaje de programación), en este caso la propiedad Id de cada entrada de TextosGeneralesSet (que es la colección que indicamos en la tabla, en el atributo items).

Eso quiere decir que los datos se mostrarán dinámicamente, enlazando esa propiedad con lo que nos devolverá el servicio oData (lo que se llama binding).

Como las tres restantes entradas no las usamos, las podríamos borrar (habiendo borrado inicialmente las columnas).

 Así que si borramos todas las columnas menos la primera, tendríamos algo así:

Fíjate que he cambiado el atributo text del texto para que se muestre "El id es XXXXXX".
 No he usado internacionalización porque me ha pillado un poco vaguete.
También hay que saber cómo navegamos al detalle. Eso lo conseguimos pulsando una entrada de la tabla. Esa entrada lanza el evento press, que está dentro del elemento "ColumnListItem". Ese press nos indica qué función se ejecutará al ser pulsada una fila. ¿Y dónde está esa función? En el controlador, Main.controller.js. Siempre que tengamos una función, iremos a buscarla a un fichero javascript (js).

Lo que hace la función es leer la fila pulsada y hacer una navegación a la ventana de detalle. Pero eso es tema de enrutamiento y ya son palabras mayores para este tutorial :P.

En el controlador del master se hace la navegación a la página de detalle

Información adicional: Sección Detail

La sección detalle se cargará cuando pulsemos una fila de la tabla, tras ejecutarse la función onLineItemPressed, que realiza una navegación. Las navegaciones se configuran en el Component.js, en el bloque routing.

En el detail teníamos unos elementos con los datos de la selección hecha en la tabla del Master. Además, tenemos una nueva tabla, que se carga con una navegación y que nos aparecerá vacía porque no habíamos incluida nada.


 Podemos ver en el fichero Detail.view.xml un elemento que pone ObjectHeader. Se trata del bloque que se muestra en la parte superior de la página. Ahí tenemos los campos que habíamos escogido para mostrar al usar la plantilla del Web IDE, todos ellos entre llaves, {Id} y {Texto}.

Así que para cambiar los datos que se van a ver (o cómo se van a ver), cambiaríamos este objeto. Podemos añadir más propiedades del servicio oData. Y si no nos gusta el ObjectHeader, podríamos cambiarlo por una tabla, un formulario, o lo que nos de la gana.

¿Pero dónde se indica la colección que se usa? En el master lo teníamos a la vista y en la vista (en el Master.view.xml).

Para el detalle, nos toca bucear en el controlador. Lo acabaremos encontrando en el fichero Detail.controller.js.


En la función _onRoutePatternMatched se recupera la selección hecha en el master (la línea que pone this._sItemPath = ...) y lo enlaza con la parte recuadrada (bindElement). Ahí es donde se van a enlazar nuestros datos. Ya sólo necesitamos indicar las propiedades entre llaves, {nombre_de_propiedad}, dentro del XML. Esos campos del XML se cargarán vacíos durante breves segundos, pero cuando se ejecute el código del controlador, se refrescarán dinámicamente.

Ahora vamos a eliminar las cosas que no necesitamos.

Al final del Detail.view.xml, tenemos un elemento de tipo Table... que es la navegación que no hemos usado. Es un elemento que podemos borrar si no vamos a utilizar, o que modificaremos si al final añadimos una asociación.


Podemos borrar el elemento de la vista sin piedad, pero también tenemos que revisar el controlador, ya que en él hay referencias a dicha tabla y, si no las quitamos, luego se pondrá a dar errores de javascript como un loco.

En el controlador nos cepillaríamos los bloques de código que hagan referencia a la navegación, tales como estas líneas que están distribuidas alegremente por el controlador:

this._oNavigationTable = this.byId("navigationTable");
this._oItemTemplate = this.byId("navigationListItem").clone();
this._sNavigationPath = this._sItemPath + "/" + "";
// Bind Review Table using oData Reviews Entity
this._bindNavigationTable(this._sNavigationPath);
  _bindNavigationTable : function (toda la función completa);
  y la definición de las variables que ya no usamos.

Resultado

Si hemos eliminado lo que no queríamos, al final tendremos una aplicación sencillota como ésta:

Esto es lo que veo al iniciar la aplicación

Y esto es lo que veo al pulsar la primera fila

¿Y qué es eso del Manifest.json?

Si te has creado la aplicación con la versión SAP Innovation, en el Component.js no te aparecerá el servicio oData. En su lugar, habrá un fichero manifest.json, que es invocado desde el component, dónde estará guardada esta información.

El formato es similar al del component, pero algunas cosas pueden cambiar. Por ejemplo, el servicio oData se indica en "dataSources" y, dentro de ésta, en "mainService".


Además, la estructura de carpetas del proyecto será diferente, existiendo una carpeta para las vistas (view) y otra para los controladores (controller). Se puede ver una pequeña comparativa de los cambios en este post.

Por lo demás, el funcionamiento debería ser más o menos parecido. Aunque para SAP Innovation, al menos cuando he creado este post, no había aplicaciones de tipo Full Screen Application sino de tipo Worklist.

7 comentarios:

  1. Holal al principio me confundi hasta que llegue a lo ultimo ya que he usado la version sap innovation, seria bueno que introdujeras algun post sobre sap web ide enterprise edition que es como el web ide pero local y aca las odatas son un poco distintos a mi parecer ya que se debe crear las configuracion en un texto y guardarlos en la carpeta destination https://tools.hana.ondemand.com/#sapui5 y tengo una gran duda, al momento de subir la aplicacion como se hace? eso lo hace un basis ?y por lo menos si ves el fiori launchpad hay muchos tiles (mosaicos) cuando uno sube una app al servidor se crea un nuevo tile para mi aplicacion??? muchisimas gracias tu blog me ha sido de muchisisisma utilidad

    ResponderEliminar
  2. Para la Enterprise Edition, tengo pendiente trastear con ella.

    Para desplegar en SAP, hay un tutorial, https://uxsap.blogspot.com.es/2017/08/desplegar-app-sapui5-onpremise.html , aunque es para la versión Web IDE y no para la Enterprise Edition :( .

    Al desplegar, se sube la aplicación al back-end pero no te crea tiles, sólo te sube la "BSP", que podrás ver en la SE80. Una vez subida, ya tendrías que desplegar el tile de la forma normal, con el Fiori Launchpad Designer y el LPD_CUST. Con las últimas versiones ya no hace falta el LPD_CUST si quieres, ojea este post https://uxsap.blogspot.com.es/2017/10/target-mapping-sin-lpdcust.html

    ResponderEliminar
    Respuestas
    1. Gracias por tu respuesta, me acabo de dar cuenta que se llama es SAP Web IDE Personal Edition. xD revisare esos enlaces que dejaste :D gracias!!!!

      Eliminar
  3. Este comentario ha sido eliminado por el autor.

    ResponderEliminar
  4. Hola, querría saber si tienes el código subido a algún repositorio público para poderlo ver. Un saludo!

    ResponderEliminar
    Respuestas
    1. No, lo siento, este ejemplo en particular no está en ningún repositorio subido.

      Eliminar
    2. De acuerdo! Muchas gracias por tu respuesta.
      Un saludo

      Eliminar

Nota: solo los miembros de este blog pueden publicar comentarios.