2022 年 4 月 27 日

使用 Remix、Prisma 和 MongoDB 構建全棧應用程式:CRUD、過濾和排序

閱讀 17 分鐘

歡迎閱讀本系列的第三篇文章,您將學習如何使用 MongoDB、Prisma 和 Remix 從頭開始構建一個全棧應用程式!在本部分中,您將構建應用程式的主要部分,該部分顯示使用者的稱讚動態並允許他們向其他使用者傳送稱讚。

Build A Fullstack App with Remix, Prisma & MongoDB: CRUD, Filtering & Sorting

目錄

引言

在本系列的上一部分中,您構建了應用程式的登入和登錄檔單,並實現了基於會話的身份驗證。您還更新了 Prisma 模式,以在 User 模型中新增一個新的嵌入式文件,用於儲存使用者資料資料。

在本部分中,您將構建應用程式的主要功能:稱讚動態。每個使用者都將擁有一個其他使用者傳送給他們的稱讚動態。使用者還將能夠向其他使用者傳送稱讚。

此外,您將實現一些搜尋和過濾功能,以便更輕鬆地在動態中查詢稱讚。

該專案的起點可在 GitHub 倉庫的 part-2 分支中找到。如果您想檢視本部分的最終結果,請訪問 part-3 分支。

開發環境

為了配合所提供的示例,您需要...

注意:可選擴充套件為 Tailwind 和 Prisma 添加了一些非常好的智慧提示和語法高亮。

構建主頁路由

應用程式的主體部分將位於 /home 路由中。透過在 app/routes 資料夾中新增一個 home.tsx 檔案來設定該路由。

這個新檔案暫時應該匯出一個名為 Home 的函式元件,以及一個 loader 函式,如果使用者未登入,該函式會將其重定向到登入介面。

這個 /home 路由將作為應用程式的主頁,而不是基本 URL。

目前,app/routes/index.tsx 檔案(/ 路由)渲染一個 React 元件。該路由應該只重定向使用者:要麼到 /home 路由,要麼到 /login 路由。在其位置設定一個資源路由來實現該功能。

資源路由

資源路由是不渲染元件的路由,但可以響應任何型別的響應。將其視為一個簡單的 API 端點。在您的 / 路由的情況下,您會希望它返回一個帶有 302 狀態碼的 redirect 響應。

刪除現有的 app/routes/index.tsx 檔案,並用一個 index.ts 檔案替換它,您將在其中定義資源路由。

注意:副檔名已更改為 .ts,因為此路由永遠不會渲染元件。

上面的 loader 會首先在使用者訪問 / 路由時檢查他們是否已登入。requireUserId 函式會在沒有有效會話時重定向到 /login

如果存在有效會話,則 loader 返回一個重定向到 /home 頁面的 redirect

新增使用者列表面板

首先,透過構建一個元件來列出網站使用者,將其放置在螢幕左側,從而開始您的主頁。

app/components 資料夾中建立一個名為 user-panel.tsx 的新檔案。

這建立了將包含使用者列表的側面板。然而,該元件是*靜態*的,這意味著它不執行任何操作,也不以任何方式變化。

在透過新增使用者列表使此元件更具*動態性*之前,將其匯入到 app/routes/home.tsx 頁面並將其渲染到頁面上。

上面的程式碼匯入了新元件和 Layout 元件,然後在新元件中渲染了新元件。

查詢所有使用者並排序結果

現在您需要實際在面板中顯示使用者列表。您應該已經有一個檔案用於儲存與使用者相關的函式:app/utils/user.server.ts

向該檔案新增一個新函式,該函式查詢資料庫中的所有使用者。此函式應接受一個 userId 引數,並按使用者的名字按升序排序結果。

where 過濾器排除了任何 iduserId 引數匹配的文件。這將被用於獲取除當前登入使用者之外的所有 user

注意:有沒有注意到按嵌入式文件中的欄位排序是多麼容易?

app/routes/home.tsx 中,匯入該新函式並在 loader 中呼叫它。然後使用 Remix 的 json 助手返回使用者列表。

注意:在 loader 函式中執行的任何程式碼都不會暴露給客戶端程式碼。您應該感謝 Remix 提供了這個很棒的功能!

如果您的資料庫中有任何使用者,並且在載入器中輸出了 users 變數,您應該會看到一個包含所有使用者(除了您自己)的列表。

