Renderizar datos de API externos en bloques de backend de WordPress | trucos CSS

Este es un seguimiento de mi último artículo sobre «Representación de datos de API externos en bloques front-end de WordPress». En el último, aprendimos cómo tomar una API externa e integrarla con un bloque que representa datos obtenidos desde el frente. -fin de un sitio de WordPress.

La cuestión es que logramos esto de una manera que no nos permite ver los datos en el editor de bloques de WordPress. En otras palabras, podemos insertar el bloque en una página, pero no obtenemos una vista previa. Solo podemos ver el bloque cuando está publicado.

Revisemos el complemento de bloque de muestra que creamos en el artículo anterior. Solo que esta vez usaremos JavaScript y el ecosistema de WordPress React para recuperar y representar esos datos en el editor de bloques de back-end también.

Donde dejamos

Al comenzar esto, aquí hay una demostración donde llegamos en el último artículo que puede consultar. Habrás notado que usé un render_callback en el último artículo para poder usar los atributos en el archivo PHP y representar el contenido.

Bueno, esto puede ser útil en situaciones en las que es posible que necesite usar alguna función nativa de WordPress o PHP para crear bloques dinámicos. Pero si desea usar solo JavaScript y el ecosistema de WordPress React (específicamente JSX) para representar el HTML estático junto con los atributos almacenados en la base de datos, solo necesita concentrarse en Edit y Save bloquear las funciones del complemento.

  • los Edit representa el contenido en función de lo que desea ver en el editor de bloques. Aquí puede tener componentes React interactivos.
  • los Save La función representa el contenido en función de lo que desea ver en la interfaz. No puede tener los componentes o ganchos de React regulares aquí. Se utiliza para devolver el HTML estático que se guarda en su base de datos junto con los atributos.

los Save La función es donde pasamos el rato hoy. Podemos crear componentes interactivos en el front-end, pero para eso tenemos que incluirlos manualmente e implementarlos fuera Save funcione en un archivo como lo hicimos en el último artículo.

Entonces, voy a repasar lo mismo que hicimos en el artículo anterior, pero esta vez puedes ver la vista previa en el editor de bloques. antes de publicarlo en la parte delantera.

Soportes de bloque

He omitido intencionalmente cualquier explicación con respecto a edit accesorios de función en el último artículo porque eso quitaría el enfoque del punto principal, la representación.

Si vienes de React, probablemente sabrás de lo que estoy hablando, pero si eres nuevo en esto, te recomiendo verifique los componentes y accesorios en la documentación de React.

si nos registramos props object a la consola, devuelve una lista de funciones y variables de WordPress asociadas con nuestro bloque:

solo necesitamos attributes objetos setAttributes función que voy a destruir de props objeto en mi código. En el último artículo, modifiqué el código RapidAPI para poder almacenar los datos de la API a través de setAttributes()Los accesorios son de solo lectura, por lo que no podemos cambiarlos directamente.

Los accesorios de bloque son similares a las variables de estado y setState en React, pero React funciona en el lado del cliente y setAttributes() se utiliza para almacenar permanentemente los atributos en la base de datos de WordPress después de guardar la publicación. Así que lo que tenemos que hacer es guardarlos en el attributes.data y luego llamar a eso como el valor inicial para useState() variable.

los edit función

Copiaré y pegaré el código HTML que usamos. football-rankings.php en el último artículo y edítelo un poco para cambiar al fondo de JavaScript. ¿Recuerda cómo creamos dos archivos adicionales en el último artículo sobre estilos y scripts de front-end? Con la forma en que abordamos las cosas hoy en día, no hay necesidad de crear estos archivos. En su lugar, podemos moverlos todos a Edit función.

código completo

