
常用文檔轉換API匯總
Last.fm是一個廣受歡迎的在線音樂信息服務平臺,您可以在這里獲取藝術家、專輯、熱門歌曲等詳細信息。在創建用戶資料后,該平臺會根據您的收聽習慣推薦歌曲。對于這個應用程序,您將使用Last.fm提供的專輯搜索API。
要構建此應用程序,您需要滿足以下條件:
如果您想跳過詳細信息并快速入門,您可以在 GitHub 上找到該應用程序。您可以直接將其安裝在 Contentful 空間上,也可以在您的計算機上本地運行它。
要學習從頭開始構建應用程序,請繼續閱讀。
您可以為您的 Contentful 空間創建應用程序,以自定義編輯體驗、集成外部服務等等。
要為您的 Contentful 空間創建應用程序,您將使用 App Framework。App Framework 提供了創建應用程序所需的所有工具。它提供的包允許您與 API 交互、與各種位置交互等。
您可以使用 CLI 在計算機上創建應用程序,方法是運行以下命令來引導應用程序。
npx create-contentful-app
注意: 默認情況下,上述命令將在 TypeScript 中引導項目。如果要使用 JavaScript,請使用 flag。--javascript
存儲庫準備就緒后,您可以通過從項目目錄運行以下命令來啟動本地服務器。
npm start
如果您導航到 localhost:3000,您將看到一條警告消息,指出您無法在 Contentful 之外查看應用程序。
在下一節中,您將學習如何在 Contentful 中創建應用程序,并在 Contentful 中與應用程序交互。
既然您已經在本地運行了項目,下一步就是創建應用程序并在Contentful中配置其應用程序定義。應用程序定義將允許您選擇位置、主機URL以及其他應用程序參數。
登錄到 Contentful 空間,然后從 應用程序 下拉列表中選擇 管理應用程序。接下來,單擊左上角的 Manage app definitions,然后單擊 Create app 按鈕。
在 App name 字段中輸入應用程序的名稱,然后單擊 Create app。這將創建一個新應用程序,并將您帶到應用程序的定義頁面。
由于您將在開發過程中在本地運行應用程序,因此在 App definition (應用程序定義) 屏幕上,在 frontend 字段中輸入 http://localhost:3000。
接下來,在 Locations (位置) 下,選擇 App configuration (應用程序配置) 屏幕。應用程序配置位置允許您從用戶那里獲取配置詳細信息,例如,設置 Last.fm API 密鑰。
您還需要選擇 Entry field > JSON 對象。這是允許用戶與應用程序交互并存儲信息的位置。
單擊 Save 按鈕以保存應用程序定義。您的應用程序定義應與下圖相同。
要在 Contentful 空間上安裝應用程序,請單擊操作,然后選擇安裝到空間。
從 Select a space 下拉列表中選擇您的空間,并從 Select an environment 下拉列表中選擇環境。授權應用程序,您將看到默認的 App config 頁面。
在下一節中,您將了解如何自定義 App configuration location。
如果您在應用程序定義中進行了配置,那么應用程序配置位置將是用戶安裝應用程序后與之交互的第一個屏幕。用戶可以在此處提供詳細信息,例如 API 密鑰、他們想要獲取的屬性等。
要自定義 App configuration location(應用程序配置位置),請打開該文件。要添加表單,請將其中的代碼替換為以下代碼。src/locations/ConfigScreen.tsxreturn()
<Flex flexDirection="column" className={css({ margin: '80px', maxWidth: '800px' })}>
<Form>
<FormControl>
<FormControl.Label>API Key</FormControl.Label>
<TextInput
value={parameters.apiKey}
type='text'
onChange={(e) => setParameters({...parameters, apiKey:e.target.value})}
/>
</FormControl>
</Form>
</Flex>
上面的代碼使用了 Forma 36 中的 、 、 和 組件。更新導入以使用這些組件。FlexFormFormControlTextInput
您還可以觀察到 value 屬性從 獲取值。TextInput
parameters.apiKey
parameters 是已為您定義的類型的狀態。AppInstallationParameters
要充分利用 TypeScript,請更新 .你的應用 AppInstallationParameters 應如下所示。AppInstallationParameters
export interface AppInstallationParameters {
apiKey: string | undefined;
}
最終代碼應如下所示。
import React, { useCallback, useState, useEffect } from 'react';
import { AppExtensionSDK } from '@contentful/app-sdk';
import {Form, FormControl, Flex, TextInput } from '@contentful/f36-components';
import { css } from 'emotion';
import { useSDK } from '@contentful/react-apps-toolkit';
export interface AppInstallationParameters {
apiKey: string | undefined;
}
const ConfigScreen = () => {
const [parameters, setParameters] = useState<AppInstallationParameters>({apiKey: ''});
const sdk = useSDK<AppExtensionSDK>();
const onConfigure = useCallback(async () => {
const currentState = await sdk.app.getCurrentState();
return {
parameters,
targetState: currentState,
};
}, [parameters, sdk]);
useEffect(() => {
sdk.app.onConfigure(() => onConfigure());
}, [sdk, onConfigure]);
useEffect(() => {
(async () => {
const currentParameters: AppInstallationParameters | null = await sdk.app.getParameters();
if (currentParameters) {
setParameters(currentParameters);
}
sdk.app.setReady();
})();
}, [sdk]);
return (
<Flex flexDirection="column" className={css({ margin: '80px', maxWidth: '800px' })}>
<Form>
<FormControl>
<FormControl.Label>API Key</FormControl.Label>
<TextInput
value={parameters.apiKey}
type='text'
onChange={(e) => setParameters({...parameters, apiKey:e.target.value})}
/>
</FormControl>
</Form>
</Flex>
);
};
export default ConfigScreen;
更深入地了解代碼超出了本文的范圍。如果您有興趣了解更多信息,可以閱讀官方文檔。
但以下是代碼作用的快速摘要:當應用程序首次安裝時,用戶會看到此表單。用戶在此處輸入其API密鑰。當用戶點擊“安裝”按鈕時,API密鑰會被存儲在參數對象中。這樣,您就可以從其他任何位置引用該值。
保存代碼,您現在將在 App configuration (應用程序配置) 位置查看表單。輸入您的 Last.fm API 密鑰,然后點擊 Install(安裝)。
您的應用程序現已安裝并可供使用!
在要添加唱片集數據的內容類型中,創建一個 JSON 對象類型的新字段。確保在 Appearance (外觀) 選項卡中選擇您的應用。
為該內容類型創建一個新條目,您應該會看到默認的 JSON 編輯器。您將在下一節中了解如何自定義此字段。
在配置 App definition 時,您選擇了 App configuration 作為 Entry 字段位置。
在上一節中,您自定義了 App configuration location(應用程序配置位置)。在本節中,您將自定義 Entry field location 以顯示用戶輸入和渲染數據。
對于此應用程序,用戶將輸入他們想要添加的相冊名稱。該應用程序將從 API 返回所有相關相冊,并在對話框組件中向用戶顯示它們。用戶選擇相冊,應用程序保存相冊信息。
與 App configuration location(應用程序配置位置)類似,您可以自定義文件中的 Entry field (src/locations/Field.tsx
) 位置。打開文件并將 return 語句替換為以下代碼。
return (
<>
<Form onSubmit={()=>openDialog()}>
<FormControl>
<FormControl.Label isRequired>Album name</FormControl.Label>
<TextInput type='text' onChange={(e) => setAlbumSearch(e.target.value)} isRequired/>
</FormControl>
<FormControl>
<Button type='submit' variant='primary'>Search</Button>
</FormControl>
</Form>
</>
)
更新導入以包括Form
、FormControl
、TextInput
和Button
組件。
如果您仔細觀察代碼,則會看到一個albumSearch
state 和一個 openDialog
function。按如下方albumSearch
式初始化狀態。確保更新useState
hook 的導入。
const [albumSearch, setAlbumSearch] = useState<string>('');
接下來,按如下方式定義openDialog
函數。
const openDialog = async () => {
const album = await sdk.dialogs.openCurrentApp({
width: 700,
parameters: {
albumName: albumSearch
},
title: "Album Search",
allowHeightOverflow:true,
shouldCloseOnEscapePress: true,
shouldCloseOnOverlayClick: true
})
}
上述函數將打開一個對話框組件,該組件從 Last.fm API 返回結果。你正在傳遞albumName
參數,該參數在Dialog
組件中使用。
您的最終代碼應如下所示。
import React, {useState} from 'react';
import { Form, FormControl, TextInput, Button } from '@contentful/f36-components';
import { FieldExtensionSDK } from '@contentful/app-sdk';
import { useSDK } from '@contentful/react-apps-toolkit';
const Field = () => {
const sdk = useSDK<FieldExtensionSDK>();
const [albumSearch, setAlbumSearch] = useState<string>('');
const openDialog = async () => {
const album = await sdk.dialogs.openCurrentApp({
width: 700,
parameters: {
albumName: albumSearch
},
title: "Album Search",
allowHeightOverflow:true,
shouldCloseOnEscapePress: true,
shouldCloseOnOverlayClick: true
})
}
return (
<>
<Form onSubmit={()=>openDialog()}>
<FormControl>
<FormControl.Label isRequired>Album name</FormControl.Label>
<TextInput type='text' onChange={(e) => setAlbumSearch(e.target.value)} isRequired/>
</FormControl>
<FormControl>
<Button type='submit' variant='primary'>Search</Button>
</FormControl>
</Form>
</>
)
};
export default Field;
保存代碼,應用程序將熱重載。您的字段現在將具有一個輸入字段和一個類似于下圖的按鈕。
嘗試提交表單,對話框將呈現。Marty,相信你已經知道下一步是什么了— 自定義對話組件。
在本節中,您將學習如何自定義對話組件。此組件將從 Last.fm API 獲取數據,并將其呈現給用戶。
打開src/locations/Dialog.tsx
文件和以下 TypeScript 接口。
export interface Album {
name: string;
artist: string;
url: string;
image: Image[];
streamable: string;
mbid: string;
}
export interface Image {
"#text": string;
size: string;
}
接下來,聲明 Album 類型的狀態相冊。您將 API 調用的結果存儲在 album 狀態中。您可以使用 API 密鑰從 Last.fm 獲取數據。由于您在安裝應用程序時已經設置了 API 密鑰,因此您將使用該 API 密鑰。銷毀apiKey
fromsdk.parameters.installation
const [album,setAlbum] = useState<Album[] | undefined>();
const {apiKey} = sdk.parameters.installation;
現在,您已經擁有了狀態和 API 密鑰,您將定義一個函數,該函數將從 Last.fm API 獲取數據。復制并粘貼以下函數代碼。
const fetchData = async (albumName: string) => {
const response = await fetch(https://ws.audioscrobbler.com/2.0/?method=album.search&album=${albumName}&api_key=${apiKey}&format=json
)
const {results} = await response.json();
setAlbum(results.albummatches.album);
}
在上面的代碼中,您正在使用 fetch API 從 API 獲取結果。在查詢參數中,您傳遞了專輯名稱 (albumNameapi
) 和 API 密鑰 (Key
)。
從 API 獲得響應后,您可以使用json()
方法解析 promise 并銷毀 results 數組。最后,使用所需的結果更新專輯狀態的值。
該應用程序從您之前創建的輸入字段中獲取搜索查詢。用戶提交表單后,fetchData
將呈現對話框。該函數應在組件渲染后立即執行。
因此,添加一個useEffect
鉤子,并調用從輸入字段fetchData
傳遞專輯名稱的函數。下面是執行此操作的代碼。
useEffect(()=>{
// @ts-expect-error
fetchData(sdk.parameters.invocation.albumName)
},[sdk.parameters.invocation])
每次用戶搜索新專輯時,您的組件都會獲取數據。但是,該組件仍然不呈現數據。將 return 替換為以下代碼,以在 dialog 組件中顯示數據。
if(!album){
return <Spinner size="large" />
}
return (
<Stack fullWidth>
<EntityList style={{
width: '100%'
}}>
{
album.map((item,i)=>{
return(<EntityList.Item
key={i}
title={item.name}
thumbnailUrl={item.image[1]['#text']}
onClick={()=> sdk.close({
name: item.name,
image: item.image[2]['#text']
})}
/>)
})
}
</EntityList>
</Stack>
);
讓我們了解一下上面的代碼中發生了什么。使用 IF 語句,您首先檢查相冊是否包含任何值。如果應用程序正在獲取數據,則專輯狀態為 null,用戶將看到一個微調器。但是一旦數據被獲取,結果就會被渲染。
onClick
組件的EntityList.Item
屬性將關閉對話框組件,并將所選影集的名稱和圖像信息發送回字段位置。
在繼續下一部分之前,請將掛鉤useAutoResizer()
添加到對話組件中。這個 hook 處理組件的大小調整。您的對話組件應如下所示。
import React, { useEffect, useState } from 'react';
import { Spinner, Stack, EntityList } from '@contentful/f36-components';
import { DialogExtensionSDK } from '@contentful/app-sdk';
import { useAutoResizer, useSDK } from '@contentful/react-apps-toolkit';
export interface Album {
name: string;
artist: string;
url: string;
image: Image[];
streamable: string;
mbid: string;
}
export interface Image {
"#text": string;
size: string;
}
const Dialog = () => {
const sdk = useSDK<DialogExtensionSDK>();
useAutoResizer();
const [album,setAlbum] = useState<Album[] | undefined>();
const {apiKey} = sdk.parameters.installation;
const fetchData = async (albumName: string) => {
const response = await fetch(https://ws.audioscrobbler.com/2.0/?method=album.search&album=${albumName}&api_key=${apiKey}&format=json
)
const {results} = await response.json();
setAlbum(results.albummatches.album);
}
useEffect(()=>{
// @ts-expect-error
fetchData(sdk.parameters.invocation.albumName)
},[sdk.parameters.invocation])
if(!album){
return <Spinner size="large" />
}
return (
<Stack fullWidth>
<EntityList style={{
width: '100%'
}}>
{
album.map((item,i)=>{
return(<EntityList.Item
key={i}
title={item.name}
thumbnailUrl={item.image[1]['#text']}
onClick={()=> sdk.close({
name: item.name,
image: item.image[2]['#text']
})}
/>)
})
}
</EntityList>
</Stack>
);
};
export default Dialog;
使用您的應用,用戶可以搜索他們最喜歡的專輯。用戶從搜索結果中選擇相冊后,應用程序既不會向用戶顯示相冊,也不會將數據存儲在 Contentful 中。
在本節中,您將更新 Field 組件以呈現所選數據并將其存儲在 Contentful 中。
打開src/locations/Field.tsx
文件并聲明接口Album
。
interface Album {
name: string,
image: string
}
接下來,從@contentful/react-apps-toolkit
包中導入useFieldValue
鉤子,并按如下方式聲明albumData
。
const [albumData, setAlbumData] = useFieldValue<Album | null>()
更新openDialog
函數,并將albumData
的值設置為album
。
const openDialog = async () => {
const album = await sdk.dialogs.openCurrentApp({
width: 700,
parameters: {
albumName: albumSearch
},
title: "Album Search",
allowHeightOverflow:true,
shouldCloseOnEscapePress: true,
shouldCloseOnOverlayClick: true
})
if(album){
setAlbumData(album);
}
}
當用戶從列表中選擇相冊時,對話框將關閉,并返回相冊名稱和相冊圖像。上面的代碼將此對象值存儲在字段中。用戶可能會關閉對話框而不選擇相冊。IF 語句可以解決這個問題。
最后一步是更新我們的組件以顯示選定的相冊。在 closing 標簽的末尾添加以下Form
代碼。
{
albumData && (
<AssetCard
type='image'
title={albumData.name}
src={albumData.image}
actions={[
<MenuItemkey="remove"onClick={()=>setAlbumData(null)}>Remove</MenuItem>
]}
/>
)
}
在這里,您再次檢查albumData
是否包含任何數據。如果它包含從對話框組件返回的數據,您將使用Forma 36的AssetCard
組件來渲染它。您還添加了一個操作,允許用戶刪除選定的專輯。
您應該添加的最后一件事是useAutoResizer
鉤子。您的字段位置(Field location)的最終代碼應該如下所示:
import React, {useState} from 'react';
import { Form, FormControl, TextInput, Button, AssetCard, MenuItem } from '@contentful/f36-components';
import { FieldExtensionSDK } from '@contentful/app-sdk';
import { useAutoResizer, useFieldValue, useSDK } from '@contentful/react-apps-toolkit';
interface Album {
name: string,
image: string
}
const Field = () => {
const sdk = useSDK<FieldExtensionSDK>();
useAutoResizer();
const [albumSearch, setAlbumSearch] = useState<string>('');
const [albumData, setAlbumData] = useFieldValue<Album | null>()
const openDialog = async () => {
const album = await sdk.dialogs.openCurrentApp({
width: 700,
parameters: {
albumName: albumSearch
},
title: "Album Search",
allowHeightOverflow:true,
shouldCloseOnEscapePress: true,
shouldCloseOnOverlayClick: true
})
if(album){
setAlbumData(album);
}
}
return (
<>
<Form onSubmit={()=>openDialog()}>
<FormControl>
<FormControl.Label isRequired>Album name</FormControl.Label>
<TextInput type='text' onChange={(e) => setAlbumSearch(e.target.value)} isRequired/>
</FormControl>
<FormControl>
<Button type='submit' variant='primary'>Search</Button>
</FormControl>
</Form>
{
albumData && (
<AssetCard
type='image'
title={albumData.name}
src={albumData.image}
actions={[
<MenuItem key="remove" onClick={()=>setAlbumData(null)}>Remove</MenuItem>
]}
/>
)
}
</>
)
};
export default Field;
嘗試搜索其他影集。您的應用程序現在將提取數據、顯示搜索結果并呈現所選相冊。
在本文中,您創建了一個與外部 API 集成的 Contentful,并允許您添加內容。
現在,您的應用程序已準備就緒,請托管它以供您的團隊使用。您可以將應用程序托管在 Contentful 或 Netlify、Vercel 等外部服務上。按照文檔了解如何托管您的應用程序。
您可以在開發者展示中查看社區創建的大量應用程序。如果您創建的應用程序可能會使其他人受益,請考慮提交。
最后,請隨時在 Twitter 上與我聯系,分享您最喜歡的音樂劇、談論 Contentful 或提出問題。
原文來源:https://www.contentful.com/blog/build-custom-contentful-app-last-fm-api/