Dette er en fortsættelse af min sidste artikel om "Gengivelse af eksterne API-data i WordPress-blokke på frontend". I den sidste lærte vi, hvordan man tager en ekstern API og integrerer den med en blok, der gengiver de hentede data på forsiden af et WordPress-websted.
Sagen er, at vi opnåede dette på en måde, der forhindrer os i at se dataene i WordPress Block Editor. Med andre ord kan vi indsætte blokken på en side, men vi får ingen forhåndsvisning af den. Vi får først blokken at se, når den er offentliggjort.
Lad os gense eksemplet med blok-plugin, vi lavede i den sidste artikel. Kun denne gang vil vi gøre brug af JavaScript og React-økosystemet i WordPress til også at hente og gengive disse data i back-end Block Editor.
Hvor vi slap
Da vi starter dette, her er en demo hvor vi landede i den sidste artikel, som du kan referere til. Du har måske bemærket, at jeg brugte en render_callback
metode i den sidste artikel, så jeg kan gøre brug af attributterne i PHP-filen og gengive indholdet.
Nå, det kan være nyttigt i situationer, hvor du måske skal bruge en eller anden indbygget WordPress- eller PHP-funktion til at skabe dynamiske blokke. Men hvis du kun vil bruge JavaScript og React (JSX, specifikt) økosystemet i WordPress til at gengive den statiske HTML sammen med de attributter, der er gemt i databasen, behøver du kun at fokusere på Edit
, Save
funktioner i blok-plugin'et.
-
Edit
funktionen gengiver indholdet baseret på, hvad du vil se i Block Editor. Du kan have interaktive React-komponenter her. -
Save
funktion gengiver indholdet baseret på, hvad du vil se på frontend. Du kan ikke have de almindelige React-komponenter eller krogene her. Det bruges til at returnere den statiske HTML, der er gemt i din database sammen med attributterne.
Save
funktion er, hvor vi hænger ud i dag. Vi kan oprette interaktive komponenter på front-end, men for det er vi nødt til manuelt at inkludere og få adgang til dem uden for Save
funktion i en fil, som vi gjorde i den sidste artikel.
Så jeg vil dække det samme område, som vi gjorde i den sidste artikel, men denne gang kan du se forhåndsvisningen i Block Editor før du udgiver det til frontend.
Blokrekvisitterne
Jeg har med vilje udeladt enhver forklaring om edit
funktions rekvisitter i den sidste artikel, fordi det ville have fjernet fokus fra hovedpunktet, gengivelsen.
Hvis du kommer fra en React-baggrund, vil du sandsynligvis forstå, hvad det er, jeg taler om, men hvis du er ny til dette, vil jeg anbefale tjekker komponenter og rekvisitter i React-dokumentationen.
Hvis vi logger props
objekt til konsollen, returnerer den en liste over WordPress-funktioner og variabler relateret til vores blok:
Vi har kun brug for attributes
objektet og setAttributes
funktion, som jeg vil destrukturere fra props
objekt i min kode. I den sidste artikel havde jeg ændret RapidAPI's kode, så jeg kan gemme API-dataene igennem setAttributes()
. Rekvisitter kan kun læses, så vi kan ikke ændre dem direkte.
Blokrekvisitter ligner tilstandsvariabler og setState
i React, men React fungerer på klientsiden og setAttributes()
bruges til at gemme attributterne permanent i WordPress-databasen efter at have gemt indlægget. Så det, vi skal gøre, er at gemme dem til attributes.data
og kald det derefter som startværdien for useState()
variabel.
edit
funktion
Jeg vil kopiere og indsætte HTML-koden, som vi brugte i football-rankings.php
i den sidste artikel og rediger den lidt for at skifte til JavaScript-baggrunden. Kan du huske, hvordan vi oprettede to ekstra filer i den sidste artikel til frontend-styling og scripts? Med den måde, vi griber tingene an på i dag, er der ingen grund til at oprette disse filer. I stedet kan vi flytte det hele til Edit
funktion.
Fuld kode
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 (
{apiData && (
Rank
Logo
Team name
GP
GW
GD
GL
GF
GA
Pts
Form history
{/* 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 (
{el.rank}
{el.team.name}
{played}
{win}
{draw}
{lose}
{goals.for}
{goals.against}
{el.points}
{el.form.split("").map((result) => {
return (
{result}
);
})}
);
}
)}
)}
);
}
Jeg har inkluderet React krogen useState()
fra @wordpress/element
i stedet for at bruge det fra React-biblioteket. Det er fordi, hvis jeg skulle indlæse den almindelige måde, ville den downloade React for hver blok, jeg bruger. Men hvis jeg bruger @wordpress/element
det indlæses fra en enkelt kilde, dvs. WordPress-laget oven på React.
Denne gang har jeg heller ikke pakket koden ind useEffect()
men inde i en funktion, der kun kaldes, når man klikker på en knap, så vi har et live preview af de hentede data. Jeg har brugt en tilstandsvariabel kaldet apiData
at gøre ligatabellen betinget. Så når først der er klikket på knappen, og dataene er hentet, indstiller jeg apiData
til de nye data inde i fetchData()
og der er en gengivelse med HTML fra fodboldranglisten tilgængelig.
Du vil bemærke, at når indlægget er gemt og siden er opdateret, er ligatabellen væk. Det er fordi vi bruger en tom tilstand (null
) til apiData
's begyndelsesværdi. Når indlægget gemmes, gemmes attributterne til attributes.data
objekt, og vi kalder det som startværdien for useState()
variabel som denne:
const [apiData, setApiData] = useState(attributes.data);
save
funktion
Vi kommer til at gøre næsten det samme med save
funktion, men modificer den lidt. For eksempel er der ikke behov for knappen "Hent data" på frontenden og apiData
tilstandsvariabel er også unødvendig, fordi vi allerede tjekker den i edit
fungere. Men vi har brug for et tilfældigt apiData
variabel, der tjekker for attributes.data
for at betinget gengive JSX, ellers vil det kaste udefinerede fejl, og Block Editor UI bliver tom.
Fuld kode
export default function save(props) {
const { attributes, setAttributes } = props;
let apiData = attributes.data;
return (
{/* Only render if apiData is available */}
{apiData && (
Rank
Logo
Team name
GP
GW
GD
GL
GF
GA
Pts
Form history
{/* 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 (
{el.rank}
{el.team.name}
{played}
{win}
{draw}
{lose}
{goals.for}
{goals.against}
{el.points}
{el.form.split("").map((result) => {
return (
{result}
);
})}
);
})}
)}
);
}
Hvis du ændrer save
funktion efter at en blok allerede er til stede i Block Editor, vil den vise en fejl som denne:
Det skyldes, at opmærkningen i det gemte indhold er forskellig fra opmærkningen i vores nye save
fungere. Da vi er i udviklingstilstand, er det nemmere at fjerne bocken fra den aktuelle side og genindsætte den som en ny blok - på den måde bruges den opdaterede kode i stedet, og tingene er tilbage i sync.
Denne situation med at fjerne det og tilføje det igen kan undgås, hvis vi havde brugt render_callback
metode, da outputtet er dynamisk og styret af PHP i stedet for gem-funktionen. Så hver metode har sine egne fordele og ulemper.
Tom Nowell giver en grundig forklaring på, hvad man ikke skal gøre i en save
funktion i denne Stack Overflow besvare.
Styling af blokken i editoren og frontend
Med hensyn til stylingen kommer det til at være næsten det samme, som vi så på i den sidste artikel, men med nogle mindre ændringer, som jeg har forklaret i kommentarerne. Jeg giver blot de fulde stilarter her, da dette kun er et bevis på konceptet snarere end noget, du vil kopiere og indsætte (medmindre du virkelig har brug for en blok for at vise fodboldranglister, der er stylet som dette). Og bemærk, at jeg stadig bruger SCSS, der kompilerer til CSS på build.
Editor stilarter
/* 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);
}
}
}
Front-end stilarter
/* 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;
& > 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;
& > div {
width: 30px;
text-align: center;
}
}
.last-5-games {
display: flex;
gap: 5px;
& > div {
width: 25px;
height: 25px;
text-align: center;
border-radius: 3px;
font-size: 15px;
& .result-W {
background: #347d39;
color: white;
}
& .result-D {
background: gray;
color: white;
}
& .result-L {
background: lightcoral;
color: white;
}
}
}
Vi tilføjer dette til src/style.scss
som tager sig af stylingen i både editor og frontend. Jeg vil ikke være i stand til at dele demo-URL'en, da den ville kræve redaktøradgang, men jeg har en video optaget, så du kan se demoen:
Ret pænt, ikke? Nu har vi en fuldt fungerende blok, der ikke kun gengiver på frontend, men også henter API-data og gengiver lige der i Block Editor - med en opdateringsknap til at starte!
Men hvis vi vil tage fuld fordel af WordPress Block Editor, bør vi overveje at kortlægge nogle af blokkens UI-elementer til blok kontrol til ting som indstilling af farve, typografi og mellemrum. Det er et godt næste skridt i blokudviklingslæringsrejsen.