import { useState } from "@wordpress/element";
export default function Edit(props) {
  const { attributes, setAttributes } = props;
  const [apiData, setApiData] = useState(null);
    function fetchData() {
      const options = {
        method: "GET",
        headers: {
          "X-RapidAPI-Key": "Your Rapid API key",
          "X-RapidAPI-Host": "api-football-v1.p.rapidapi.com",
        },
      };
      fetch(
        "https://api-football-v1.p.rapidapi.com/v3/standings?season=2021&league=39",
          options
      )
      .then((response) => response.json())
      .then((response) => {
        let newData = { ...response }; // Deep clone the response data
        setAttributes({ data: newData }); // Store the data in WordPress attributes
        setApiData(newData); // Modify the state with the new data
      })
      .catch((err) => console.error(err));
    }
    return (
      <div {...useBlockProps()}>
        <button onClick={() => getData()}>Fetch data</button>
        {apiData && (
          <>
          <div id="league-standings">
            <div
              className="header"
              style={{
                backgroundImage: `url(${apiData.response[0].league.logo})`,
              }}
            >
              <div className="position">Rank</div>
              <div className="team-logo">Logo</div>
              <div className="team-name">Team name</div>
              <div className="stats">
                <div className="games-played">GP</div>
                <div className="games-won">GW</div>
                <div className="games-drawn">GD</div>
                <div className="games-lost">GL</div>
                <div className="goals-for">GF</div>
                <div className="goals-against">GA</div>
                <div className="points">Pts</div>
              </div>
              <div className="form-history">Form history</div>
            </div>
            <div className="league-table">
              {/* Usage of [0] might be weird but that is how the API structure is. */}
              {apiData.response[0].league.standings[0].map((el) => {
                
                {/* Destructure the required data from all */}
                const { played, win, draw, lose, goals } = el.all;
                  return (
                    <>
                    <div className="team">
                      <div class="position">{el.rank}</div>
                      <div className="team-logo">
                        <img src={el.team.logo} />
                      </div>
                      <div className="team-name">{el.team.name}</div>
                      <div className="stats">
                        <div className="games-played">{played}</div>
                        <div className="games-won">{win}</div>
                        <div className="games-drawn">{draw}</div>
                        <div className="games-lost">{lose}</div>
                        <div className="goals-for">{goals.for}</div>
                        <div className="goals-against">{goals.against}</div>
                        <div className="points">{el.points}</div>
                      </div>
                      <div className="form-history">
                        {el.form.split("").map((result) => {
                          return (
                            <div className={`result-${result}`}>{result}</div>
                          );
                        })}
                      </div>
                    </div>
                    </>
                  );
                }
              )}
            </div>
          </div>
        </>
      )}
    </div>
  );
}

He incluido el gancho React useState() de @wordpress/element en lugar de usarlo desde la biblioteca React. Esto se debe a que si tuviera que cargar de la manera habitual, descargará React por cada bloque que use. pero si uso @wordpress/element se carga desde una sola fuente, es decir, la capa de WordPress encima de React.

Tampoco he envuelto el código dentro de esta vez. useEffect() pero dentro de una función que solo se llama cuando se hace clic en un botón para que tengamos una vista previa en vivo de los datos recuperados. Usé una variable de estado llamada apiData para mostrar condicionalmente la tabla de clasificación. Entonces, después de hacer clic en el botón y obtener los datos, configuré apiData a los nuevos datos dentro del fetchData() y está disponible una representación en HTML de la tabla de clasificación de fútbol.

Notarás que una vez que se guarda la publicación y se actualiza la página, la tabla de clasificación desaparece. Esto se debe a que usamos un estado vacío (null) por apiDatael valor inicial de. Cuando se guarda la publicación, los atributos se guardan en el attributes.data objeto y lo llamamos como el valor inicial para useState() variables como esta:

const [apiData, setApiData] = useState(attributes.data);

los save función

voy a hacer lo mismo con save función, pero cámbiala un poco. Por ejemplo, no es necesario el botón «Obtener datos» en la parte frontal y apiData la variable de estado también es innecesaria porque ya la registramos edit Pero necesitamos uno al azar. apiData variable que verifica attributes.data para la representación condicional de JSX o, de lo contrario, generará errores indefinidos y la interfaz de usuario del editor de bloques permanecerá en blanco.

código completo

export default function save(props) {
  const { attributes, setAttributes } = props;
  let apiData = attributes.data;
  return (
    <>
      {/* Only render if apiData is available */}
      {apiData && (
        <div {...useBlockProps.save()}>
        <div id="league-standings">
          <div
            className="header"
            style={{
              backgroundImage: `url(${apiData.response[0].league.logo})`,
            }}
          >
            <div className="position">Rank</div>
            <div className="team-logo">Logo</div>
            <div className="team-name">Team name</div>
            <div className="stats">
              <div className="games-played">GP</div>
              <div className="games-won">GW</div>
              <div className="games-drawn">GD</div>
              <div className="games-lost">GL</div>
              <div className="goals-for">GF</div>
              <div className="goals-against">GA</div>
              <div className="points">Pts</div>
            </div>
            <div className="form-history">Form history</div>
          </div>
          <div className="league-table">
            {/* Usage of [0] might be weird but that is how the API structure is. */}
            {apiData.response[0].league.standings[0].map((el) => {
              const { played, win, draw, lose, goals } = el.all;
                return (
                  <>
                  <div className="team">
                    <div className="position">{el.rank}</div>
                      <div className="team-logo">
                        <img src={el.team.logo} />
                      </div>
                      <div className="team-name">{el.team.name}</div>
                      <div className="stats">
                        <div className="games-played">{played}</div>
                        <div className="games-won">{win}</div>
                        <div className="games-drawn">{draw}</div>
                        <div className="games-lost">{lose}</div>
                        <div className="goals-for">{goals.for}</div>
                        <div className="goals-against">{goals.against}</div>
                        <div className="points">{el.points}</div>
                      </div>
                      <div className="form-history">
                        {el.form.split("").map((result) => {
                          return (
                            <div className={`result-${result}`}>{result}</div>
                          );
                        })}
                      </div>
                    </div>
                  </>
                );
              })}
            </div>
          </div>
        </div>
      )}
    </>
  );
}

si modificas save una vez que un bloque ya está presente en el editor de bloques, mostrará un error como este:

El bloque de clasificación de fútbol en el editor de bloques de WordPress con un mensaje de error que indica que el bloque contiene un error inesperado.

Esto se debe a que el marcado en el contenido guardado es diferente del marcado en nuestro nuevo save Dado que estamos en modo de desarrollo, es más fácil eliminar el bloque de la página actual y volver a insertarlo como un bloque nuevo; de esa manera, se usa el código actualizado y las cosas vuelven a estar sincronizadas.

Esta situación de quitarlo y volverlo a agregar se puede evitar si usamos render_callback método porque la salida es dinámica y controlada por PHP en lugar de la función de guardar. Así que cada método tiene sus ventajas y desventajas.

Tom Nowell proporciona una explicación detallada de lo que no se debe hacer en un save funcion c este desbordamiento de pila responder.

Estilo de bloque en el editor y front-end

En cuanto al estilo, será casi el mismo que cubrimos en el último artículo, pero con algunos cambios menores que expliqué en los comentarios. Solo proporciono los estilos completos aquí, ya que es solo una prueba de concepto, no algo que desee copiar y pegar (a menos que realmente necesite un bloque de visualización de clasificaciones de fútbol con un estilo como este). utilizando SCSS, que se compila en CSS en tiempo de compilación.

Editor de estilos

