Tuesday, March 14, 2023
HomeCSSRendering Exterior API Knowledge in WordPress Blocks on the Again Finish |...

Rendering Exterior API Knowledge in WordPress Blocks on the Again Finish | CSS-Tips


It is a continuation of my final article about “Rendering Exterior API Knowledge in WordPress Blocks on the Entrance Finish”. In that final one, we realized take an exterior API and combine it with a block that renders the fetched information on the entrance finish of a WordPress website.

The factor is, we achieved this in a manner that forestalls us from seeing the info within the WordPress Block Editor. In different phrases, we will insert the block on a web page however we get no preview of it. We solely get to see the block when it’s revealed.

Let’s revisit the instance block plugin we made within the final article. Solely this time, we’re going to utilize the JavaScript and React ecosystem of WordPress to fetch and render that information within the back-end Block Editor as properly.

Working With Exterior APIs in WordPress Blocks

The place we left off

As we kick this off, right here’s a demo the place we landed within the final article that you could reference. You could have observed that I used a render_callback methodology within the final article in order that I could make use of the attributes within the PHP file and render the content material.

Nicely, which may be helpful in conditions the place you might need to make use of some native WordPress or PHP operate to create dynamic blocks. However if you wish to make use of simply the JavaScript and React (JSX, particularly) ecosystem of WordPress to render the static HTML together with the attributes saved within the database, you solely must concentrate on the Edit and Save capabilities of the block plugin.

  • The Edit operate renders the content material based mostly on what you wish to see within the Block Editor. You possibly can have interactive React elements right here.
  • The Save operate renders the content material based mostly on what you wish to see on the entrance finish. You can’t have the the common React elements or the hooks right here. It’s used to return the static HTML that’s saved into your database together with the attributes.

The Save operate is the place we’re hanging out at the moment. We will create interactive elements on the front-end, however for that we have to manually embody and entry them outdoors the Save operate in a file like we did within the final article.

So, I’m going to cowl the identical floor we did within the final article, however this time you may see the preview within the Block Editor earlier than you publish it to the entrance finish.

The block props

I deliberately not noted any explanations in regards to the edit operate’s props within the final article as a result of that will have taken the main focus off of the primary level, the rendering.

In case you are coming from a React background, you’ll possible perceive what’s that I’m speaking about, however in case you are new to this, I might advocate trying out elements and props within the React documentation.

If we log the props object to the console, it returns a listing of WordPress capabilities and variables associated to our block:

Console log of the block properties.

We solely want the attributes object and the setAttributes operate which I’m going to destructure from the props object in my code. Within the final article, I had modified RapidAPI’s code in order that I can retailer the API information by means of setAttributes(). Props are solely readable, so we’re unable to change them instantly.

Block props are much like state variables and setState in React, however React works on the shopper aspect and setAttributes() is used to retailer the attributes completely within the WordPress database after saving the publish. So, what we have to do is save them to attributes.information after which name that because the preliminary worth for the useState() variable.

The edit operate

I’m going to copy-paste the HTML code that we utilized in football-rankings.php within the final article and edit it just a little to shift to the JavaScript background. Keep in mind how we created two extra recordsdata within the final article for the entrance finish styling and scripts? With the way in which we’re approaching issues at the moment, there’s no must create these recordsdata. As an alternative, we will transfer all of it to the Edit operate.