注意:整個 profile 嵌入式文件作為巢狀物件被檢索,而無需顯式包含它。

您現在將擁有資料。是時候用它做點什麼了!

向用戶面板提供使用者資料

UserPanel 元件中設定一個新的 users 屬性。

此處使用的 User 型別由 Prisma 生成,並透過 Prisma Client 提供。Remix 與 Prisma 配合得非常好,因為它非常容易在全棧框架中實現端到端型別安全。

注意:當整個技術棧中的型別隨著資料形狀的變化而保持同步時,就會實現端到端型別安全。

現在,在 app/routes/home.tsx 中,您可以將使用者提供給 UserPanel 元件。匯入 Remix 提供的 useLoaderData 鉤子,並使用它來訪問 loader 函式返回的任何資料,並用它來訪問 users 資料。

該元件現在將擁有可供使用的 users。現在它需要顯示它們。

構建使用者顯示元件

列表項現在將以一個圓形顯示,其中包含使用者姓名的第一個字母。

app/components 中建立一個名為 user-circle.tsx 的新檔案,並向其中新增以下元件。

該元件使用 Prisma 生成的 Profile 型別,因為您將只傳入 user 文件中的 profile 資料。

它還有一些可配置選項,允許您提供點選操作並新增額外的類來自定義其樣式。

app/components/user-panel.tsx 中,匯入這個新元件,併為每個使用者渲染一個,而不是渲染 <p>Users go here</p>

太棒了!您的使用者現在將以漂亮的列形式呈現在主頁的左側。此時側面板中唯一非功能性的部分是退出按鈕。

新增退出登入功能

app/routes 中新增另一個名為 logout.ts資源路由,該路由在呼叫時將執行退出登入操作。

此路由處理兩種可能的操作:POST 和 GET

  • POST:這將觸發本系列前一部分中編寫的 logout 函式。
  • GET:如果發出 GET 請求,使用者將被髮送到主頁。

app/components/user-panel.ts 中,在您的退出按鈕周圍新增一個 form,在提交時將釋出到此路由。

您的使用者現在可以退出應用程式了!與 POST 請求關聯的會話的使用者將被登出,其會話將被銷燬。

添加發送稱讚功能

當用戶列表中的某個使用者被點選時,會彈出一個模態框,其中包含一個表單。提交此表單會將稱讚儲存到資料庫中。

此表單將具有以下功能:

  • 顯示您正在稱讚哪個使用者。
  • 一個文字區域,您可以在其中填寫給使用者的訊息。
  • 樣式選項,允許您選擇帖子的背景顏色和文字顏色。
  • 一個表情符號選擇器,您可以在帖子中新增表情符號。
  • 一個準確的預覽,顯示您的帖子將是什麼樣子。

更新 Prisma 模式

有一些資料點您將儲存和顯示,但這些資料點尚未在您的模式中定義。以下是需要更改的列表:

  1. 新增一個 Kudo 模型,其中包含一個嵌入式文件來儲存樣式自定義。
  2. User 模型中新增一個 1:n 關係,該關係定義了使用者是其作者的稱讚。同時新增一個類似的關係,定義使用者是其接收者的稱讚。
  3. 為表情符號、部門和顏色新增 enum 以定義可用選項。

注意: 在將 @default 應用到某個欄位後,如果您的集合中的記錄沒有新的必需欄位,則下次讀取時,該記錄將被更新以包含該欄位及其預設值。

目前只需更新這些。執行 npx prisma db push,它將自動重新生成 PrismaClient

巢狀路由

您將使用巢狀路由來建立將包含表單的模態框。這將允許您設定一個子路由,該子路由將在您定義的 Outlet 處渲染到父路由上。

當用戶導航到此巢狀路由時,螢幕上會渲染一個模態框,而無需重新渲染整個頁面。

要建立巢狀路由,首先在 app/routes 中新增一個名為 home 的新資料夾。

注意:該資料夾的命名很重要。因為您有一個 home.tsx 檔案,Remix 會將新 home 資料夾中的任何檔案識別為 /home 的子路由。

在新 app/routes/home 目錄中,建立一個名為 kudo.$userId.tsx 的新檔案。這將允許您像處理獨立路由一樣處理模態元件。