/* Target all the blocks with the data-title="Football Rankings" */
.block-editor-block-list__layout 
.block-editor-block-list__block.wp-block[data-title="Football Rankings"] {
  /* By default, the blocks are constrained within 650px max-width plus other design specific code */
  max-width: unset;
  background: linear-gradient(to right, #8f94fb, #4e54c8);
  display: grid;
  place-items: center;
  padding: 60px 0;

  /* Button CSS - From: https://getcssscan.com/css-buttons-examples - Some properties really not needed 🙂 */
  button.fetch-data {
    align-items: center;
    background-color: #ffffff;
    border: 1px solid rgb(0 0 0 / 0.1);
    border-radius: 0.25rem;
    box-shadow: rgb(0 0 0 / 0.02) 0 1px 3px 0;
    box-sizing: border-box;
    color: rgb(0 0 0 / 0.85);
    cursor: pointer;
    display: inline-flex;
    font-family: system-ui, -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif;
    font-size: 16px;
    font-weight: 600;
    justify-content: center;
    line-height: 1.25;
    margin: 0;
    min-height: 3rem;
    padding: calc(0.875rem - 1px) calc(1.5rem - 1px);
    position: relative;
    text-decoration: none;
    transition: all 250ms;
    user-select: none;
    -webkit-user-select: none;
    touch-action: manipulation;
    vertical-align: baseline;
    width: auto;
    &:hover,
    &:focus {
      border-color: rgb(0, 0, 0, 0.15);
      box-shadow: rgb(0 0 0 / 0.1) 0 4px 12px;
      color: rgb(0, 0, 0, 0.65);
    }
    &:hover {
      transform: translateY(-1px);
    }
    &:active {
      background-color: #f0f0f1;
      border-color: rgb(0 0 0 / 0.15);
      box-shadow: rgb(0 0 0 / 0.06) 0 2px 4px;
      color: rgb(0 0 0 / 0.65);
      transform: translateY(0);
    }
  }
}

Estilos de front-end

/* Front-end block styles */
.wp-block-post-content .wp-block-football-rankings-league-table {
  background: linear-gradient(to right, #8f94fb, #4e54c8);
  max-width: unset;
  display: grid;
  place-items: center;
}

#league-standings {
  width: 900px;
  margin: 60px 0;
  max-width: unset;
  font-size: 16px;
  .header {
    display: grid;
    gap: 1em;
    padding: 10px;
    grid-template-columns: 1fr 1fr 3fr 4fr 3fr;
    align-items: center;
    color: white;
    font-size: 16px;
    font-weight: 600;
    background-color: transparent;
    background-repeat: no-repeat;
    background-size: contain;
    background-position: right;

    .stats {
      display: flex;
      gap: 15px;
      &amp; &gt; div {
        width: 30px;
      }
    }
  }
}
.league-table {
  background: white;
  box-shadow:
    rgba(50, 50, 93, 0.25) 0px 2px 5px -1px,
    rgba(0, 0, 0, 0.3) 0px 1px 3px -1px;
  padding: 1em;
  .position {
    width: 20px;
  }
  .team {
    display: grid;
    gap: 1em;
    padding: 10px 0;
    grid-template-columns: 1fr 1fr 3fr 4fr 3fr;
    align-items: center;
  }
  .team:not(:last-child) {
    border-bottom: 1px solid lightgray;
  }
  .team-logo img {
    width: 30px;
    top: 3px;
    position: relative;
  }
  .stats {
    display: flex;
    gap: 15px;
    &amp; &gt; div {
      width: 30px;
      text-align: center;
    }
  }
  .last-5-games {
    display: flex;
    gap: 5px;
    &amp; &gt; div {
      width: 25px;
      height: 25px;
      text-align: center;
      border-radius: 3px;
      font-size: 15px;
    &amp; .result-W {
      background: #347d39;
      color: white;
    }
    &amp; .result-D {
      background: gray;
      color: white;
    }
    &amp; .result-L {
      background: lightcoral;
      color: white;
    }
  }
}

Agregamos esto a src/style.scss que se encarga del estilo tanto en el editor como en la interfaz. No podré compartir la URL de demostración, ya que requeriría acceso a un editor, pero tengo un video grabado para que vea la demostración:


Bastante bien, ¿verdad? Ahora tenemos un bloque en pleno funcionamiento que no solo se procesa en la parte frontal, sino que también obtiene datos de API y se procesa allí mismo en el editor de bloques, ¡con un botón de actualización para arrancar!

Pero si queremos tomar completo ventaja del editor de bloques de WordPress, deberíamos considerar mapear algunos de los elementos de la interfaz de usuario del bloque controles de bloque para cosas como establecer el color, la tipografía y el espaciado. Este es un buen próximo paso en el viaje de aprendizaje del desarrollo de bloques.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

rtp live