Это продолжение моей прошлой статьи о «Визуализация данных внешнего API в блоках WordPress на внешнем интерфейсе». В последнем мы узнали, как взять внешний API и интегрировать его с блоком, который отображает полученные данные во внешнем интерфейсе сайта WordPress.
Дело в том, что мы сделали это таким образом, что не можем видеть данные в редакторе блоков WordPress. Другими словами, мы можем вставить блок на страницу, но не получим его предварительный просмотр. Мы видим блок только тогда, когда он опубликован.
Давайте вернемся к примеру блочного плагина, который мы сделали в прошлой статье. Только на этот раз мы собираемся использовать экосистему JavaScript и React WordPress для извлечения и рендеринга этих данных во внутреннем редакторе блоков.
Где мы остановились
Когда мы начинаем это, вот демо где мы приземлились в последней статье, на которую вы можете сослаться. Вы, наверное, заметили, что я использовал render_callback
метод в последней статье, чтобы я мог использовать атрибуты в файле PHP и отображать содержимое.
Что ж, это может быть полезно в ситуациях, когда вам, возможно, придется использовать некоторые встроенные функции WordPress или PHP для создания динамических блоков. Но если вы хотите использовать только экосистему JavaScript и React (в частности, JSX) WordPress для отображения статического HTML вместе с атрибутами, хранящимися в базе данных, вам нужно сосредоточиться только на Edit
и Save
функции блочного плагина.
- Ассоциация
Edit
Функция отображает содержимое на основе того, что вы хотите видеть в редакторе блоков. Здесь вы можете иметь интерактивные компоненты React. - Ассоциация
Save
Функция отображает контент в зависимости от того, что вы хотите видеть в интерфейсе. У вас не может быть здесь обычных компонентов React или хуков. Он используется для возврата статического HTML, который сохраняется в вашей базе данных вместе с атрибутами.
Ассоциация Save
функция, где мы болтаемся сегодня. Мы можем создавать интерактивные компоненты во внешнем интерфейсе, но для этого нам нужно вручную включить и получить к ним доступ вне интерфейса. Save
в файле, как мы это делали в прошлой статье.
Итак, я собираюсь рассказать о том же, что и в прошлой статье, но на этот раз вы можете увидеть предварительный просмотр в редакторе блоков. до вы публикуете его в интерфейсе.
Блок реквизит
Я намеренно опустил какие-либо пояснения по поводу edit
функции в последней статье, потому что это отвлекло бы внимание от основного момента, рендеринга.
Если у вас есть опыт работы с React, вы, вероятно, поймете, о чем я говорю, но если вы новичок в этом, я бы порекомендовал проверка компонентов и реквизита в документации React.
Если мы зарегистрируем props
объект в консоль, он возвращает список функций и переменных WordPress, связанных с нашим блоком:
Нам нужно только attributes
объект и setAttributes
функцию, которую я собираюсь деструктурировать из props
объект в моем коде. В прошлой статье я модифицировал код RapidAPI, чтобы хранить данные API через setAttributes()
. Реквизит доступен только для чтения, поэтому мы не можем изменять его напрямую.
Блочные реквизиты похожи на переменные состояния и setState
в React, но React работает на стороне клиента и setAttributes()
используется для постоянного хранения атрибутов в базе данных WordPress после сохранения сообщения. Итак, что нам нужно сделать, это сохранить их в attributes.data
а затем вызовите это как начальное значение для useState()
Переменная.
edit
функция
Ассоциация Я собираюсь скопировать и вставить код HTML, который мы использовали в football-rankings.php
в прошлой статье и немного отредактируйте его, чтобы перейти к фону JavaScript. Помните, как в прошлой статье мы создали два дополнительных файла для стилей интерфейса и скриптов? С тем, как мы подходим к делу сегодня, нет необходимости создавать эти файлы. Вместо этого мы можем переместить все это в Edit
функции.
Полный код
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}
);
})}
);
}
)}
)}
);
}
Я включил хук React useState()
от @wordpress/element
вместо того, чтобы использовать его из библиотеки React. Это потому, что если бы я загружался обычным способом, он загружал бы React для каждого блока, который я использую. Но если я использую @wordpress/element
он загружается из одного источника, то есть слоя WordPress поверх React.
На этот раз я также не заворачивал код внутрь useEffect()
но внутри функции, которая вызывается только при нажатии на кнопку, чтобы у нас был предварительный просмотр полученных данных в реальном времени. Я использовал переменную состояния, называемую apiData
для условного отображения рейтинговой таблицы. Итак, как только кнопка нажата и данные получены, я устанавливаю apiData
к новым данным внутри fetchData()
и есть повторный рендеринг с HTML-таблицей футбольного рейтинга.
Вы заметите, что после сохранения сообщения и обновления страницы рейтинговая таблица исчезает. Это потому, что мы используем пустое состояние (null
) Для apiData
начальное значение. Когда сообщение сохраняется, атрибуты сохраняются в attributes.data
объект, и мы называем его начальным значением для useState()
такая переменная:
const [apiData, setApiData] = useState(attributes.data);
save
функция
Ассоциация Мы собираемся сделать почти то же самое с save
функцию, но немного изменить ее. Например, нет необходимости в кнопке «Получить данные» во внешнем интерфейсе, а apiData
переменная состояния также не нужна, потому что мы уже проверяем ее в edit
функция. Но нам нужен случайный apiData
переменная, которая проверяет attributes.data
для условного рендеринга JSX, иначе он выдаст неопределенные ошибки, а пользовательский интерфейс редактора блоков станет пустым.
Полный код
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}
);
})}
);
})}
)}
);
}
Если вы модифицируете save
после того, как блок уже присутствует в редакторе блоков, будет отображаться такая ошибка:
Это связано с тем, что разметка в сохраненном контенте отличается от разметки в нашем новом save
функция. Поскольку мы находимся в режиме разработки, проще удалить блок с текущей страницы и повторно вставить его как новый блок — таким образом, вместо этого будет использоваться обновленный код, и все снова будет синхронизировано.
Этой ситуации удаления и повторного добавления можно было бы избежать, если бы мы использовали render_callback
метод, так как вывод является динамическим и управляется PHP вместо функции сохранения. Таким образом, каждый метод имеет свои преимущества и недостатки.
Том Ноуэлл подробно объясняет, чего не следует делать в save
функции в это переполнение стека ответ.
Стилизация блока в редакторе и интерфейсе
Что касается стиля, он будет почти таким же, как мы рассматривали в прошлой статье, но с небольшими изменениями, которые я объяснил в комментариях. Я просто предоставляю полные стили здесь, так как это всего лишь доказательство концепции, а не то, что вы хотите скопировать-вставить (если вам действительно не нужен блок для отображения футбольных рейтингов, оформленный именно так). И обратите внимание, что я все еще использую SCSS, который компилируется в CSS при сборке.
Редактор стилей
/* 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 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;
}
}
}
Мы добавляем это к src/style.scss
который заботится о стиле как в редакторе, так и во внешнем интерфейсе. Я не смогу поделиться демонстрационным URL-адресом, так как для этого потребуется доступ редактора, но у меня есть записанное видео, чтобы вы могли посмотреть демонстрацию:
Довольно аккуратно, правда? Теперь у нас есть полностью функционирующий блок, который не только рендерится во внешнем интерфейсе, но также извлекает данные API и рендерится прямо в редакторе блоков — с кнопкой обновления для загрузки!
Но если мы хотим взять полный преимущества редактора блоков WordPress, мы должны рассмотреть возможность сопоставления некоторых элементов пользовательского интерфейса блока с блок управления для таких вещей, как настройка цвета, типографики и интервалов. Это хороший следующий шаг в обучении разработке блоков.