Detta är en fortsättning på min senaste artikel om "Återgivning av extern API-data i WordPress-block i användargränssnittet". I den sista lärde vi oss hur man tar ett externt API och integrerar det med ett block som återger hämtad data på fronten av en WordPress-webbplats.
Saken är den att vi åstadkom detta på ett sätt som hindrar oss från att se data i WordPress Block Editor. Med andra ord kan vi infoga blocket på en sida men vi får ingen förhandsvisning av det. Blocket får vi se först när det publiceras.
Låt oss återgå till exempelblockplugin som vi gjorde i den senaste artikeln. Bara den här gången kommer vi att använda JavaScript och React-ekosystemet i WordPress för att hämta och rendera dessa data även i back-end Block Editor.
Där vi slutade
När vi startar detta, här är en demo där vi landade i den sista artikeln som du kan referera till. Du kanske har märkt att jag använde en render_callback
metod i den sista artikeln så att jag kan använda attributen i PHP-filen och rendera innehållet.
Tja, det kan vara användbart i situationer där du kanske måste använda någon inbyggd WordPress- eller PHP-funktion för att skapa dynamiska block. Men om du bara vill använda JavaScript och React (JSX, specifikt) ekosystemet i WordPress för att återge den statiska HTML-koden tillsammans med attributen som lagras i databasen, behöver du bara fokusera på Edit
och Save
funktioner för blockplugin.
- Smakämnen
Edit
funktionen återger innehållet baserat på vad du vill se i Block Editor. Du kan ha interaktiva React-komponenter här. - Smakämnen
Save
funktionen återger innehållet baserat på vad du vill se på användargränssnittet. Du kan inte ha de vanliga React-komponenterna eller krokarna här. Den används för att returnera den statiska HTML som sparats i din databas tillsammans med attributen.
Smakämnen Save
funktion är där vi umgås idag. Vi kan skapa interaktiva komponenter på front-end, men för det måste vi manuellt inkludera och komma åt dem utanför Save
funktion i en fil som vi gjorde i förra artikeln.
Så jag kommer att täcka samma grund som vi gjorde i förra artikeln, men den här gången kan du se förhandsvisningen i Block Editor innan du publicerar den i frontend.
Blockets rekvisita
Jag utelämnade medvetet alla förklaringar om edit
funktions rekvisita i den senaste artikeln eftersom det skulle ha tagit fokus från huvudpoängen, renderingen.
Om du kommer från en React-bakgrund kommer du förmodligen att förstå vad jag pratar om, men om du är ny på detta skulle jag rekommendera kolla in komponenter och rekvisita i React-dokumentationen.
Om vi loggar props
objekt till konsolen, returnerar den en lista med WordPress-funktioner och variabler relaterade till vårt block:
Vi behöver bara attributes
objekt och setAttributes
funktion som jag kommer att destrukturera från props
objekt i min kod. I den förra artikeln hade jag modifierat RapidAPI:s kod så att jag kan lagra API-data igenom setAttributes()
. Rekvisita är bara läsbara, så vi kan inte ändra dem direkt.
Blockrekvisita liknar tillståndsvariabler och setState
i React, men React arbetar på klientsidan och setAttributes()
används för att lagra attributen permanent i WordPress-databasen efter att inlägget har sparats. Så vad vi behöver göra är att spara dem till attributes.data
och kalla det sedan som startvärdet för useState()
variabel.
edit
fungera
Smakämnen Jag kommer att kopiera och klistra in HTML-koden som vi använde i football-rankings.php
i den sista artikeln och redigera den lite för att flytta till JavaScript-bakgrunden. Kommer du ihåg hur vi skapade ytterligare två filer i den senaste artikeln för frontend-styling och skript? Med det sätt vi närmar oss saker idag, finns det inget behov av att skapa dessa filer. Istället kan vi flytta allt till Edit
funktion.
Fullständig kod
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}
);
})}
);
}
)}
)}
);
}
Jag har inkluderat React-kroken useState()
från @wordpress/element
istället för att använda det från React-biblioteket. Det beror på att om jag skulle ladda på det vanliga sättet, skulle det ladda ner React för varje block som jag använder. Men om jag använder @wordpress/element
det laddas från en enda källa, dvs WordPress-lagret ovanpå React.
Den här gången har jag inte heller packat in koden useEffect()
men inuti en funktion som bara anropas när man klickar på en knapp så att vi har en liveförhandsvisning av hämtad data. Jag har använt en tillståndsvariabel som heter apiData
att göra serietabellen villkorligt. Så när knappen väl har klickats och data hämtas, ställer jag in apiData
till de nya uppgifterna inuti fetchData()
och det finns en återgivning med HTML-koden för fotbollsrankningstabellen tillgänglig.
Du kommer att märka att när inlägget har sparats och sidan har uppdaterats är ligatabellen borta. Det beror på att vi använder ett tomt tillstånd (null
) för apiData
s initiala värde. När inlägget sparas sparas attributen till attributes.data
objekt och vi kallar det som det initiala värdet för useState()
variabel så här:
const [apiData, setApiData] = useState(attributes.data);
save
fungera
Smakämnen Vi kommer att göra nästan exakt samma sak med save
funktion, men modifiera den lite. Till exempel, det finns inget behov av "Hämta data"-knappen på fronten, och apiData
tillståndsvariabel är också onödig eftersom vi redan kontrollerar den i edit
fungera. Men vi behöver en slump apiData
variabel som kollar efter attributes.data
för att villkorligt rendera JSX, annars kommer det att skapa odefinierade fel och Block Editor-gränssnittet blir tomt.
Fullständig kod
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}
);
})}
);
})}
)}
);
}
Om du ändrar save
funktion efter att ett block redan finns i Block Editor, skulle det visa ett fel som detta:
Det beror på att uppmärkningen i det sparade innehållet skiljer sig från uppmärkningen i vår nya save
fungera. Eftersom vi är i utvecklingsläge är det lättare att ta bort bocken från den aktuella sidan och infoga den igen som ett nytt block — på så sätt används den uppdaterade koden istället och saker och ting är tillbaka i synk.
Denna situation med att ta bort den och lägga till den igen kan undvikas om vi hade använt den render_callback
metod eftersom utdata är dynamiskt och styrs av PHP istället för spara-funktionen. Så varje metod har sina egna fördelar och nackdelar.
Tom Nowell ger en grundlig förklaring om vad man inte ska göra i en save
funktion i detta Stack Overflow svara.
Styling av blocket i editorn och frontend
Angående stylingen kommer det att vara nästan samma sak som vi tittade på i förra artikeln, men med några mindre ändringar som jag har förklarat i kommentarerna. Jag tillhandahåller bara de fullständiga stilarna här eftersom detta bara är ett bevis på konceptet snarare än något du vill kopiera och klistra in (såvida du inte verkligen behöver ett block för att visa fotbollsrankningar utformade precis så här). Och notera att jag fortfarande använder SCSS som kompilerar till CSS på build.
Redaktörsstilar
/* 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 stilar
/* 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 lägger till detta src/style.scss
som tar hand om stylingen i både editorn och frontend. Jag kommer inte att kunna dela demo-URL eftersom det skulle kräva redaktörsåtkomst men jag har en video inspelad så att du kan se demon:
Ganska snyggt, eller hur? Nu har vi ett fullt fungerande block som inte bara renderar i gränssnittet, utan också hämtar API-data och renderar direkt i blockredigeraren – med en uppdateringsknapp för att starta upp!
Men om vi vill ta full fördelen med WordPress Block Editor, bör vi överväga att kartlägga några av blockets UI-element till blockera kontroller för saker som att ställa in färg, typografi och avstånd. Det är ett trevligt nästa steg i inlärningsresan för blockutveckling.