此檔名中的 $userId 部分是一個路由引數,它作為一個動態值,您可以透過 URL 提供給應用程式。Remix 隨後會將該檔名轉換為路由:/home/kudos/$userId,其中 $userId 可以是任何值。

在該新檔案中匯出一個 loader 函式和一個 React 元件,該元件渲染一些文字以確保動態值正常工作。

上面的程式碼做了幾件事:

  1. 它從載入器函式中提取 params 欄位。
  2. 然後它獲取 userId 值。
  3. 最後,它使用 Remix 的 userLoaderData 鉤子從 loader 函式中檢索資料,並將 userId 渲染到螢幕上。

因為這是一個巢狀路由,為了顯示它,您需要定義它應該在其父路由中輸出的位置。

使用 Remix 的 Outlet 元件來指定您希望子路由作為 app/routes/home.tsxLayout 元件的直接子項進行渲染。

如果您前往 https://:3000/home/kudo/123,您現在應該會在頁面頂部看到“User: 123”字樣。如果您將 URL 中的值更改為 123 之外的其他值,您應該會看到螢幕上反映出該更改。

按 ID 獲取使用者

您的巢狀路由正在工作,但您仍然需要使用 userId 檢索使用者資料。在 app/utils/user.server.ts 中建立一個新函式,該函式根據使用者的 id 返回單個使用者。

上面的查詢在資料庫中找到具有給定 id 的唯一記錄。findUnique 函式允許您使用唯一標識欄位或資料庫中該記錄必須唯一的值的欄位來過濾查詢。

接下來

  1. app/routes/home/kudo.$userId.tsx 匯出的載入器中呼叫該函式。
  2. 使用 json 函式返回該載入器的結果。

接下來,您需要一種方法來導航到具有有效 id 的巢狀路由。

app/components/user-panel.tsx 檔案(您正在渲染使用者列表的檔案)中,匯入 Remix 提供的 useNavigation 鉤子,並使用它在單擊使用者時導航到巢狀路由。

現在,當您的使用者在該面板中點選另一個使用者時,他們將被導航到一個包含該使用者資訊的子路由。

如果一切看起來都很好,下一步是構建將顯示錶單的模態元件。

開啟一個門戶

要構建此模態框,您首先需要構建一個幫助元件來建立一個門戶,它允許您在父文件物件模型 (DOM) 分支之外的某個位置渲染子元件,同時仍允許父元件像處理其直接子元件一樣對其進行管理。

注意:這個門戶將很重要,因為它將允許您將模態框渲染到一個不會從父級繼承任何樣式或定位的位置,這可能會影響模態框的定位。

app/components 中建立一個名為 portal.tsx 的新檔案,其內容如下:

以下是該元件中發生的事情的解釋:

  1. 定義了一個函式,用於生成一個帶有 iddiv。然後將該元素附加到文件的 body 中。
  2. 如果帶有提供 id 的元素尚不存在,則呼叫 createWrapper 函式來建立一個。
  3. Portal 元件解除安裝時,這將銷燬該元素。
  4. 建立一個指向新生成的 div 的門戶。

結果是,任何包裹在此 Portal 中的元素或元件都將作為 body 標籤的直接子元素呈現,而不是在當前 DOM 樹中作為其父元素的子元素呈現。

試一試,看看它的實際效果。在 app/routes/home/kudos.$userId.tsx 中,匯入新的 Portal 元件,並用它包裹返回的元件。

如果您導航到巢狀路由,您將看到一個 div,其 id"kudo-modal",現在作為 body 的直接子元素渲染,而不是在 DOM 樹中巢狀路由正在渲染的位置。

構建模態框元件

既然您已經有了一個安全的門戶,那麼就開始構建模態框元件本身吧。這個應用程式中將有兩個模態框,因此以可重用的方式構建元件。

app/components/modal.tsx 建立一個新檔案。這個檔案應該匯出一個元件,包含以下 props

  • children:要在模態框內渲染的元素。
  • isOpen:一個標誌,用於確定模態框是否正在顯示。
  • ariaLabel(可選) 用作 aria 標籤的字串。
  • className(可選) 允許您向模態框內容新增額外類的字串。

新增以下程式碼來建立 Modal 元件:

匯入 Portal 元件並將其包裹在整個模態框外部,以確保其渲染位置安全。

然後,使用各種 TailwindCSS 輔助類將模態框定義為螢幕上的固定元素,並帶有一個不透明的背景。