Full code
import { useState } from "@wordpress/component";
export default operate Edit(props) {
  const { attributes, setAttributes } = props;
  const [apiData, setApiData] = useState(null);
    operate fetchData() {
      const choices = {
        methodology: "GET",
        headers: {
          "X-RapidAPI-Key": "Your Fast 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",
          choices
      )
      .then((response) => response.json())
      .then((response) => {
        let newData = { ...response }; // Deep clone the response information
        setAttributes({ information: newData }); // Retailer the info in WordPress attributes
        setApiData(newData); // Modify the state with the brand new information
      })
      .catch((err) => console.error(err));
    }
    return (
      <div {...useBlockProps()}>
        <button onClick={() => getData()}>Fetch information</button>
        {apiData && (
          <>
          <div id="league-standings">
            <div
              className="header"
              fashion={{
                backgroundImage: `url(${apiData.response[0].league.brand})`,
              }}
            >
              <div className="place">Rank</div>
              <div className="team-logo">Brand</div>
              <div className="team-name">Group title</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="factors">Pts</div>
              </div>
              <div className="form-history">Type historical past</div>
            </div>
            <div className="league-table">
              {/* Utilization of [0] is likely to be bizarre however that's how the API construction is. */}
              {apiData.response[0].league.standings[0].map((el) => {
                
                {/* Destructure the required information from all */}
                const { performed, win, draw, lose, targets } = el.all;
                  return (
                    <>
                    <div className="staff">
                      <div class="place">{el.rank}</div>
                      <div className="team-logo">
                        <img src={el.staff.brand} />
                      </div>
                      <div className="team-name">{el.staff.title}</div>
                      <div className="stats">
                        <div className="games-played">{performed}</div>
                        <div className="games-won">{win}</div>
                        <div className="games-drawn">{draw}</div>
                        <div className="games-lost">{lose}</div>
                        <div className="goals-for">{targets.for}</div>
                        <div className="goals-against">{targets.in opposition to}</div>
                        <div className="factors">{el.factors}</div>
                      </div>
                      <div className="form-history">
                        {el.type.break up("").map((consequence) => {
                          return (
                            <div className={`result-${consequence}`}>{consequence}</div>
                          );
                        })}
                      </div>
                    </div>
                    </>
                  );
                }
              )}
            </div>
          </div>
        </>
      )}
    </div>
  );
}

I’ve included the React hook useState() from @wordpress/component relatively than utilizing it from the React library. That’s as a result of if I have been to load the common manner, it might obtain React for each block that I’m utilizing. But when I’m utilizing @wordpress/component it hundreds from a single supply, i.e., the WordPress layer on prime of React.

This time, I’ve additionally not wrapped the code inside useEffect() however inside a operate that known as solely when clicking on a button in order that now we have a reside preview of the fetched information. I’ve used a state variable referred to as apiData to render the league desk conditionally. So, as soon as the button is clicked and the info is fetched, I’m setting apiData to the brand new information contained in the fetchData() and there’s a rerender with the HTML of the soccer rankings desk out there.

You’ll discover that when the publish is saved and the web page is refreshed, the league desk is gone. That’s as a result of we’re utilizing an empty state (null) for apiData‘s preliminary worth. When the publish saves, the attributes are saved to the attributes.information object and we name it because the preliminary worth for the useState() variable like this:

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

The save operate

We’re going to do virtually the identical precise factor with the save operate, however modify it just a little bit. For instance, there’s no want for the “Fetch information” button on the entrance finish, and the apiData state variable can be pointless as a result of we’re already checking it within the edit operate. However we do want a random apiData variable that checks for attributes.information to conditionally render the JSX or else it’ll throw undefined errors and the Block Editor UI will go clean.

Full code
export default operate save(props) {
  const { attributes, setAttributes } = props;
  let apiData = attributes.information;
  return (
    <>
      {/* Solely render if apiData is obtainable */}
      {apiData && (
        <div {...useBlockProps.save()}>
        <div id="league-standings">
          <div
            className="header"
            fashion={{
              backgroundImage: `url(${apiData.response[0].league.brand})`,
            }}
          >
            <div className="place">Rank</div>
            <div className="team-logo">Brand</div>
            <div className="team-name">Group title</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="factors">Pts</div>
            </div>
            <div className="form-history">Type historical past</div>
          </div>
          <div className="league-table">
            {/* Utilization of [0] is likely to be bizarre however that's how the API construction is. */}
            {apiData.response[0].league.standings[0].map((el) => {
              const { performed, win, draw, lose, targets } = el.all;
                return (
                  <>
                  <div className="staff">
                    <div className="place">{el.rank}</div>
                      <div className="team-logo">
                        <img src={el.staff.brand} />
                      </div>
                      <div className="team-name">{el.staff.title}</div>
                      <div className="stats">
                        <div className="games-played">{performed}</div>
                        <div className="games-won">{win}</div>
                        <div className="games-drawn">{draw}</div>
                        <div className="games-lost">{lose}</div>
                        <div className="goals-for">{targets.for}</div>
                        <div className="goals-against">{targets.in opposition to}</div>
                        <div className="factors">{el.factors}</div>
                      </div>
                      <div className="form-history">
                        {el.type.break up("").map((consequence) => {
                          return (
                            <div className={`result-${consequence}`}>{consequence}</div>
                          );
                        })}
                      </div>
                    </div>
                  </>
                );
              })}
            </div>
          </div>
        </div>
      )}
    </>
  );
}

In case you are modifying the save operate after a block is already current within the Block Editor, it might present an error like this:

The football rankings block in the WordPress block Editor with an error message that the block contains an unexpected error.

That’s as a result of the markup within the saved content material is completely different from the markup in our new save operate. Since we’re in growth mode, it’s simpler to take away the bock from the present web page and re-insert it as a brand new block — that manner, the up to date code is used as an alternative and issues are again in sync.

This case of eradicating it and including it once more might be prevented if we had used the render_callback methodology for the reason that output is dynamic and managed by PHP as an alternative of the save operate. So every methodology has it’s personal benefits and downsides.

Tom Nowell supplies a radical clarification on what to not do in a save operate in this Stack Overflow reply.

Styling the block within the editor and the entrance finish

Relating to the styling, it’s going to be virtually the identical factor we checked out within the final article, however with some minor adjustments which I’ve defined within the feedback. I’m merely offering the complete types right here since that is solely a proof of idea relatively than one thing you wish to copy-paste (until you actually do want a block for displaying soccer rankings styled similar to this). And notice that I’m nonetheless utilizing SCSS that compiles to CSS on construct.

Editor types
/* Goal all of the blocks with the data-title="Soccer Rankings" */
.block-editor-block-list__layout 
.block-editor-block-list__block.wp-block[data-title="Football Rankings"] {
  /* By default, the blocks are constrained inside 650px max-width plus different design particular code */
  max-width: unset;
  background: linear-gradient(to proper, #8f94fb, #4e54c8);
  show: grid;
  place-items: heart;
  padding: 60px 0;

  /* Button CSS - From: https://getcssscan.com/css-buttons-examples - Some properties actually not wanted :) */
  button.fetch-data {
    align-items: heart;
    background-color: #ffffff;
    border: 1px strong 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;
    coloration: rgb(0 0 0 / 0.85);
    cursor: pointer;
    show: inline-flex;
    font-family: system-ui, -apple-system, system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif;
    font-size: 16px;
    font-weight: 600;
    justify-content: heart;
    line-height: 1.25;
    margin: 0;
    min-height: 3rem;
    padding: calc(0.875rem - 1px) calc(1.5rem - 1px);
    place: 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;
      coloration: rgb(0, 0, 0, 0.65);
    }
    &:hover {
      rework: translateY(-1px);
    }
    &:energetic {
      background-color: #f0f0f1;
      border-color: rgb(0 0 0 / 0.15);
      box-shadow: rgb(0 0 0 / 0.06) 0 2px 4px;
      coloration: rgb(0 0 0 / 0.65);
      rework: translateY(0);
    }
  }
}
Entrance-end types
/* Entrance-end block types */
.wp-block-post-content .wp-block-football-rankings-league-table {
  background: linear-gradient(to proper, #8f94fb, #4e54c8);
  max-width: unset;
  show: grid;
  place-items: heart;
}

#league-standings {
  width: 900px;
  margin: 60px 0;
  max-width: unset;
  font-size: 16px;
  .header {
    show: grid;
    hole: 1em;
    padding: 10px;
    grid-template-columns: 1fr 1fr 3fr 4fr 3fr;
    align-items: heart;
    coloration: white;
    font-size: 16px;
    font-weight: 600;
    background-color: clear;
    background-repeat: no-repeat;
    background-size: comprise;
    background-position: proper;

    .stats {
      show: flex;
      hole: 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;
  .place {
    width: 20px;
  }
  .staff {
    show: grid;
    hole: 1em;
    padding: 10px 0;
    grid-template-columns: 1fr 1fr 3fr 4fr 3fr;
    align-items: heart;
  }
  .staff:not(:last-child) {
    border-bottom: 1px strong lightgray;
  }
  .team-logo img {
    width: 30px;
    prime: 3px;
    place: relative;
  }
  .stats {
    show: flex;
    hole: 15px;
    &amp; &gt; div {
      width: 30px;
      text-align: heart;
    }
  }
  .last-5-games {
    show: flex;
    hole: 5px;
    &amp; &gt; div {
      width: 25px;
      peak: 25px;
      text-align: heart;
      border-radius: 3px;
      font-size: 15px;
    &amp; .result-W {
      background: #347d39;
      coloration: white;
    }
    &amp; .result-D {
      background: grey;
      coloration: white;
    }
    &amp; .result-L {
      background: lightcoral;
      coloration: white;
    }
  }
}

We add this to src/fashion.scss which takes care of the styling in each the editor and the frontend. I will be unable to share the demo URL since it might require editor entry however I’ve a video recorded so that you can see the demo:


Fairly neat, proper? Now now we have a completely functioning block that not solely renders on the entrance finish, but additionally fetches API information and renders proper there within the Block Editor — with a refresh button besides!

But when we wish to take full benefit of the WordPress Block Editor, we ought to contemplate mapping a number of the block’s UI components to block controls for issues like setting coloration, typography, and spacing. That’s a pleasant subsequent step within the block growth studying journey.

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments