Apex · APEX complementos · APEX Css · Interactive Grid · javascript · Oracle

Oracle APEX Multiple Carrusel en Reporte, Carousel in Report

En cierta oportunidad vamos a necesitar agregar diferentes imágenes en un reporte, en este método que mostrare, agregaremos un carrousel de imágenes por linea de un reporte.

El ejemplo lo ven en: https://apex.oracle.com/pls/apex/f?p=32431:1070:103822500571635::::: demo/demo

En este tutorial estamos usando las tablas de ejemplo de Oracle APEX, Order, products.

Quiero hacer notar que el siente podrían ser no las mejores practicas en desarrollo, pero para que se entienda la idea de manera general lo agregare todo en una pagina. Próximamente lo trabajaremos como ponente, asi el código quedara mas ordenado.

Entonces en el Inline CSS de la pagina agregaremos algunas clases que nos ayudaran a dimensionar los elementos.

/* Slideshow container */
.slideshow-container {
  max-width: 1000px;
  position: relative;
  margin: auto;
}

/* Next & previous buttons */
.prev, .next {
  cursor: pointer;
  position: absolute;
  top: 50%;
  width: auto;
  padding: 0.5rem;
  margin-top: -22px;
  /* color: white; */
  font-weight: bold;
  font-size: 18px;
  transition: 0.6s ease;
  border-radius: 0 3px 3px 0;
  user-select: none;
  background-color: #f1f1f1;
  color: black;
}

/* Position the "next button" to the right */
.next {
  right: 0;
  border-radius: 3px 0 0 3px;
}

/* On hover, add a grey background color */
/* .prev:hover, .next:hover {
  background-color: #f1f1f1;
  color: black;
} */

.slide-img {
  width:100%;
}

Ahora el código JavaScript a nivel de página también.

// ===========================================================================
// Init Carousel

let slideIndex = {}; // Objeto para almacenar índices de slides por ID único

function initializeSlides() {
  // Encuentra todos los elementos con clase 'slideshow-container'
  const allContainers = document.querySelectorAll('.slideshow-container');

  allContainers.forEach((container) => {
    const id = container.getAttribute('data-id'); // Obtiene el 'data-id' del contenedor

    // Si el ID no está ya en slideIndex, lo agregamos
    if (!slideIndex[id]) {
      slideIndex[id] = 1; // Inicializa el índice del nuevo carrusel
      showSlides(1, id);  // Muestra el primer slide del nuevo carrusel
    }

    // Si el carrusel ya tiene más de un slide, aseguramos que el índice esté actualizado
    const slides = container.querySelectorAll('.slide');
    if (slides.length > 1 && slideIndex[id] > slides.length) {
      slideIndex[id] = slides.length;  // Aseguramos que el índice no exceda el número de slides
    }
  });
}

function plusSlides(n, id) {
  if (!slideIndex[id]) return; // Asegúrate de que el ID exista en slideIndex
  showSlides((slideIndex[id] += n), id);
}

function showSlides(n, id) {
  let i;
  const slides = document.querySelectorAll(`.slideshow-container[data-id="${id}"] .slide`);
  if (!slides.length) return; // Si no hay slides, no hace nada

  // Lógica para circular a través de los slides
  if (n > slides.length) { slideIndex[id] = 1; }
  if (n < 1) { slideIndex[id] = slides.length; }

  for (i = 0; i < slides.length; i++) {
    slides[i].style.display = "none";
  }
  slides[slideIndex[id] - 1].style.display = "block";
}



// Listener para botones prev/next
document.addEventListener('click', function (event) {
  if (event.target.classList.contains('prev') || event.target.classList.contains('next')) {
    const isNext = event.target.classList.contains('next');
    const id = event.target.getAttribute('data-id'); // Obtén el ID único del botón
    const direction = isNext ? 1 : -1;

    plusSlides(direction, id); // Llama a la función con la dirección y el ID
  }
});

Crearemos un Interactive Report, y dentro haremos el fetch de las imágenes.

Para esta técnica necesitaremos las imágenes en URL. si las tenemos solo lo concatenamos para mostrar, si no aplicamos la técnica que use, el cual usando get_blob_file_src y el ID del producto nos generara un link para mostrar la imagen.

with w_items_order as (
 select listagg( product_id, '|' ) within group(order by product_id ) as products
      , listagg( apex_util.get_blob_file_src('P1070_PRODUCT_IMAGE', product_id), '|' ON OVERFLOW TRUNCATE )
          within group(order by product_id )  products_img_url
      , order_id
   from demo_order_items
  group by order_id
)
select o.order_id,
       o.customer_id,
       o.order_total,
       o.order_timestamp,
       o.user_name,
       o.tags
     , io.products
     , io.products_img_url products_img_url
     , null carousel
  from demo_orders o
  left join w_items_order io on io.order_id = o.order_id

P1070_PRODUCT_IMAGE es un elemento de la “form” de APEX, para la tabla DEMO_PRODUCT_INFO

Regresando a nuestra salida de columna “carousel” del primero SQL.. este tendrá el siguiente HTML

<div class="a-CardView-media a-CardView-media--body ">
  <div class="slideshow-container" data-id="#ORDER_ID#">
    {loop "|" PRODUCTS_IMG_URL/}
      <div class="slide">
        <img src="&APEX$ITEM." class="slide-img" >
      </div>             
    {endloop/}    
    <a class="prev" data-id="#ORDER_ID#">❮</a>
    <a class="next" data-id="#ORDER_ID#">❯</a>          
  </div>
</div>

Este como ven hace referencia a la salida de las url de las imágenes en PRODUCTS_IMG_URL. y hace un fetch de los mismos para mostrar.

Finalmente agregaremos una action dinámica on refresh o cuando lo necesitamos para que las imágenes cargadas adopten la apariencia del carrousel.

initializeSlides();

Referencia:

Este se dio gracias a la implementación de https://www.w3schools.com/howto/howto_js_slideshow_gallery.asp y https://www.w3schools.com/howto/tryit.asp?filename=tryhow_js_slideshow_multiple

Leave a comment