當背景(模態框本身之外的任何地方)被點選時,使用者將被導航到 /home 路由,導致模態框關閉。

構建表單

app/routes/home/kudo.$userId.tsx 中,匯入新的 Modal 元件,並渲染一個 Modal 而不是當前正在渲染的 Portal

現在,當單擊側面板中的使用者時,模態框應該會彈出。

您的表單在顯示訊息預覽時將需要登入使用者的資訊,因此在構建表單之前,將該資料新增到 loader 函式的響應中。

然後對該檔案中的 KudoModal 函式進行以下更改:

這是一大段新程式碼,讓我們看看做了哪些更改:

  1. 匯入您需要的幾個元件和鉤子。
  2. 設定您需要處理表單資料和錯誤的各種表單變數。
  3. 建立將處理輸入更改的函式。
  4. 在原來是 <h2> 標籤的位置渲染表單元件的基本佈局。

允許使用者自定義他們的稱讚

此表單還需要允許使用者使用選擇框選擇自定義樣式。

app/components 中建立一個名為 select-box.tsx 的新檔案,該檔案匯出一個 SelectBox 元件。

此元件類似於 FormField 元件,它是一個受控元件,接受一些配置並允許其父級管理其狀態。

這些選擇框需要填充顏色和表情符號選項。建立一個輔助檔案來儲存 app/utils/constants.ts 中的可能選項。

現在,在 app/routes/home/kudo.$userId.tsx 中,匯入 SelectBox 元件和常量。同時新增所需的變數和函式,將它們與表單狀態關聯起來,並用 SelectBox 元件替換 {/* Select Boxes Go Here */} 註釋。

選擇框現在將顯示所有可能的選項。

新增稱讚顯示元件

此表單將有一個預覽部分,使用者可以在其中看到收件人將看到的元件的實際渲染效果。

app/components 中建立一個名為 kudo.tsx 的新檔案。

此元件接受以下屬性:

  • profile:來自收件人 user 文件的 profile 資料。
  • kudoKudo 的資料和樣式選項。

匯入了包含顏色和表情符號選項的常量,並用於渲染自定義樣式。

您現在可以將此元件匯入到 app/routes/home/kudo.$userId.tsx 中,並將其渲染到 {/* The Preview Goes Here */} 註釋所在的位置。

預覽現在將呈現,顯示當前登入使用者的資訊以及他們將傳送的樣式化的訊息。

構建傳送稱讚的操作

表單在視覺上已經完成,剩下的唯一部分就是使其具有功能性!

app/utils 中建立一個名為 kudos.server.ts 的新檔案,您將在其中編寫與查詢或儲存稱讚相關的任何函式。

在此檔案中,匯出一個 createKudo 方法,該方法接受稱讚表單資料、作者的 id 和接收者的 id。然後使用 Prisma 儲存該資料。

上面的查詢執行以下操作:

  1. 傳入 message 字串和 style 嵌入式文件。
  2. 使用傳遞給函式的 ID 將新的稱讚連線到相應的作者接收者

將此新函式匯入到 app/routes/home/kudo.$userId.tsx 檔案中,並建立一個 action 函式來處理表單資料和 createKudo 函式的呼叫。

以下是上述程式碼片段的概述:

  1. 匯入新的 createKudo 函式,以及 Prisma 生成的一些型別、Remix 的 ActionFunction 型別,以及您之前編寫的 requireUserId 函式。
  2. 從請求中提取您需要的所有表單資料和欄位。
  3. 驗證所有表單資料,如果出現問題,將適當的錯誤發回表單以供顯示。
  4. 使用 createKudo 函式建立新的 kudo
  5. 將使用者重定向到 /home 路由,導致模態框關閉。

構建稱讚動態

現在您的使用者可以互相傳送稱讚了,您需要一種方式來在 /home 頁面的使用者動態中顯示這些稱讚。

您已經構建了稱讚顯示元件,因此您只需在主頁上檢索並渲染稱讚列表。

app/utils/kudos.server.ts 中建立並匯出一個名為 getFilteredKudos 的新函式。

上述函式接受幾個不同的引數。它們是:

  • userId:查詢應檢索其稱讚的使用者的 id
  • sortFilter:一個物件,將傳遞到查詢中的 orderBy 選項以排序結果。
  • whereFilter:一個物件,將傳遞到查詢中的 where 選項以過濾結果。

注意:Prisma 會生成可用於安全地對查詢片段進行型別定義的型別,例如上面使用的 Prisma.KudoWhereInput

現在在 app/routes/home.tsx 中,匯入該函式並在 loader 函式中呼叫它。同時匯入 Kudo 元件和渲染稱讚動態所需的型別。

Prisma 生成的 KudoProfile 型別組合在一起建立了一個 KudoWithProfile 型別。這是因為您的陣列中包含帶有作者資料資料的稱讚。

如果您向某個帳戶傳送了幾個稱讚並登入該帳戶,您現在應該會在您的動態中看到已渲染的稱讚列表。

您可能會注意到 getFilteredKudos 呼叫為排序和過濾選項提供了空物件。這是因為使用者介面中還沒有過濾或排序動態的方法。接下來,您將在動態頂部建立搜尋欄來處理這個問題。

app/components 中建立一個名為 search-bar.tsx 的新檔案。此元件將向 /home 頁面提交一個表單,並傳遞將用於構建您需要的排序和篩選物件的查詢引數。

在上面的程式碼中,添加了 inputbutton 來處理文字過濾和搜尋引數的提交。

當 URL 中存在 filter 變數時,按鈕將變為“清除過濾器”按鈕,而不是“搜尋”按鈕。

將該檔案匯入到 app/routes/home.tsx 並將其渲染到 {/* Search Bar Goes Here */} 註釋的位置。

這些更改將處理動態的過濾,但是您還希望按各種列對動態進行排序。

app/utils/constants.ts 中新增一個 sortOptions 常量來定義可用的列。

現在將該常量和 SelectBox 元件匯入到 app/components/search-bar.tsx 檔案中,並在 button 元素之前渲染帶有這些選項的 SelectBox

現在您應該會在搜尋欄中看到一個包含您選項的下拉選單。

構建搜尋欄操作

當搜尋表單提交時,將向 /home 發出 GET 請求,並在 URL 中傳遞過濾和排序資料。在 app/routes/home.tsx 匯出的 loader 函式中,從 URL 中提取 sortfilter 資料,並使用結果構建查詢。

上面的程式碼

  1. 提取 URL 引數。
  2. 構建一個 sortOptions 物件,以傳遞到您的 Prisma 查詢中,該物件可能根據 URL 中傳遞的資料而異。
  3. 構建一個 textFilter 物件,以傳遞到您的 Prisma 查詢中,該物件可能根據 URL 中傳遞的資料而異。
  4. 更新 getFilteredKudos 呼叫以包含新的過濾器。

現在,如果您提交表單,您應該會在動態中看到您的結果!

顯示最新的稱讚

您的動態所需的最後一件事是顯示最新發送的稱讚。此元件將顯示三個最新稱讚接收者的 UserCircle 元件。

app/components 中建立一個名為 recent-bar.tsx 的新檔案,其中包含以下程式碼:

此元件接受最近三個稱讚的列表,並將其渲染到面板中。

現在您需要編寫一個查詢來獲取該資料。在 app/utils/kudos.server.ts 中新增一個名為 getRecentKudos 的函式,該函式返回以下查詢:

此查詢

  1. createdAt 降序排序結果,以獲取從最新到最舊的記錄。
  2. 僅從該列表中獲取前三個,以獲取三個最新文件。

現在你需要:

  • RecentBar 元件和 getRecentKudos 函式匯入到 app/routes/home.tsx 檔案中。
  • 在該檔案的 loader 函式中呼叫 getRecentKudos
  • RecentBar 渲染到主頁,替換掉 {/* Recent Kudos Goes Here */} 註釋。

至此,您的主頁已完成,您應該會在應用程式中看到最近傳送的三個稱讚的列表!

總結與展望

在本文中,您構建了此應用程式的主要功能,並在此過程中學到了許多概念,包括:

  • Remix 中的重定向
  • 使用資源路由
  • 使用 Prisma Client 過濾和排序資料
  • 在 Prisma Schema 中使用嵌入式文件
  • ... 還有更多!

在本系列的下一節中,您將透過構建站點的個人資料設定部分和建立影像上傳元件來管理個人資料圖片來完成此應用程式。

不要錯過下一篇文章!

訂閱 Prisma 電子郵件簡報

© . This site is unofficial and not affiliated with Prisma Data, Inc.