到目前为止,我们已经介绍了如何在自定义 WordPress 块中使用来自外部 API 的数据。 我们介绍了 获取该数据以供 WordPress 网站前端使用 的过程,以及如何在 WordPress 块编辑器中 直接渲染该数据,以便在内容中放置块。 这次,我们将通过连接到块编辑器的控制面板来创建我们制作的块的设置 UI,从而将这两篇文章联系起来。
您知道我指的是哪个控制面板吗? 它是块编辑器中右侧包含帖子和块设置的面板。

看到那个红色突出显示的区域了吗? 那就是控制面板。 当前选中了段落块,并且其设置显示在面板中。 我们可以更改样式、颜色、排版……很多东西!
好吧,这正是我们这次要做的。 我们将为我们在前两篇文章中使用的足球排名块创建设置控件。 上次,我们在我们的块中创建了一个按钮,用于获取足球排名的外部数据。 我们已经知道所需的 URL 和端点。 但是,如果我们想为不同的国家获取排名怎么办? 或者可能是不同的联赛? 不同赛季的数据呢?
我们需要表单控件来做到这一点。 我们可以使用交互式 React 组件(例如 React-Select)来浏览可用于解析该数据的各种 API 选项。 但由于 WordPress 附带了一堆核心组件,我们可以直接连接到这些组件,所以没有必要这样做!
这些组件(称为 InspectorControls
)的 文档 在 WordPress 块编辑器手册 中变得越来越好。 随着时间的推移,这会变得更好,但与此同时,我们还有 WordPress Gutenberg Storybook 和 WordPress Gutenberg 组件 网站可以提供更多帮助。
API 架构
在连接到任何内容之前,最好先规划一下我们首先需要什么。 我已经映射了我们正在获取的 RapidAPI 数据的结构,以便我们知道哪些数据可用

赛季和国家是映射到联赛端点的两个顶级端点。 从那里,我们拥有我们已经用于填充排名表的其余数据。 因此,我们要做的是在 WordPress 块编辑器中创建设置,以按赛季、国家和联赛过滤数据,然后将这些过滤后的数据传递到排名表中。 这使我们能够将块放在任何 WordPress 页面或帖子中,并在块中显示数据的变体。
为了获得排名,我们首先需要获得联赛。 为了获得联赛,我们首先需要获得国家或赛季。 您可以在 RapidAPI 仪表板中查看各种端点。

我们可以使用不同的数据组合来填充排名,您可能对想要哪些数据有偏好。 为本文起见,我们将在块设置面板中创建以下选项
- 选择国家
- 选择联赛
- 选择赛季
然后我们将有一个按钮提交这些选择并获取相关数据,并将它们传递到排名表中。
加载和存储国家列表
如果我们没有要选择的国家列表,我们就无法选择我们想要哪些国家的数据。 因此,我们的首要任务是从 RapidAPI 获取国家列表。
理想的情况是在块实际用于页面或帖子内容时获取国家列表。 如果块未被使用,则无需获取任何内容。 该方法与我们在 第一篇文章 中所做的非常相似,区别在于我们使用的是不同的 API 端点和不同的属性来存储返回的国家列表。 WordPress 有其他方法可以获取数据,例如 api-fetch,但这超出了我们在这里讨论的范围。
我们可以在从 API 数据中复制后手动包含国家列表,或者我们可以使用单独的 API 或库来填充国家列表。 但是,我们正在使用的 API 已经包含了国家列表,因此我建议您使用其中一个端点。 让我们确保在将块插入块编辑器中的页面或帖子内容时加载初始国家列表
// edit.js
const [countriesList, setCountriesList] = useState(null);
useEffect(() => {
let countryOptions = {
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/countries", countryOptions)
.then( (response) => response.json() )
.then( (response) => {
let countriesArray = { ...response };
console.log("Countries list", countriesArray.response);
setCountriesList(countriesArray.response);
})
.catch((err) => console.error(err));
}, []);
我们有一个状态变量来存储国家列表。 接下来,我们将从 @wordpress/block-editor 包中导入一个名为 InspectorControls
的组件,该组件包含我们用于创建设置控件的所有组件。
import { InspectorControls } from "@wordpress/block-editor";
该包的 GitHub 仓库 对 InspectorControls
做了很好的解释。 在我们的示例中,我们可以使用它来控制 API 数据设置,例如国家、联赛和赛季。 这是一个预览,以便您了解我们正在制作的 UI

并且一旦在块设置中完成这些选择,我们将在 块的 Edit
函数 中使用它们
<InspectorControls>
{ countriesList && (
<LeagueSettings
props={props}
countriesList={ countriesList }
setApiData={ setApiData }
></LeagueSettings>
)}
</InspectorControls>
在这里,我确保我们使用的是条件渲染,以便该函数仅在加载国家列表后加载该组件。 如果您对 LeagueSettings
组件感到好奇,它是我在块中的一个单独的 components
子文件夹中创建的自定义组件,以便我们可以拥有更简洁、更有条理的 Edit
函数,而不是在一个文件中处理数百行国家数据。

我们可以像这样将其导入 edit.js
文件中
import { LeagueSettings } from "./components/LeagueSettings";
接下来,我们将必需的道具传递给父 Edit
组件中的 LeagueSettings
组件,以便我们可以从 LeagueSettings
子组件访问状态变量和属性。 我们还可以使用其他方法(例如 Context API)来避免道具向下传递,但我们现在所拥有的对于我们正在做的事情来说已经足够了。
Edit
函数的其他部分也可以转换为组件。 例如,联赛排名代码可以放在一个单独的组件(例如 LeagueTable.js
)中,然后像我们导入 LeagueSettings
到 Edit
函数中一样导入它。
LeagueSettings.js
文件中
在 LeagueSettings
就像另一个 React 组件,我们可以从中解构来自父组件的道具。 我将使用三个状态变量和一个额外的 leagueID
状态,因为我们将从 league
对象中提取 ID
const [country, setCountry] = useState(null);
const [league, setLeague] = useState(null);
const [season, setSeason] = useState(null);
const [leagueID, setLeagueID] = useState(null);
我们要做的第一件事是从 @wordpress/block-editor 包中导入 PanelBody
组件
import { PanelBody } from "@wordpress/block-editor";
…并将它包含在我们的 return
函数中
<PanelBody title="Data settings" initialOpen={false}></PanelBody>
存在 其他面板标签和属性 - 我只是个人喜欢使用这些属性。 其他属性都不是必需的……但是 看看我们有多少组件可用 来制作一个设置面板! 我喜欢 PanelBody
在我们的用例中的简洁性。 它展开和折叠以显示块的下拉设置,仅此而已。
说到这里,我们需要为这些选择做出选择。 我们可以使用 SelectControl
组件或 ComboBoxControl
,文档将其描述为“SelectControl
的增强版本,增加了能够使用搜索输入搜索选项的功能”。 这对我们来说很好,因为国家列表可能会变得很长,用户可以进行搜索查询或从列表中选择。
这是一个 ComboboxControl
如何用于我们的国家列表的示例
<ComboboxControl
label="Choose country"
value={country}
options={ filteredCountryOptions }
onChange={ (value) => handleCountryChange(value) }
onInputChange={ (inputValue) => {
setFilteredCountryOptions(
setupCountrySelect.filter((option) =>
option.label
.toLowerCase()
.startsWith(inputValue.toLowerCase())
)
);
}}
/>
ComboboxControl
是可配置的,这意味着我们可以为控件的标签和值应用不同的大小
{
value: 'small',
label: 'Small',
},
但是我们的 API 数据不是这种语法,因此我们可以转换来自父组件的 countriesList
数组,当块被包含时
let setupCountrySelect;
setupCountrySelect = countriesList.map((country) => {
return {
label: country.name,
value: country.name,
};
});
当从 ComboboxControl
中选择一个国家时,国家值会发生变化,我们相应地过滤数据
function handleCountryChange(value) {
// Set state of the country
setCountry(value);
// League code from RapidAPI
const options = {
method: "GET",
headers: {
"X-RapidAPI-Key": "Your RapidAPI key",
"X-RapidAPI-Host": "api-football-v1.p.rapidapi.com",
},
};
fetch(`https://api-football-v1.p.rapidapi.com/v3/leagues?country=${value}`, options)
.then((response) => response.json())
.then((response) => {
return response.response;
})
.then((leagueOptions) => {
// Set state of the league variable
setLeague(leagueOptions);
// Convert it as we did for Country options
setupLeagueSelect = leagueOptions.map((league) => {
return {
label: league.league.name,
value: league.league.name,
};
});
setFilteredLeagueOptions(setupLeagueSelect);
})
.catch((err) => console.error(err));
}
请注意,我正在使用另外三个状态变量来处理国家选择发生变化时的更改。
const [filteredCountryOptions, setFilteredCountryOptions] = useState(setupCountrySelect);
const [filteredLeagueOptions, setFilteredLeagueOptions] = useState(null);
const [filteredSeasonOptions, setFilteredSeasonOptions] = useState(null);
其他设置选项呢?
我将展示我用于其他设置的代码,但它只是在定义特殊情况的错误时考虑正常情况。例如,在某些国家和联赛中会出现错误,因为
- 某些联赛没有排名,以及
- 一些联赛有排名,但它们不在一个表格中。
这不是一个 JavaScript 或 React 教程,因此我将让您处理您打算使用的 API 的特殊情况。
function handleLeagueChange(value) {
setLeague(value);
if (league) {
const selectedLeague = league.filter((el) => {
if (el.league.name === value) {
return el;
}
});
if (selectedLeague) {
setLeague(selectedLeague[0].league.name);
setLeagueID(selectedLeague[0].league.id);
setupSeasonSelect = selectedLeague[0].seasons.map((season) => {
return {
label: season.year,
value: season.year,
};
});
setFilteredSeasonOptions(setupSeasonSelect);
}
} else {
return;
}
}
function handleSeasonChange(value) {
setSeason(value);
}
提交设置选择
在 上一篇文章 中,我们在块编辑器中创建了一个按钮,该按钮从 API 获取最新数据。现在我们有了设置,不再需要它了。好吧,我们确实需要它——只是不在它当前的位置。我们不会直接将其放在块编辑器中呈现的块中,而是将其移动到我们的 PanelBody
组件中,以提交设置选择。
因此,回到 LeagueSettings.js
// When countriesList is loaded, show the country combo box
{ countriesList && (
<ComboboxControl
label="Choose country"
value={country}
options={filteredCountryOptions}
onChange={(value) => handleCountryChange(value)}
onInputChange={(inputValue) => {
setFilteredCountryOptions(
setupCountrySelect.filter((option) =>
option.label
.toLowerCase()
.startsWith(inputValue.toLowerCase())
)
);
}}
/>
)}
// When filteredLeagueOptions is set through handleCountryChange, show league combobox
{ filteredLeagueOptions && (
<ComboboxControl
label="Choose league"
value={league}
options={filteredLeagueOptions}
onChange={(value) => handleLeagueChange(value)}
onInputChange={(inputValue) => {
setFilteredLeagueOptions(
setupLeagueSelect.filter((option) =>
option.label
.toLowerCase()
.startsWith(inputValue.toLowerCase())
)
);
}}
/>
)}
// When filteredSeasonOptions is set through handleLeagueChange, show season combobox
{ filteredSeasonOptions && (
<>
<ComboboxControl
label="Choose season"
value={season}
options={filteredSeasonOptions}
onChange={(value) => handleSeasonChange(value)}
onInputChange={
(inputValue) => {
setFilteredSeasonOptions(
setupSeasonSelect.filter((option) =>
option.label
.toLowerCase()
.startsWith(inputValue.toLowerCase()
)
);
}
}
/>
// When season is set through handleSeasonChange, show the "Fetch data" button
{
season && (
<button className="fetch-data" onClick={() => getData()}>Fetch data</button>
)
}
</>
</>
)}
结果如下!
我们的块处于非常好的状态。我们可以在块编辑器和网站的前端呈现它。我们可以根据我们创建的筛选数据的设置选择从外部 API 获取数据。它非常实用!
但是,我们还有另一件事需要解决。现在,当我们保存包含该块的页面或帖子时,我们为该块选择的设置将重置。换句话说,这些选择没有保存到任何地方。要使这些选择持久化,还需要做一些工作。我们将在下一篇文章中介绍这些内容,敬请关注。
很棒的文章。我一直在寻找一篇解释如何在 WordPress 中为块创建设置 UI 的文章,可以向同事展示。这是我找到的最简单的方法。