Der Autor wählte die Electronic Frontier Foundation aus, um im Rahmen des Programms Write for Donations eine Spende zu erhalten.
Einführung
Ruby on Rails ist ein beliebtes serverseitiges Webanwendungsframework. Es treibt viele populäre Anwendungen an, die heute im Web existieren, wie GitHub, Basecamp, SoundCloud, Airbnb und Twitch. Mit seinem Schwerpunkt auf der Erfahrung des Programmierers und der leidenschaftlichen Community, die sich darum gebildet hat, bietet Ruby on Rails die Werkzeuge, die Sie benötigen, um Ihre moderne Webanwendung zu erstellen und zu pflegen.
React ist eine JavaScript-Bibliothek, die zur Erstellung von Benutzeroberflächen für Front-Ends verwendet wird. Unterstützt von Facebook ist sie eine der beliebtesten Front-End-Bibliotheken, die heute im Web verwendet wird. React bietet Funktionen wie ein virtuelles Document Object Model (DOM), eine Komponentenarchitektur und Statusverwaltung, die den Prozess der Front-End-Entwicklung organisierter und effizienter machen.
Mit dem Frontend des Webs, das sich von serverseitigem Code entkoppelt und auf Frameworks setzt, die die Eleganz von Rails mit der Effizienz von React kombinieren, können Sie leistungsstarke und moderne Anwendungen entwickeln, die von aktuellen Trends beeinflusst sind. Durch die Verwendung von React zum Rendern von Komponenten innerhalb einer Rails-Ansicht (anstatt des Rails-Template-Engines) profitiert Ihre Anwendung von den neuesten Entwicklungen in JavaScript und Frontend-Entwicklung und nutzt gleichzeitig die Ausdrucksstärke von Ruby on Rails.
In diesem Tutorial erstellen Sie eine Ruby on Rails-Anwendung, die Ihre Lieblingsrezepte speichert und sie dann mit einem React-Frontend anzeigt. Nach Abschluss des Tutorials können Sie Rezepte mit einer React-Schnittstelle erstellen, anzeigen und löschen, die mit Bootstrap gestylt ist:
Voraussetzungen
Um diesem Tutorial zu folgen, benötigen Sie:
-
Node.js und npm sind auf Ihrem Entwicklungssystem installiert. Dieses Tutorial verwendet Node.js Version 16.14.0 und npm Version 8.3.1. Node.js ist eine JavaScript-Laufzeitumgebung, die es Ihnen ermöglicht, Ihren Code außerhalb des Browsers auszuführen. Es wird mit einem vorinstallierten Paketmanager namens npm geliefert, der es Ihnen ermöglicht, Pakete zu installieren und zu aktualisieren. Um diese auf Ubuntu 20.04 oder macOS zu installieren, folgen Sie dem Abschnitt „Installation mit einem PPA“ von So installieren Sie Node.js unter Ubuntu 20.04 oder den Schritten in So installieren Sie Node.js und erstellen eine lokale Entwicklungsumgebung unter macOS.
-
Der Paketmanager Yarn ist auf Ihrer Entwicklungsplattform installiert und ermöglicht Ihnen das Herunterladen des React-Frameworks. Dieses Tutorial wurde mit Version 1.22.10 getestet. Um diese Abhängigkeit zu installieren, folgen Sie der offiziellen Yarn-Installationsanleitung.
-
Ruby on Rails installiert. Um dies zu erhalten, folgen Sie unserer Anleitung unter So installieren Sie Ruby on Rails mit rbenv unter Ubuntu 20.04. Wenn Sie diese Anwendung auf macOS entwickeln möchten, können Sie So installieren Sie Ruby on Rails mit rbenv unter macOS verwenden. Dieses Tutorial wurde mit Version 3.1.2 von Ruby und Version 7.0.4 von Rails getestet, stellen Sie also sicher, dass Sie diese Versionen während des Installationsprozesses angeben.
Hinweis: Rails Version 7 ist nicht abwärtskompatibel. Wenn Sie Rails Version 5 verwenden, besuchen Sie bitte das Tutorial für So richten Sie ein Ruby on Rails v5-Projekt mit einem React-Frontend unter Ubuntu 18.04 ein.
- PostgreSQL installiert, wie in Schritten 1 und 2 beschrieben, wie man PostgreSQL mit Ihrer Ruby on Rails-Anwendung auf Ubuntu 20.04 verwendet oder wie man PostgreSQL mit Ihrer Ruby on Rails-Anwendung auf macOS verwendet. Um diesem Tutorial zu folgen, können Sie PostgreSQL Version 12 oder höher verwenden. Wenn Sie diese Anwendung auf einer anderen Linux-Distribution oder einem anderen Betriebssystem entwickeln möchten, sehen Sie auf der offiziellen PostgreSQL-Downloadseite nach. Für weitere Informationen zur Verwendung von PostgreSQL siehe Wie man PostgreSQL installiert und verwendet.
Schritt 1 — Erstellen einer neuen Rails-Anwendung
In diesem Schritt werden Sie Ihre Rezeptanwendung auf dem Rails-Anwendungsframework aufbauen. Zuerst werden Sie eine neue Rails-Anwendung erstellen, die so eingerichtet wird, dass sie mit React arbeitet.
Rails bietet mehrere Skripte, genannt Generatoren, die alles Notwendige erstellen, um eine moderne Webanwendung zu bauen. Um eine vollständige Liste dieser Befehle und ihrer Funktionen zu überprüfen, führen Sie den folgenden Befehl in Ihrem Terminal aus:
- rails -h
Diese Anweisung liefert eine umfassende Liste von Optionen, die es Ihnen ermöglichen, die Parameter Ihrer Anwendung festzulegen. Eine der aufgeführten Befehle ist der new
-Befehl, der eine neue Rails-Anwendung erstellt.
Jetzt erstellen Sie eine neue Rails-Anwendung mit dem new
-Generator. Führen Sie den folgenden Befehl in Ihrem Terminal aus:
- rails new rails_react_recipe -d postgresql -j esbuild -c bootstrap -T
Der vorherige Befehl erstellt eine neue Rails-Anwendung in einem Verzeichnis namens rails_react_recipe
, installiert die erforderlichen Ruby- und JavaScript-Abhängigkeiten und konfiguriert Webpack. Die mit diesem new
-Generator-Befehl verbundenen Flags umfassen folgende:
- Die
-d
-Flag gibt den bevorzugten Datenbank-Motor an, der in diesem Fall PostgreSQL ist. - Die
-j
-Flag gibt den Ansatz für die JavaScript-Implementierung der Anwendung an. Rails bietet verschiedene Möglichkeiten, Javascript-Code in Rails-Anwendungen zu behandeln. Die Optionesbuild
, die an die-j
-Flag übergeben wird, weist Rails an, esbuild als bevorzugten JavaScript-Bundler vorzukonfigurieren. - Die
-c
-Flag gibt den CSS-Prozessor der Anwendung an. Bootstrap ist in diesem Fall die bevorzugte Option. - Die
-T
-Flag weist Rails an, die Generierung von Testdateien zu überspringen, da Sie für dieses Tutorial keine Tests schreiben werden. Dieser Befehl wird auch vorgeschlagen, wenn Sie ein anderes Ruby-Testwerkzeug als das von Rails bereitgestellte verwenden möchten.
Nachdem der Befehl abgeschlossen ist, wechseln Sie zum Verzeichnis rails_react_recipe
, das das Stammverzeichnis Ihrer App ist:
- cd rails_react_recipe
Anschließend listen Sie den Inhalt des Verzeichnisses auf:
- ls
OutputGemfile README.md bin db node_modules storage yarn.lock
Gemfile.lock Rakefile config lib package.json tmp
Procfile.dev app config.ru log public vendor
Dieses Stammverzeichnis enthält mehrere automatisch generierte Dateien und Ordner, die die Struktur einer Rails-Anwendung ausmachen, einschließlich einer package.json
-Datei mit Abhängigkeiten für eine React-Anwendung.
Jetzt, da Sie erfolgreich eine neue Rails-Anwendung erstellt haben, werden Sie sie im nächsten Schritt an eine Datenbank anschließen.
Schritt 2 — Einrichten der Datenbank
Bevor Sie Ihre neue Rails-Anwendung ausführen, müssen Sie sie zuerst mit einer Datenbank verbinden. In diesem Schritt verbinden Sie die neu erstellte Rails-Anwendung mit einer PostgreSQL-Datenbank, damit Rezeptdaten gespeichert und bei Bedarf abgerufen werden können.
In der Datei database.yml
unter config/database.yml
befinden sich Details zur Datenbank wie Datenbanknamen für verschiedene Entwicklungsumgebungen. Rails gibt einen Datenbanknamen für die verschiedenen Entwicklungsumgebungen an, indem es einen Unterstrich (_
) gefolgt vom Umgebungsnamen anfügt. In diesem Tutorial verwenden Sie die Standardwerte für die Datenbankkonfiguration, aber Sie können Ihre Konfigurationswerte bei Bedarf ändern.
config/database.yml
ändern, um festzulegen, welches PostgreSQL-Benutzerkonto Rails verwenden soll, um Ihre Datenbank zu erstellen. Während der Voraussetzungen haben Sie ein Benutzerkonto erstellt, das durch ein Passwort gesichert ist, wie im Tutorial So verwenden Sie PostgreSQL mit Ihrer Ruby on Rails-Anwendung beschrieben. Wenn Sie den Benutzer noch nicht festgelegt haben, können Sie nun den Anweisungen für Schritt 4 – Konfigurieren und Erstellen Ihrer Datenbank im gleichen Voraussetzungs-Tutorial folgen.
Rails bietet viele Befehle, die die Entwicklung von Webanwendungen erleichtern, einschließlich Befehlen zur Arbeit mit Datenbanken wie create
, drop
und reset
. Um eine Datenbank für Ihre Anwendung zu erstellen, führen Sie den folgenden Befehl in Ihrem Terminal aus:
- rails db:create
Dieser Befehl erstellt eine development
und test
Datenbank und gibt die folgende Ausgabe aus:
OutputCreated database 'rails_react_recipe_development'
Created database 'rails_react_recipe_test'
Da die Anwendung mit einer Datenbank verbunden ist, starten Sie die Anwendung, indem Sie den folgenden Befehl ausführen:
- bin/dev
Rails bietet ein alternatives bin/dev
Skript, das eine Rails-Anwendung startet, indem es die Befehle in der Datei Procfile.dev
im Stammverzeichnis der App mit dem Foreman Gem ausführt.
Nachdem Sie diesen Befehl ausgeführt haben, wird Ihre Eingabeaufforderung verschwinden, und an ihrer Stelle wird die folgende Ausgabe gedruckt:
Outputstarted with pid 70099
started with pid 70100
started with pid 70101
yarn run v1.22.10
yarn run v1.22.10
$ esbuild app/javascript/*.* --bundle --sourcemap --outdir=app/assets/builds --public-path=assets --watch
$ sass ./app/assets/stylesheets/application.bootstrap.scss:./app/assets/builds/application.css --no-source-map --load-path=node_modules --watch
=> Booting Puma
=> Rails 7.0.4 application starting in development
=> Run `bin/rails server --help` for more startup options
[watch] build finished, watching for changes...
Puma starting in single mode...
* Puma version: 5.6.5 (ruby 3.1.2-p20) ("Birdie's Version")
* Min threads: 5
* Max threads: 5
* Environment: development
* PID: 70099
* Listening on http://127.0.0.1:3000
* Listening on http://[::1]:3000
Use Ctrl-C to stop
Sass is watching for changes. Press Ctrl-C to stop.
Um auf Ihre Anwendung zuzugreifen, öffnen Sie ein Browserfenster und navigieren Sie zu http://localhost:3000
. Die Standard-Willkommensseite von Rails wird geladen, was bedeutet, dass Sie Ihre Rails-Anwendung korrekt eingerichtet haben:
Um den Webserver zu stoppen, drücken Sie CTRL+C
im Terminal, in dem der Server läuft. Sie erhalten eine Abschiedsnachricht von Puma:
Output^C SIGINT received, starting shutdown
- Gracefully stopping, waiting for requests to finish
=== puma shutdown: 2019-07-31 14:21:24 -0400 ===
- Goodbye!
Exiting
sending SIGTERM to all processes
terminated by SIGINT
terminated by SIGINT
exited with code 0
Ihre Terminal-Eingabeaufforderung wird dann wieder erscheinen.
Sie haben erfolgreich eine Datenbank für Ihre Rezeptanwendung eingerichtet. Im nächsten Schritt installieren Sie die JavaScript-Abhängigkeiten, die Sie benötigen, um Ihr React-Frontend zusammenzustellen.
Schritt 3 — Installation von Frontend-Abhängigkeiten
In diesem Schritt installieren Sie die JavaScript-Abhängigkeiten, die auf der Frontend-Seite Ihrer Rezeptanwendung benötigt werden. Dazu gehören:
- React zum Erstellen von Benutzeroberflächen.
- React DOM zur Interaktion von React mit dem Browser-DOM.
- React Router zur Behandlung der Navigation in einer React-Anwendung.
Führen Sie den folgenden Befehl aus, um diese Pakete mit dem Yarn-Paketmanager zu installieren:
- yarn add react react-dom react-router-dom
Dieser Befehl verwendet Yarn, um die angegebenen Pakete zu installieren und fügt sie zur Datei „package.json“ hinzu. Um dies zu überprüfen, öffnen Sie die „package.json“-Datei im Stammverzeichnis des Projekts:
- nano package.json
Die installierten Pakete werden unter dem Schlüssel „dependencies“ aufgelistet:
{
"name": "app",
"private": "true",
"dependencies": {
"@hotwired/stimulus": "^3.1.0",
"@hotwired/turbo-rails": "^7.1.3",
"@popperjs/core": "^2.11.6",
"bootstrap": "^5.2.1",
"bootstrap-icons": "^1.9.1",
"esbuild": "^0.15.7",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.3.0",
"sass": "^1.54.9"
},
"scripts": {
"build": "esbuild app/javascript/*.* --bundle --sourcemap --outdir=app/assets/builds --public-path=assets",
"build:css": "sass ./app/assets/stylesheets/application.bootstrap.scss:./app/assets/builds/application.css --no-source-map --load-path=node_modules"
}
}
Schließen Sie die Datei, indem Sie „STRG+X“ drücken.
Sie haben einige Front-End-Abhängigkeiten für Ihre Anwendung installiert. Als Nächstes richten Sie eine Homepage für Ihre Rezeptanwendung ein.
Schritt 4 – Einrichten der Homepage
Mit den erforderlichen Abhängigkeiten installiert, werden Sie nun eine Homepage für die Anwendung erstellen, die als Startseite dient, wenn Benutzer die Anwendung zum ersten Mal besuchen.
Rails folgt dem architektonischen Muster „Model-View-Controller“ für Anwendungen. Im MVC-Muster dient ein Controller dazu, spezifische Anfragen entgegenzunehmen und sie an das entsprechende Modell oder die entsprechende Ansicht weiterzuleiten. Die Anwendung zeigt derzeit die Rails-Begrüßungsseite an, wenn die Root-URL im Browser geladen wird. Um dies zu ändern, werden Sie einen Controller und eine Ansicht für die Homepage erstellen und dann mit einer Route verknüpfen.
Rails bietet einen controller
-Generator zum Erstellen eines Controllers. Der controller
-Generator erhält einen Controller-Namen und eine passende Aktion. Weitere Informationen dazu finden Sie in der Rails-Dokumentation.
In diesem Tutorial wird der Controller Homepage
genannt. Führen Sie den folgenden Befehl aus, um einen Homepage
-Controller mit einer index
-Aktion zu erstellen:
- rails g controller Homepage index
Hinweis:
Auf Linux kann der Fehler FATAL: Listen error: unable to monitor directories for changes.
aufgrund einer Systembegrenzung für die Anzahl der Dateien auftreten, die Ihr Rechner überwachen kann. Führen Sie den folgenden Befehl aus, um dies zu beheben:
- echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p
Dieser Befehl erhöht die Anzahl der Verzeichnisse, die Sie mit Listen
überwachen können, dauerhaft auf 524288
. Sie können dies erneut ändern, indem Sie denselben Befehl ausführen und 524288
durch Ihre gewünschte Zahl ersetzen.
Das Ausführen des controller
-Befehls generiert die folgenden Dateien:
- A
homepage_controller.rb
file for receiving all homepage-related requests. This file contains theindex
action you specified in the command. - A
homepage_helper.rb
file for adding helper methods related to theHomepage
controller. - Eine
index.html.erb
-Datei als Ansichtsseite für die Darstellung von allem, was die Homepage betrifft.
Neben diesen neuen Seiten, die durch den Rails-Befehl erstellt wurden, aktualisiert Rails auch Ihre Routendatei unter config/routes.rb
und fügt eine get
-Route für Ihre Homepage hinzu, die Sie als Root-Route ändern werden.
A root route in Rails specifies what will show up when users visit the root URL of your application. In this case, you want your users to see your homepage. Open the routes file located at config/routes.rb
in your favorite editor:
- nano config/routes.rb
Ersetzen Sie in dieser Datei get 'homepage/index'
durch root 'homepage#index'
, damit die Datei wie folgt übereinstimmt:
Rails.application.routes.draw do
root 'homepage#index'
# Für Details zur DSL, die in dieser Datei verfügbar ist, siehe http://guides.rubyonrails.org/routing.html
end
Diese Änderung weist Rails an, Anfragen an die Wurzel der Anwendung auf die Aktion index
des Controllers Homepage
abzubilden, der wiederum im Browser das rendert, was in der Datei index.html.erb
unter app/views/homepage/index.html.erb
liegt.
Datei speichern und schließen.
Um zu überprüfen, ob dies funktioniert, starten Sie Ihre Anwendung:
- bin/dev
Wenn Sie die Anwendung im Browser öffnen oder aktualisieren, wird eine neue Startseite für Ihre Anwendung geladen:
Nachdem Sie überprüft haben, dass Ihre Anwendung funktioniert, drücken Sie STRG+C
, um den Server zu stoppen.
Öffnen Sie als nächstes die Datei ~/rails_react_recipe/app/views/homepage/index.html.erb
:
- nano ~/rails_react_recipe/app/views/homepage/index.html.erb
Entfernen Sie den Code in der Datei und speichern Sie die Datei als leer. Dadurch stellen Sie sicher, dass der Inhalt von index.html.erb
nicht mit dem React-Rendering Ihres Frontends interferiert.
Jetzt, da Sie Ihre Startseite für Ihre Anwendung eingerichtet haben, können Sie zum nächsten Abschnitt übergehen, in dem Sie das Frontend Ihrer Anwendung so konfigurieren, dass es React verwendet.
Schritt 5 — Konfigurieren von React als Ihr Rails-Frontend
In diesem Schritt konfigurieren Sie Rails so, dass React auf der Frontend-Anwendung verwendet wird, anstelle des Template-Engines. Diese neue Konfiguration ermöglicht es Ihnen, eine optisch ansprechendere Homepage mit React zu erstellen.
Mit Hilfe der `esbuild`-Option, die beim Generieren der Rails-Anwendung angegeben wurde, ist bereits der Großteil der Einrichtung, die erforderlich ist, um JavaScript nahtlos mit Rails zusammenarbeiten zu lassen, vorhanden. Alles, was noch übrig bleibt, ist, den Einstiegspunkt der React-Anwendung in den `esbuild`-Einstiegspunkt für JavaScript-Dateien zu laden. Beginnen Sie damit, ein Verzeichnis `components` im Verzeichnis `app/javascript` zu erstellen:
- mkdir ~/rails_react_recipe/app/javascript/components
Das Verzeichnis `components` wird das Komponente für die Homepage beherbergen, zusammen mit anderen React-Komponenten in der Anwendung, einschließlich der Einstiegsdatei in die React-Anwendung.
Öffnen Sie als nächstes die Datei `application.js`, die sich unter `app/javascript/application.js` befindet:
- nano ~/rails_react_recipe/app/javascript/application.js
Fügen Sie die hervorgehobene Codezeile zur Datei hinzu:
// Einstiegspunkt für das Build-Skript in Ihrer package.json
import "@hotwired/turbo-rails"
import "./controllers"
import * as bootstrap from "bootstrap"
import "./components"
Die hinzugefügte Codezeile zur Datei `application.js` importiert den Code in die Einstiegsdatei `index.jsx`, wodurch er für `esbuild` zum Bündeln verfügbar wird. Mit dem Import des Verzeichnisses `/components` in den JavaScript-Einstiegspunkt der Rails-App können Sie eine React-Komponente für Ihre Homepage erstellen. Die Homepage wird einige Texte und eine Handlungsaufforderungsschaltfläche enthalten, um alle Rezepte anzuzeigen.
Speichern und schließen Sie die Datei.
Erstellen Sie dann eine Datei `Home.jsx` im Verzeichnis `components`:
- nano ~/rails_react_recipe/app/javascript/components/Home.jsx
Fügen Sie den folgenden Code zur Datei hinzu:
import React from "react";
import { Link } from "react-router-dom";
export default () => (
<div className="vw-100 vh-100 primary-color d-flex align-items-center justify-content-center">
<div className="jumbotron jumbotron-fluid bg-transparent">
<div className="container secondary-color">
<h1 className="display-4">Food Recipes</h1>
<p className="lead">
A curated list of recipes for the best homemade meal and delicacies.
</p>
<hr className="my-4" />
<Link
to="/recipes"
className="btn btn-lg custom-button"
role="button"
>
View Recipes
</Link>
</div>
</div>
</div>
);
In diesem Code importieren Sie React und das Link
-Komponente von React Router. Die Link
-Komponente erstellt einen Hyperlink, um von einer Seite zur anderen zu navigieren. Dann erstellen und exportieren Sie eine funktionale Komponente, die einige Markup-Sprache für Ihre Homepage enthält, die mit Bootstrap-Klassen gestylt ist.
Speichern und schließen Sie die Datei.
Mit Ihrer Home
-Komponente eingerichtet, richten Sie jetzt Routing mit React Router ein. Erstellen Sie ein routes
-Verzeichnis im app/javascript
-Verzeichnis:
- mkdir ~/rails_react_recipe/app/javascript/routes
Das routes
-Verzeichnis wird einige Routen mit ihren entsprechenden Komponenten enthalten. Immer wenn eine bestimmte Route geladen wird, wird sie ihre entsprechende Komponente im Browser rendern.
Im routes
-Verzeichnis erstellen Sie eine index.jsx
-Datei:
- nano ~/rails_react_recipe/app/javascript/routes/index.jsx
Fügen Sie den folgenden Code hinzu:
import React from "react";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import Home from "../components/Home";
export default (
<Router>
<Routes>
<Route path="/" element={<Home />} />
</Routes>
</Router>
);
In dieser index.jsx
-Routendatei importieren Sie die folgenden Module: das React
-Modul, das es Ihnen ermöglicht, React zu verwenden, sowie die BrowserRouter
, Routes
und Route
-Module von React Router, die Ihnen zusammen helfen, von einer Route zur anderen zu navigieren. Zuletzt importieren Sie Ihre Home
-Komponente, die gerendert wird, wenn eine Anfrage der Wurzelroute (/
) entspricht. Wenn Sie weitere Seiten zu Ihrer Anwendung hinzufügen möchten, können Sie in dieser Datei eine Route deklarieren und mit der Komponente abgleichen, die Sie für diese Seite rendern möchten.
Speichern und beenden Sie die Datei.
Du hast jetzt die Routen mit React Router eingerichtet. Damit React von den verfügbaren Routen weiß und sie verwenden kann, müssen die Routen am Einstiegspunkt der Anwendung verfügbar sein. Um dies zu erreichen, wirst du deine Routen in einem Komponenten rendern, den React in deiner Eingabedatei rendern wird.
Erstelle eine App.jsx
-Datei im Verzeichnis app/javascript/components
:
- nano ~/rails_react_recipe/app/javascript/components/App.jsx
Füge den folgenden Code in die App.jsx
-Datei ein:
import React from "react";
import Routes from "../routes";
export default props => <>{Routes}</>;
In der App.jsx
-Datei importierst du React und die Routendateien, die du gerade erstellt hast. Du exportierst dann eine Komponente, um die Routen innerhalb von Fragmenten zu rendern. Diese Komponente wird am Einstiegspunkt der Anwendung gerendert, wodurch die Routen immer verfügbar sind, wenn die Anwendung geladen wird.
Speichere und schließe die Datei.
Jetzt, da du deine App.jsx
-Datei eingerichtet hast, kannst du sie in deiner Eingabedatei rendern. Erstelle eine index.jsx
-Datei im Verzeichnis components
:
- nano ~/rails_react_recipe/app/javascript/components/index.jsx
Füge den folgenden Code in die index.js
-Datei ein:
import React from "react";
import { createRoot } from "react-dom/client";
import App from "./App";
document.addEventListener("turbo:load", () => {
const root = createRoot(
document.body.appendChild(document.createElement("div"))
);
root.render(<App />);
});
In den import
-Zeilen importierst du die React-Bibliothek, die createRoot
-Funktion von ReactDOM und deine App
-Komponente. Mit der createRoot
-Funktion von ReactDOM erstellst du ein Wurzelelement als div
-Element, das der Seite hinzugefügt wird, und du renderst deine App
-Komponente darin. Wenn die Anwendung geladen wird, wird React den Inhalt der App
-Komponente innerhalb des div
-Elements auf der Seite rendern.
Speichere und schließe die Datei.
Zuletzt fügst du einige CSS-Stile zu deiner Startseite hinzu.
Öffnen Sie die Datei application.bootstrap.scss
in Ihrem Verzeichnis ~/rails_react_recipe/app/assets/stylesheets/application.bootstrap.scss
:
- nano ~/rails_react_recipe/app/assets/stylesheets/application.bootstrap.scss
Als nächstes ersetzen Sie den Inhalt der Datei application.bootstrap.scss
durch den folgenden Code:
@import 'bootstrap/scss/bootstrap';
@import 'bootstrap-icons/font/bootstrap-icons';
.bg_primary-color {
background-color: #FFFFFF;
}
.primary-color {
background-color: #FFFFFF;
}
.bg_secondary-color {
background-color: #293241;
}
.secondary-color {
color: #293241;
}
.custom-button.btn {
background-color: #293241;
color: #FFF;
border: none;
}
.hero {
width: 100vw;
height: 50vh;
}
.hero img {
object-fit: cover;
object-position: top;
height: 100%;
width: 100%;
}
.overlay {
height: 100%;
width: 100%;
opacity: 0.4;
}
Sie setzen einige benutzerdefinierte Farben für die Seite. Der Abschnitt .hero
wird das Framework für ein Heldenbild oder ein großes Webbanner auf der Startseite Ihrer Website erstellen, das Sie später hinzufügen werden. Zusätzlich wird der Stil custom-button.btn
den Button formatieren, den der Benutzer verwenden wird, um die Anwendung zu betreten.
Mit Ihren CSS-Stilen an Ort und Stelle, speichern Sie die Datei und verlassen Sie sie.
Starten Sie dann den Webserver für Ihre Anwendung neu:
- bin/dev
Laden Sie anschließend die Anwendung in Ihrem Browser neu. Eine brandneue Startseite wird geladen:
Stoppen Sie den Webserver mit CTRL+C
.
Sie haben Ihre Anwendung so konfiguriert, dass sie React als Frontend verwendet. Im nächsten Schritt werden Sie Modelle und Controller erstellen, die es Ihnen ermöglichen, Rezepte zu erstellen, zu lesen, zu aktualisieren und zu löschen.
Schritt 6 — Erstellen des Rezeptcontrollers und -modells
Nun, da Sie eine React-Oberfläche für Ihre Anwendung eingerichtet haben, werden Sie ein Rezeptmodell und einen Controller erstellen. Das Rezeptmodell repräsentiert die Datenbanktabelle mit Informationen zu den Rezepten des Benutzers, während der Controller Anfragen zum Erstellen, Lesen, Aktualisieren oder Löschen von Rezepten empfängt und verarbeitet. Wenn ein Benutzer ein Rezept anfordert, nimmt der Rezeptcontroller diese Anfrage entgegen und leitet sie an das Rezeptmodell weiter, das die angeforderten Daten aus der Datenbank abruft. Das Modell gibt dann die Rezeptdaten als Antwort an den Controller zurück. Schließlich werden diese Informationen im Browser angezeigt.
Beginnen Sie damit, ein Rezept
-Modell zu erstellen, indem Sie den von Rails bereitgestellten Befehl generate model
verwenden und den Namen des Modells sowie seine Spalten und Datentypen angeben. Führen Sie den folgenden Befehl aus:
- rails generate model Recipe name:string ingredients:text instruction:text image:string
Der vorherige Befehl weist Rails an, ein Rezept
-Modell zusammen mit einer name
-Spalte vom Typ string
, einer ingredients
– und instruction
-Spalte vom Typ text
sowie einer image
-Spalte vom Typ string
zu erstellen. In diesem Tutorial wurde das Modell als Rezept
benannt, da Models in Rails einen Singularnamen verwenden, während ihre entsprechenden Datenbanktabellen einen Pluralnamen verwenden.
Die Ausführung des Befehls generate model
erstellt zwei Dateien und gibt die folgende Ausgabe aus:
Output invoke active_record
create db/migrate/20221017220817_create_recipes.rb
create app/models/recipe.rb
Die beiden erstellten Dateien sind:
- A
recipe.rb
file that holds all the model-related logic. - A
20221017220817_create_recipes.rb
file (the number at the beginning of the file may differ depending on the date when you run the command). This migration file contains the instruction for creating the database structure.
Als nächstes bearbeiten Sie die Rezeptmodelldatei, um sicherzustellen, dass nur gültige Daten in die Datenbank gespeichert werden. Dies können Sie erreichen, indem Sie einige Datenbankvalidierungen zu Ihrem Modell hinzufügen.
Öffnen Sie Ihr Rezeptmodell, das sich unter app/models/recipe.rb
befindet:
- nano ~/rails_react_recipe/app/models/recipe.rb
Fügen Sie den folgenden markierten Code zu der Datei hinzu:
class Recipe < ApplicationRecord
validates :name, presence: true
validates :ingredients, presence: true
validates :instruction, presence: true
end
In diesem Code fügen Sie die Modellvalidierung hinzu, die auf das Vorhandensein der Felder name
, ingredients
und instruction
überprüft. Ohne diese drei Felder ist ein Rezept ungültig und wird nicht in die Datenbank gespeichert.
Speichern Sie die Datei und schließen Sie sie.
Um Rails zu veranlassen, die Tabelle recipes
in Ihrer Datenbank zu erstellen, müssen Sie eine Migration durchführen, was eine Möglichkeit ist, Änderungen an Ihrer Datenbank programmgesteuert vorzunehmen. Um sicherzustellen, dass die Migration mit der von Ihnen eingerichteten Datenbank funktioniert, müssen Sie Änderungen an der Datei 20221017220817_create_recipes.rb
vornehmen.
Öffnen Sie diese Datei in Ihrem Editor:
- nano ~/rails_react_recipe/db/migrate/20221017220817_create_recipes.rb
Fügen Sie die markierten Materialien hinzu, damit Ihre Datei wie folgt aussieht:
class CreateRecipes < ActiveRecord::Migration[5.2]
def change
create_table :recipes do |t|
t.string :name, null: false
t.text :ingredients, null: false
t.text :instruction, null: false
t.string :image, default: 'https://raw.githubusercontent.com/do-community/react_rails_recipe/master/app/assets/images/Sammy_Meal.jpg'
t.timestamps
end
end
end
Diese Migrationsdatei enthält eine Ruby-Klasse mit einer change
-Methode und einem Befehl zur Erstellung einer Tabelle namens recipes
zusammen mit den Spalten und ihren Datentypen. Außerdem aktualisieren Sie 20221017220817_create_recipes.rb
mit einer NOT NULL
-Einschränkung für die Spalten name
, ingredients
und instruction
, indem Sie null: false
hinzufügen, was sicherstellt, dass diese Spalten einen Wert haben, bevor Sie die Datenbank ändern. Schließlich fügen Sie eine Standardbild-URL für Ihre Bildspalte hinzu; dies könnte eine andere URL sein, wenn Sie ein anderes Bild verwenden möchten.
Mit diesen Änderungen speichern und beenden Sie die Datei. Sie sind nun bereit, Ihre Migration auszuführen und Ihre Tabelle zu erstellen. Führen Sie in Ihrem Terminal den folgenden Befehl aus:
- rails db:migrate
Du verwendest den Befehl `database migrate`, um die Anweisungen in deiner Migrationsdatei auszuführen. Sobald der Befehl erfolgreich ausgeführt wurde, erhältst du eine Ausgabe ähnlich der folgenden:
Output== 20190407161357 CreateRecipes: migrating ====================================
-- create_table(:recipes)
-> 0.0140s
== 20190407161357 CreateRecipes: migrated (0.0141s) ===========================
Mit deinem Rezeptmodell an Ort und Stelle wirst du als nächstes deinen Rezept-Controller erstellen, um die Logik zum Erstellen, Lesen und Löschen von Rezepten hinzuzufügen. Führe den folgenden Befehl aus:
- rails generate controller api/v1/Recipes index create show destroy --skip-template-engine --no-helper
In diesem Befehl erstellst du einen `Recipes`-Controller in einem `api/v1`-Verzeichnis mit einer `index`, `create`, `show` und `destroy` Aktion. Die `index`-Aktion wird das Abrufen aller deiner Rezepte behandeln; die `create`-Aktion wird für das Erstellen neuer Rezepte verantwortlich sein; die `show`-Aktion wird ein einzelnes Rezept abrufen, und die `destroy`-Aktion wird die Logik zum Löschen eines Rezepts enthalten.
Du übergibst auch einige Flags, um den Controller schlanker zu machen, darunter:
- `–skip-template-engine`, das Rails anweist, Rails-View-Dateien zu überspringen, da React deine Front-End-Anforderungen handhabt.
- `–no-helper`, das Rails anweist, das Generieren einer Hilfsdatei für deinen Controller zu überspringen.
Durch Ausführen des Befehls wird auch deine Routendatei mit einer Route für jede Aktion im `Recipes`-Controller aktualisiert.
Wenn der Befehl ausgeführt wird, wird eine Ausgabe wie folgt gedruckt:
Output create app/controllers/api/v1/recipes_controller.rb
route namespace :api do
namespace :v1 do
get 'recipes/index'
get 'recipes/create'
get 'recipes/show'
get 'recipes/destroy'
end
end
Um diese Routen zu verwenden, wirst du Änderungen an deiner `config/routes.rb`-Datei vornehmen. Öffne die `routes.rb`-Datei in deinem Texteditor:
- nano ~/rails_react_recipe/config/routes.rb
Aktualisiere diese Datei, um wie der folgende Code auszusehen, indem du die hervorgehobenen Zeilen änderst oder hinzufügst:
Rails.application.routes.draw do
namespace :api do
namespace :v1 do
get 'recipes/index'
post 'recipes/create'
get '/show/:id', to: 'recipes#show'
delete '/destroy/:id', to: 'recipes#destroy'
end
end
root 'homepage#index'
get '/*path' => 'homepage#index'
# Definieren Sie Ihre Anwendungswege gemäß der DSL in https://guides.rubyonrails.org/routing.html
# Definiert den Wurzelpfad ("/")
# root "articles#index"
end
In dieser Routendatei ändern Sie das HTTP-Verb der create
– und destroy
-Wege, damit es Daten post
und delete
kann. Sie ändern auch die Wege für die Aktionen show
und destroy
, indem Sie einen :id
-Parameter zum Weg hinzufügen. :id
wird die Identifikationsnummer des Rezepts halten, das Sie lesen oder löschen möchten.
Sie fügen einen Catch-All-Weg mit get '/*path'
hinzu, der jede andere Anfrage, die nicht mit den vorhandenen Wegen übereinstimmt, zur Aktion index
des Controllers homepage
leitet. Die Front-End-Routing wird Anfragen behandeln, die nichts mit dem Erstellen, Lesen oder Löschen von Rezepten zu tun haben.
Speichern und beenden Sie die Datei.
Um eine Liste der verfügbaren Routen in Ihrer Anwendung zu bewerten, führen Sie den folgenden Befehl aus:
- rails routes
Durch Ausführen dieses Befehls wird eine umfangreiche Liste von URI-Mustern, Verben und übereinstimmenden Controllern oder Aktionen für Ihr Projekt angezeigt.
Als nächstes fügen Sie die Logik hinzu, um alle Rezepte auf einmal zu erhalten. Rails verwendet die ActiveRecord-Bibliothek, um datenbankbezogene Aufgaben wie diese zu behandeln. ActiveRecord verbindet Klassen mit relationalen Datenbanktabellen und bietet eine umfangreiche API zum Arbeiten mit ihnen.
Um alle Rezepte zu erhalten, verwenden Sie ActiveRecord, um die Tabelle „recipes“ abzufragen und alle Rezepte in der Datenbank abzurufen.
Öffnen Sie die Datei recipes_controller.rb
mit folgendem Befehl:
- nano ~/rails_react_recipe/app/controllers/api/v1/recipes_controller.rb
Fügen Sie die hervorgehobenen Zeilen dem Rezept-Controller hinzu:
class Api::V1::RecipesController < ApplicationController
def index
recipe = Recipe.all.order(created_at: :desc)
render json: recipe
end
def create
end
def show
end
def destroy
end
end
In Ihrer index
-Aktion verwenden Sie die all
-Methode von ActiveRecord, um alle Rezepte in Ihrer Datenbank zu erhalten. Mit der order
-Methode ordnen Sie sie absteigend nach ihrem Erstellungsdatum, was die neuesten Rezepte zuerst platziert. Schließlich senden Sie Ihre Liste der Rezepte als JSON-Antwort mit render
.
Als nächstes fügen Sie die Logik für das Erstellen neuer Rezepte hinzu. Wie beim Abrufen aller Rezepte verlassen Sie sich auf ActiveRecord, um die bereitgestellten Rezeptdetails zu validieren und zu speichern. Aktualisieren Sie Ihren Rezept-Controller mit den folgenden hervorgehobenen Codezeilen:
class Api::V1::RecipesController < ApplicationController
def index
recipe = Recipe.all.order(created_at: :desc)
render json: recipe
end
def create
recipe = Recipe.create!(recipe_params)
if recipe
render json: recipe
else
render json: recipe.errors
end
end
def show
end
def destroy
end
private
def recipe_params
params.permit(:name, :image, :ingredients, :instruction)
end
end
Im create
-Aktion verwenden Sie die create
-Methode von ActiveRecord, um ein neues Rezept zu erstellen. Die create
-Methode kann alle Controller-Parameter auf einmal dem Modell zuweisen. Diese Methode erleichtert das Erstellen von Datensätzen, öffnet aber die Möglichkeit eines böswilligen Gebrauchs. Böswilliger Gebrauch kann verhindert werden, indem Sie das von Rails bereitgestellte starke Parameter-Feature verwenden. Auf diese Weise können Parameter nur zugewiesen werden, wenn sie zugelassen wurden. Sie übergeben einen recipe_params
-Parameter an die create
-Methode in Ihrem Code. Die recipe_params
ist eine private
-Methode, in der Sie Ihre Controller-Parameter zulassen, um falsche oder bösartige Inhalte daran zu hindern, in Ihre Datenbank zu gelangen. In diesem Fall gestatten Sie einen name
-, image
-, ingredients
– und instruction
-Parameter für die gültige Verwendung der create
-Methode.
Ihr Rezept-Controller kann jetzt Rezepte lesen und erstellen. Alles, was noch übrig ist, ist die Logik zum Lesen und Löschen eines einzelnen Rezepts. Aktualisieren Sie Ihren Rezept-Controller mit dem hervorgehobenen Code:
class Api::V1::RecipesController < ApplicationController
before_action :set_recipe, only: %i[show destroy]
def index
recipe = Recipe.all.order(created_at: :desc)
render json: recipe
end
def create
recipe = Recipe.create!(recipe_params)
if recipe
render json: recipe
else
render json: recipe.errors
end
end
def show
render json: @recipe
end
def destroy
@recipe&.destroy
render json: { message: 'Recipe deleted!' }
end
private
def recipe_params
params.permit(:name, :image, :ingredients, :instruction)
end
def set_recipe
@recipe = Recipe.find(params[:id])
end
end
Im neuen Code erstellen Sie eine private set_recipe
-Methode, die nur von einem before_action
aufgerufen wird, wenn die Aktionen show
und delete
einer Anfrage entsprechen. Die set_recipe
-Methode verwendet die find
-Methode von ActiveRecord, um ein Rezept zu finden, dessen id
mit der im params
bereitgestellten id
übereinstimmt, und weist es einer Instanzvariable @recipe
zu. In der show
-Aktion geben Sie das durch die set_recipe
-Methode festgelegte @recipe
-Objekt als JSON-Antwort zurück.
In der destroy
-Aktion haben Sie etwas Ähnliches gemacht, indem Sie den sicheren Navigationsoperator &.
von Ruby verwendet haben, der Fehler bei der Methodenaufruf vermeidet. Diese Ergänzung ermöglicht es Ihnen, ein Rezept nur zu löschen, wenn es existiert, und dann eine Nachricht als Antwort zu senden.
Nachdem Sie diese Änderungen an recipes_controller.rb
vorgenommen haben, speichern und schließen Sie die Datei.
In diesem Schritt haben Sie ein Modell und einen Controller für Ihre Rezepte erstellt. Sie haben die gesamte Logik geschrieben, die benötigt wird, um mit Rezepten auf der Backend-Seite zu arbeiten. Im nächsten Abschnitt werden Sie Komponenten erstellen, um Ihre Rezepte anzuzeigen.
Schritt 7 — Anzeigen von Rezepten
In diesem Abschnitt werden Sie Komponenten zum Anzeigen von Rezepten erstellen. Sie werden zwei Seiten erstellen: eine, um alle vorhandenen Rezepte anzuzeigen, und eine andere, um einzelne Rezepte anzuzeigen.
Sie beginnen damit, eine Seite zur Ansicht aller Rezepte zu erstellen. Bevor Sie die Seite erstellen, benötigen Sie Rezepte zum Arbeiten, da Ihre Datenbank derzeit leer ist. Rails bietet eine Möglichkeit, Stammdaten für Ihre Anwendung zu erstellen.
Öffnen Sie die Stammdaten-Datei namens seeds.rb
zum Bearbeiten:
- nano ~/rails_react_recipe/db/seeds.rb
Ersetzen Sie den anfänglichen Inhalt der Stammdaten-Datei durch den folgenden Code:
9.times do |i|
Recipe.create(
name: "Recipe #{i + 1}",
ingredients: '227g tub clotted cream, 25g butter, 1 tsp cornflour,100g parmesan, grated nutmeg, 250g fresh fettuccine or tagliatelle, snipped chives or chopped parsley to serve (optional)',
instruction: 'In a medium saucepan, stir the clotted cream, butter, and cornflour over a low-ish heat and bring to a low simmer. Turn off the heat and keep warm.'
)
end
In diesem Code verwenden Sie eine Schleife, die Rails anweist, neun Rezepte mit Abschnitten für Name
, Zutaten
und Anweisungen
zu erstellen. Speichern Sie die Datei und verlassen Sie sie.
Um die Datenbank mit diesen Daten zu füllen, führen Sie den folgenden Befehl in Ihrem Terminal aus:
- rails db:seed
Das Ausführen dieses Befehls fügt neun Rezepte Ihrer Datenbank hinzu. Jetzt können Sie sie abrufen und auf der Benutzeroberfläche rendern.
Die Komponente zum Anzeigen aller Rezepte sendet eine HTTP-Anfrage an die Index
-Aktion im RecipesController
, um eine Liste aller Rezepte zu erhalten. Diese Rezepte werden dann auf der Seite in Karten angezeigt.
Erstellen Sie eine Datei Recipes.jsx
im Verzeichnis app/javascript/components
:
- nano ~/rails_react_recipe/app/javascript/components/Recipes.jsx
Sobald die Datei geöffnet ist, importieren Sie die Module React
, useState
, useEffect
, Link
und useNavigate
, indem Sie die folgenden Zeilen hinzufügen:
import React, { useState, useEffect } from "react";
import { Link, useNavigate } from "react-router-dom";
Fügen Sie als nächstes die hervorgehobenen Zeilen hinzu, um eine funktionale React-Komponente namens Recipes
zu erstellen und zu exportieren:
import React, { useState, useEffect } from "react";
import { Link, useNavigate } from "react-router-dom";
const Recipes = () => {
const navigate = useNavigate();
const [recipes, setRecipes] = useState([]);
};
export default Recipes;
Im Recipe
-Komponenten wird die Navigations-API von React Router den useNavigate-Hook aufrufen. Der useState-Hook von React initialisiert den recipes
-Status, der ein leeres Array ([]
) ist, und eine setRecipes
-Funktion zur Aktualisierung des recipes
-Status.
Als nächstes wird in einem useEffect
-Hook eine HTTP-Anfrage zum Abrufen aller Rezepte gestellt. Fügen Sie dazu die markierten Zeilen hinzu:
import React, { useState, useEffect } from "react";
import { Link, useNavigate } from "react-router-dom";
const Recipes = () => {
const navigate = useNavigate();
const [recipes, setRecipes] = useState([]);
useEffect(() => {
const url = "/api/v1/recipes/index";
fetch(url)
.then((res) => {
if (res.ok) {
return res.json();
}
throw new Error("Network response was not ok.");
})
.then((res) => setRecipes(res))
.catch(() => navigate("/"));
}, []);
};
export default Recipes;
In Ihrem useEffect
-Hook tätigen Sie einen HTTP-Aufruf, um alle Rezepte mit der Fetch-API abzurufen. Wenn die Antwort erfolgreich ist, speichert die Anwendung das Array von Rezepten im recipes
-Status. Bei einem Fehler wird der Benutzer zur Startseite weitergeleitet.
Zum Schluss geben Sie die Markup für die Elemente zurück, die beim Rendern des Komponenten im Browser angezeigt werden. In diesem Fall rendert die Komponente eine Karte mit Rezepten aus dem recipes
-Status. Fügen Sie die markierten Zeilen zu Recipes.jsx
hinzu:
import React, { useState, useEffect } from "react";
import { Link, useNavigate } from "react-router-dom";
const Recipes = () => {
const navigate = useNavigate();
const [recipes, setRecipes] = useState([]);
useEffect(() => {
const url = "/api/v1/recipes/index";
fetch(url)
.then((res) => {
if (res.ok) {
return res.json();
}
throw new Error("Network response was not ok.");
})
.then((res) => setRecipes(res))
.catch(() => navigate("/"));
}, []);
const allRecipes = recipes.map((recipe, index) => (
<div key={index} className="col-md-6 col-lg-4">
<div className="card mb-4">
<img
src={recipe.image}
className="card-img-top"
alt={`${recipe.name} image`}
/>
<div className="card-body">
<h5 className="card-title">{recipe.name}</h5>
<Link to={`/recipe/${recipe.id}`} className="btn custom-button">
View Recipe
</Link>
</div>
</div>
</div>
));
const noRecipe = (
<div className="vw-100 vh-50 d-flex align-items-center justify-content-center">
<h4>
No recipes yet. Why not <Link to="/new_recipe">create one</Link>
</h4>
</div>
);
return (
<>
<section className="jumbotron jumbotron-fluid text-center">
<div className="container py-5">
<h1 className="display-4">Recipes for every occasion</h1>
<p className="lead text-muted">
We’ve pulled together our most popular recipes, our latest
additions, and our editor’s picks, so there’s sure to be something
tempting for you to try.
</p>
</div>
</section>
<div className="py-5">
<main className="container">
<div className="text-end mb-3">
<Link to="/recipe" className="btn custom-button">
Create New Recipe
</Link>
</div>
<div className="row">
{recipes.length > 0 ? allRecipes : noRecipe}
</div>
<Link to="/" className="btn btn-link">
Home
</Link>
</main>
</div>
</>
);
};
export default Recipes;
Speichern Sie und beenden Sie Recipes.jsx
.
Da Sie jetzt eine Komponente erstellt haben, um alle Rezepte anzuzeigen, erstellen Sie eine Route dafür. Öffnen Sie die Frontend-Routendatei app/javascript/routes/index.jsx
:
- nano app/javascript/routes/index.jsx
Fügen Sie die markierten Zeilen zur Datei hinzu:
import React from "react";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import Home from "../components/Home";
import Recipes from "../components/Recipes";
export default (
<Router>
<Routes>
<Route path="/" exact component={Home} />
<Route path="/recipes" element={<Recipes />} />
</Routes>
</Router>
);
Speichern und schließen Sie die Datei.
An diesem Punkt ist es eine gute Idee, zu überprüfen, ob Ihr Code wie erwartet funktioniert. Verwenden Sie wie zuvor den folgenden Befehl, um Ihren Server zu starten:
- bin/dev
Öffnen Sie dann die App in Ihrem Browser. Drücken Sie die Schaltfläche Rezept anzeigen auf der Startseite, um eine Anzeigeseite mit Ihren Ausgangsrezepten aufzurufen:
Verwenden Sie CTRL+C
in Ihrem Terminal, um den Server zu stoppen und zu Ihrer Eingabeaufforderung zurückzukehren.
Jetzt, da Sie alle Rezepte in Ihrer Anwendung anzeigen können, ist es an der Zeit, eine zweite Komponente zu erstellen, um einzelne Rezepte anzuzeigen. Erstellen Sie eine Datei namens Recipe.jsx
im Verzeichnis app/javascript/components
:
- nano app/javascript/components/Recipe.jsx
Wie bei der Recipes
-Komponente importieren Sie die Module React
, useState
, useEffect
, Link
, useNavigate
und useParam
, indem Sie die folgenden Zeilen hinzufügen:
import React, { useState, useEffect } from "react";
import { Link, useNavigate, useParams } from "react-router-dom";
Fügen Sie als Nächstes die hervorgehobenen Zeilen hinzu, um eine funktionale React-Komponente namens Recipe
zu erstellen und zu exportieren:
import React, { useState, useEffect } from "react";
import { Link, useNavigate, useParams } from "react-router-dom";
const Recipe = () => {
const params = useParams();
const navigate = useNavigate();
const [recipe, setRecipe] = useState({ ingredients: "" });
};
export default Recipe;
Wie bei der Recipes
-Komponente initialisieren Sie die Navigation von React Router mit dem useNavigate
-Hook. Ein Rezept
-Status und eine setRecipe
-Funktion aktualisieren den Status mit dem useState
-Hook. Außerdem rufen Sie den useParams
-Hook auf, der ein Objekt zurückgibt, dessen Schlüssel/Wert-Paare URL-Parameter sind.
Um ein bestimmtes Rezept zu finden, muss Ihre Anwendung die id
des Rezepts kennen, was bedeutet, dass Ihre Recipe
-Komponente einen id
–param
in der URL erwartet. Sie können darauf über das params
-Objekt zugreifen, das den Rückgabewert des useParams
-Hooks enthält.
Nächster Schritt: Deklarieren Sie einen useEffect
-Hook, in dem Sie auf das id
–param
aus dem params
-Objekt zugreifen. Sobald Sie das Rezept id
-Param haben, führen Sie eine HTTP-Anfrage aus, um das Rezept abzurufen. Fügen Sie die hervorgehobenen Zeilen zu Ihrer Datei hinzu:
import React, { useState, useEffect } from "react";
import { Link, useNavigate, useParams } from "react-router-dom";
const Recipe = () => {
const params = useParams();
const navigate = useNavigate();
const [recipe, setRecipe] = useState({ ingredients: "" });
useEffect(() => {
const url = `/api/v1/show/${params.id}`;
fetch(url)
.then((response) => {
if (response.ok) {
return response.json();
}
throw new Error("Network response was not ok.");
})
.then((response) => setRecipe(response))
.catch(() => navigate("/recipes"));
}, [params.id]);
};
export default Recipe;
Im useEffect
-Hook verwenden Sie den Wert params.id
, um eine GET-HTTP-Anfrage zum Abrufen des Rezepts durchzuführen, das die id
besitzt, und es dann mit der setRecipe
-Funktion im Komponentenzustand zu speichern. Die App leitet den Benutzer zur Rezepte-Seite um, wenn das Rezept nicht existiert.
Als nächstes fügen Sie eine addHtmlEntities
-Funktion hinzu, die dazu verwendet wird, Zeichenentitäten durch HTML-Entitäten im Komponenteninhalt zu ersetzen. Die addHtmlEntities
-Funktion nimmt einen String und ersetzt alle entkommenen öffnenden und schließenden Klammern durch ihre HTML-Entitäten. Diese Funktion hilft Ihnen, das in Ihrer Rezeptanweisung gespeicherte entkommene Zeichen in das entsprechende Format zu konvertieren. Fügen Sie die hervorgehobenen Zeilen hinzu:
import React, { useState, useEffect } from "react";
import { Link, useNavigate, useParams } from "react-router-dom";
const Recipe = () => {
const params = useParams();
const navigate = useNavigate();
const [recipe, setRecipe] = useState({ ingredients: "" });
useEffect(() => {
const url = `/api/v1/show/${params.id}`;
fetch(url)
.then((response) => {
if (response.ok) {
return response.json();
}
throw new Error("Network response was not ok.");
})
.then((response) => setRecipe(response))
.catch(() => navigate("/recipes"));
}, [params.id]);
const addHtmlEntities = (str) => {
return String(str).replace(/</g, "<").replace(/>/g, ">");
};
};
export default Recipe;
Zum Schluss geben Sie das Markup zurück, um das Rezept im Komponentenzustand auf der Seite zu rendern, indem Sie die hervorgehobenen Zeilen hinzufügen:
import React, { useState, useEffect } from "react";
import { Link, useNavigate, useParams } from "react-router-dom";
const Recipe = () => {
const params = useParams();
const navigate = useNavigate();
const [recipe, setRecipe] = useState({ ingredients: "" });
useEffect(() => {
const url = `/api/v1/show/${params.id}`;
fetch(url)
.then((response) => {
if (response.ok) {
return response.json();
}
throw new Error("Network response was not ok.");
})
.then((response) => setRecipe(response))
.catch(() => navigate("/recipes"));
}, [params.id]);
const addHtmlEntities = (str) => {
return String(str).replace(/</g, "<").replace(/>/g, ">");
};
const ingredientList = () => {
let ingredientList = "No ingredients available";
if (recipe.ingredients.length > 0) {
ingredientList = recipe.ingredients
.split(",")
.map((ingredient, index) => (
<li key={index} className="list-group-item">
{ingredient}
</li>
));
}
return ingredientList;
};
const recipeInstruction = addHtmlEntities(recipe.instruction);
return (
<div className="">
<div className="hero position-relative d-flex align-items-center justify-content-center">
<img
src={recipe.image}
alt={`${recipe.name} image`}
className="img-fluid position-absolute"
/>
<div className="overlay bg-dark position-absolute" />
<h1 className="display-4 position-relative text-white">
{recipe.name}
</h1>
</div>
<div className="container py-5">
<div className="row">
<div className="col-sm-12 col-lg-3">
<ul className="list-group">
<h5 className="mb-2">Ingredients</h5>
{ingredientList()}
</ul>
</div>
<div className="col-sm-12 col-lg-7">
<h5 className="mb-2">Preparation Instructions</h5>
<div
dangerouslySetInnerHTML={{
__html: `${recipeInstruction}`,
}}
/>
</div>
<div className="col-sm-12 col-lg-2">
<button
type="button"
className="btn btn-danger"
>
Delete Recipe
</button>
</div>
</div>
<Link to="/recipes" className="btn btn-link">
Back to recipes
</Link>
</div>
</div>
);
};
export default Recipe;
Mit einer ingredientList
-Funktion teilen Sie Ihre kommagetrennten Rezeptzutaten in ein Array auf und erstellen eine Liste der Zutaten, indem Sie über sie mappen. Wenn es keine Zutaten gibt, zeigt die App eine Nachricht an, die besagt Keine Zutaten verfügbar. Sie ersetzen auch alle öffnenden und schließenden Klammern in den Rezeptanweisungen, indem Sie sie durch die addHtmlEntities
-Funktion übergeben. Schließlich zeigt der Code das Rezeptbild als Hero-Bild an, fügt neben der Rezeptanweisung eine Rezept löschen-Schaltfläche hinzu und fügt eine Schaltfläche hinzu, die zur Rezeptseite zurückverlinkt.
Hinweis: Die Verwendung des dangerouslySetInnerHTML
-Attributs von React ist riskant, da es Ihre App anfällig für Cross-Site Scripting-Angriffe macht. Dieses Risiko wird verringert, indem sichergestellt wird, dass bei der Erstellung von Rezepten eingegebene Sonderzeichen durch die in der NewRecipe
-Komponente deklarierte stripHtmlEntities
-Funktion ersetzt werden.
Speichern und schließen Sie die Datei.
Um die Recipe
-Komponente auf einer Seite anzuzeigen, fügen Sie sie Ihrer Routendatei hinzu. Öffnen Sie Ihre Routendatei zur Bearbeitung:
- nano app/javascript/routes/index.jsx
Fügen Sie die folgenden hervorgehobenen Zeilen zur Datei hinzu:
import React from "react";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import Home from "../components/Home";
import Recipes from "../components/Recipes";
import Recipe from "../components/Recipe";
export default (
<Router>
<Routes>
<Route path="/" exact component={Home} />
<Route path="/recipes" exact component={Recipes} />
<Route path="/recipe/:id" element={<Recipe />} />
</Routes>
</Router>
);
Sie importieren Ihre Recipe
-Komponente in dieser Routendatei und fügen eine Route hinzu. Die Route hat einen :id
–param
, der durch die id
des Rezepts ersetzt wird, das Sie anzeigen möchten.
Speichern und schließen Sie die Datei.
Verwenden Sie das bin/dev
-Skript, um Ihren Server erneut zu starten, dann besuchen Sie http://localhost:3000
in Ihrem Browser. Klicken Sie auf die Schaltfläche Rezepte anzeigen, um zur Rezepteseite zu navigieren. Auf der Rezepteseite greifen Sie auf ein beliebiges Rezept zu, indem Sie auf dessen Schaltfläche Rezept anzeigen klicken. Sie werden mit einer Seite begrüßt, die mit den Daten aus Ihrer Datenbank gefüllt ist:
Sie können den Server mit STRG+C
stoppen.
In diesem Schritt haben Sie neun Rezepte zu Ihrer Datenbank hinzugefügt und Komponenten erstellt, um diese Rezepte sowohl einzeln als auch als Sammlung anzuzeigen. Im nächsten Schritt fügen Sie eine Komponente hinzu, um Rezepte zu erstellen.
Schritt 8 — Rezepte erstellen
Der nächste Schritt zur Erstellung einer verwendbaren Lebensmittelrezeptanwendung besteht darin, neue Rezepte erstellen zu können. In diesem Schritt erstellen Sie eine Komponente für diese Funktion. Die Komponente wird ein Formular enthalten, um die erforderlichen Rezeptdetails vom Benutzer zu sammeln, und dann eine Anfrage an die create
-Aktion im Recipe
-Controller senden, um die Rezeptdaten zu speichern.
Erstellen Sie eine Datei NewRecipe.jsx
im Verzeichnis app/javascript/components
:
- nano app/javascript/components/NewRecipe.jsx
In der neuen Datei importieren Sie die React
, useState
, Link
und useNavigate
Module, die Sie in anderen Komponenten verwendet haben:
import React, { useState } from "react";
import { Link, useNavigate } from "react-router-dom";
Erstellen und exportieren Sie dann eine funktionale NewRecipe
-Komponente, indem Sie die markierten Zeilen hinzufügen:
import React, { useState } from "react";
import { Link, useNavigate } from "react-router-dom";
const NewRecipe = () => {
const navigate = useNavigate();
const [name, setName] = useState("");
const [ingredients, setIngredients] = useState("");
const [instruction, setInstruction] = useState("");
};
export default NewRecipe;
Wie bei den vorherigen Komponenten initialisieren Sie die React Router-Navigation mit dem useNavigate
-Hook und verwenden dann den useState
-Hook, um einen name
-, ingredients
– und instruction
-Status zu initialisieren, jeweils mit ihren entsprechenden Aktualisierungsfunktionen. Dies sind die Felder, die Sie benötigen, um ein gültiges Rezept zu erstellen.
Als nächstes erstellen Sie eine stripHtmlEntities
-Funktion, die Sonderzeichen (wie <
) in ihre entkommenen/kodierten Werte (wie <
) umwandelt. Fügen Sie dazu die hervorgehobenen Zeilen der NewRecipe
-Komponente hinzu:
import React, { useState } from "react";
import { Link, useNavigate } from "react-router-dom";
const NewRecipe = () => {
const navigate = useNavigate();
const [name, setName] = useState("");
const [ingredients, setIngredients] = useState("");
const [instruction, setInstruction] = useState("");
const stripHtmlEntities = (str) => {
return String(str)
.replace(/\n/g, "<br> <br>")
.replace(/</g, "<")
.replace(/>/g, ">");
};
};
export default NewRecipe;
In der stripHtmlEntities
-Funktion ersetzen Sie die <
– und >
-Zeichen durch ihre entkommenen Werte. Auf diese Weise speichern Sie kein rohes HTML in Ihrer Datenbank.
Fügen Sie als nächstes die hervorgehobenen Zeilen hinzu, um die onChange
– und onSubmit
-Funktionen der NewRecipe
-Komponente hinzuzufügen, um die Bearbeitung und Übermittlung des Formulars zu behandeln:
import React, { useState } from "react";
import { Link, useNavigate } from "react-router-dom";
const NewRecipe = () => {
const navigate = useNavigate();
const [name, setName] = useState("");
const [ingredients, setIngredients] = useState("");
const [instruction, setInstruction] = useState("");
const stripHtmlEntities = (str) => {
return String(str)
.replace(/\n/g, "<br> <br>")
.replace(/</g, "<")
.replace(/>/g, ">");
};
const onChange = (event, setFunction) => {
setFunction(event.target.value);
};
const onSubmit = (event) => {
event.preventDefault();
const url = "/api/v1/recipes/create";
if (name.length == 0 || ingredients.length == 0 || instruction.length == 0)
return;
const body = {
name,
ingredients,
instruction: stripHtmlEntities(instruction),
};
const token = document.querySelector('meta[name="csrf-token"]').content;
fetch(url, {
method: "POST",
headers: {
"X-CSRF-Token": token,
"Content-Type": "application/json",
},
body: JSON.stringify(body),
})
.then((response) => {
if (response.ok) {
return response.json();
}
throw new Error("Network response was not ok.");
})
.then((response) => navigate(`/recipe/${response.id}`))
.catch((error) => console.log(error.message));
};
};
export default NewRecipe;
Die Funktion onChange
akzeptiert die Benutzereingabe event
und die Statussetzungsfunktion. Danach aktualisiert sie den Status mit dem Wert der Benutzereingabe. In der Funktion onSubmit
überprüfen Sie, dass keines der erforderlichen Eingabefelder leer ist. Anschließend erstellen Sie ein Objekt, das die zum Erstellen eines neuen Rezepts erforderlichen Parameter enthält. Mit der Funktion stripHtmlEntities
ersetzen Sie die Zeichen <
und >
in den Rezeptanweisungen durch ihren escaped-Wert und ersetzen jedes Zeichen für einen Zeilenumbruch durch einen Umbruchtag. Dadurch wird das vom Benutzer eingegebene Textformat beibehalten. Schließlich senden Sie eine POST-HTTP-Anfrage zum Erstellen des neuen Rezepts und leiten bei einer erfolgreichen Antwort zur Seite des neuen Rezepts weiter.
Um sich gegen Cross-Site Request Forgery (CSRF)-Angriffe zu schützen, fügt Rails einen CSRF-Sicherheitstoken dem HTML-Dokument hinzu. Dieser Token ist immer erforderlich, wenn eine nicht-GET
-Anfrage gestellt wird. Mit der Konstanten token
im vorhergehenden Code überprüft Ihre Anwendung den Token auf dem Server und wirft eine Ausnahme, wenn der Sicherheitstoken nicht mit dem erwarteten übereinstimmt. In der Funktion onSubmit
ruft die Anwendung den in Ihrem HTML-Dokument von Rails eingebetteten CSRF-Token ab und sendet dann eine HTTP-Anfrage mit einem JSON-String. Wenn das Rezept erfolgreich erstellt wurde, leitet die Anwendung den Benutzer zur Rezeptseite weiter, wo er sein neu erstelltes Rezept anzeigen kann.
Zuletzt geben Sie die Markup zurück, das ein Formular rendert, damit der Benutzer die Details für das zu erstellende Rezept eingeben kann. Fügen Sie die markierten Zeilen hinzu:
import React, { useState } from "react";
import { Link, useNavigate } from "react-router-dom";
const NewRecipe = () => {
const navigate = useNavigate();
const [name, setName] = useState("");
const [ingredients, setIngredients] = useState("");
const [instruction, setInstruction] = useState("");
const stripHtmlEntities = (str) => {
return String(str)
.replace(/\n/g, "<br> <br>")
.replace(/</g, "<")
.replace(/>/g, ">");
};
const onChange = (event, setFunction) => {
setFunction(event.target.value);
};
const onSubmit = (event) => {
event.preventDefault();
const url = "/api/v1/recipes/create";
if (name.length == 0 || ingredients.length == 0 || instruction.length == 0)
return;
const body = {
name,
ingredients,
instruction: stripHtmlEntities(instruction),
};
const token = document.querySelector('meta[name="csrf-token"]').content;
fetch(url, {
method: "POST",
headers: {
"X-CSRF-Token": token,
"Content-Type": "application/json",
},
body: JSON.stringify(body),
})
.then((response) => {
if (response.ok) {
return response.json();
}
throw new Error("Network response was not ok.");
})
.then((response) => navigate(`/recipe/${response.id}`))
.catch((error) => console.log(error.message));
};
return (
<div className="container mt-5">
<div className="row">
<div className="col-sm-12 col-lg-6 offset-lg-3">
<h1 className="font-weight-normal mb-5">
Add a new recipe to our awesome recipe collection.
</h1>
<form onSubmit={onSubmit}>
<div className="form-group">
<label htmlFor="recipeName">Recipe name</label>
<input
type="text"
name="name"
id="recipeName"
className="form-control"
required
onChange={(event) => onChange(event, setName)}
/>
</div>
<div className="form-group">
<label htmlFor="recipeIngredients">Ingredients</label>
<input
type="text"
name="ingredients"
id="recipeIngredients"
className="form-control"
required
onChange={(event) => onChange(event, setIngredients)}
/>
<small id="ingredientsHelp" className="form-text text-muted">
Separate each ingredient with a comma.
</small>
</div>
<label htmlFor="instruction">Preparation Instructions</label>
<textarea
className="form-control"
id="instruction"
name="instruction"
rows="5"
required
onChange={(event) => onChange(event, setInstruction)}
/>
<button type="submit" className="btn custom-button mt-3">
Create Recipe
</button>
<Link to="/recipes" className="btn btn-link mt-3">
Back to recipes
</Link>
</form>
</div>
</div>
</div>
);
};
export default NewRecipe;
Die zurückgegebene Markup enthält ein Formular mit drei Eingabefeldern; eines für den recipeName
, eines für die recipeIngredients
und eines für die instruction
. Jedes Eingabefeld hat einen onChange
-Ereignishandler, der die Funktion onChange
aufruft. Ein onSubmit
-Ereignishandler ist auch an die Schaltfläche „Senden“ angehängt und ruft die Funktion onSubmit
auf, die die Formulardaten übermittelt.
Speichern Sie die Datei und beenden Sie sie.
Um auf dieses Komponente im Browser zuzugreifen, aktualisieren Sie Ihre Routendatei mit ihrer Route:
- nano app/javascript/routes/index.jsx
Aktualisieren Sie Ihre Routendatei, um diese markierten Zeilen einzuschließen:
import React from "react";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import Home from "../components/Home";
import Recipes from "../components/Recipes";
import Recipe from "../components/Recipe";
import NewRecipe from "../components/NewRecipe";
export default (
<Router>
<Routes>
<Route path="/" exact component={Home} />
<Route path="/recipes" exact component={Recipes} />
<Route path="/recipe/:id" exact component={Recipe} />
<Route path="/recipe" element={<NewRecipe />} />
</Routes>
</Router>
);
Mit der Route an ihrem Platz speichern und beenden Sie Ihre Datei.
Starten Sie Ihren Entwicklungsserver neu und besuchen Sie http://localhost:3000
in Ihrem Browser. Navigieren Sie zur Rezeptseite und klicken Sie auf die Schaltfläche Neues Rezept erstellen. Sie gelangen zu einer Seite mit einem Formular, um Rezepte zu Ihrer Datenbank hinzuzufügen:
Geben Sie die erforderlichen Rezeptdetails ein und klicken Sie auf die Schaltfläche Rezept erstellen. Das neu erstellte Rezept erscheint dann auf der Seite. Wenn Sie bereit sind, schließen Sie den Server.
In diesem Schritt haben Sie die Möglichkeit hinzugefügt, Rezepte zu Ihrer Lebensmittel-Rezeptanwendung zu erstellen. Im nächsten Schritt werden Sie die Funktionalität zum Löschen von Rezepten hinzufügen.
Schritt 9 — Rezepte löschen
In diesem Abschnitt werden Sie Ihre Rezeptkomponente ändern, um eine Option zum Löschen von Rezepten einzuschließen. Wenn Sie auf die Schaltfläche „Löschen“ auf der Rezeptseite klicken, sendet die Anwendung eine Anfrage zum Löschen eines Rezepts aus der Datenbank.
Öffnen Sie zunächst Ihre Datei Recipe.jsx
zum Bearbeiten:
- nano app/javascript/components/Recipe.jsx
Fügen Sie in der Recipe
-Komponente eine Funktion deleteRecipe
mit den markierten Zeilen hinzu:
import React, { useState, useEffect } from "react";
import { Link, useNavigate, useParams } from "react-router-dom";
const Recipe = () => {
const params = useParams();
const navigate = useNavigate();
const [recipe, setRecipe] = useState({ ingredients: "" });
useEffect(() => {
const url = `/api/v1/show/${params.id}`;
fetch(url)
.then((response) => {
if (response.ok) {
return response.json();
}
throw new Error("Network response was not ok.");
})
.then((response) => setRecipe(response))
.catch(() => navigate("/recipes"));
}, [params.id]);
const addHtmlEntities = (str) => {
return String(str).replace(/</g, "<").replace(/>/g, ">");
};
const deleteRecipe = () => {
const url = `/api/v1/destroy/${params.id}`;
const token = document.querySelector('meta[name="csrf-token"]').content;
fetch(url, {
method: "DELETE",
headers: {
"X-CSRF-Token": token,
"Content-Type": "application/json",
},
})
.then((response) => {
if (response.ok) {
return response.json();
}
throw new Error("Network response was not ok.");
})
.then(() => navigate("/recipes"))
.catch((error) => console.log(error.message));
};
const ingredientList = () => {
let ingredientList = "No ingredients available";
if (recipe.ingredients.length > 0) {
ingredientList = recipe.ingredients
.split(",")
.map((ingredient, index) => (
<li key={index} className="list-group-item">
{ingredient}
</li>
));
}
return ingredientList;
};
const recipeInstruction = addHtmlEntities(recipe.instruction);
return (
<div className="">
...
In der Funktion deleteRecipe
erhalten Sie die id
des zu löschenden Rezepts, bauen dann Ihre URL und greifen auf das CSRF-Token zu. Anschließend stellen Sie eine DELETE
-Anfrage an den Recipes
-Controller, um das Rezept zu löschen. Die Anwendung leitet den Benutzer zur Rezepte-Seite um, wenn das Rezept erfolgreich gelöscht wurde.
Um den Code in der Funktion deleteRecipe
auszuführen, wenn die Löschen-Schaltfläche geklickt wird, geben Sie diese als Klick-Ereignishandler an die Schaltfläche weiter. Fügen Sie ein onClick
-Ereignis zum Löschen-Schaltflächenelement in der Komponente hinzu:
...
return (
<div className="">
<div className="hero position-relative d-flex align-items-center justify-content-center">
<img
src={recipe.image}
alt={`${recipe.name} image`}
className="img-fluid position-absolute"
/>
<div className="overlay bg-dark position-absolute" />
<h1 className="display-4 position-relative text-white">
{recipe.name}
</h1>
</div>
<div className="container py-5">
<div className="row">
<div className="col-sm-12 col-lg-3">
<ul className="list-group">
<h5 className="mb-2">Ingredients</h5>
{ingredientList()}
</ul>
</div>
<div className="col-sm-12 col-lg-7">
<h5 className="mb-2">Preparation Instructions</h5>
<div
dangerouslySetInnerHTML={{
__html: `${recipeInstruction}`,
}}
/>
</div>
<div className="col-sm-12 col-lg-2">
<button
type="button"
className="btn btn-danger"
onClick={deleteRecipe}
>
Delete Recipe
</button>
</div>
</div>
<Link to="/recipes" className="btn btn-link">
Back to recipes
</Link>
</div>
</div>
);
...
Zu diesem Zeitpunkt sollte Ihre vollständige Datei Recipe.jsx
dieser Datei entsprechen:
import React, { useState, useEffect } from "react";
import { Link, useNavigate, useParams } from "react-router-dom";
const Recipe = () => {
const params = useParams();
const navigate = useNavigate();
const [recipe, setRecipe] = useState({ ingredients: "" });
useEffect(() => {
const url = `/api/v1/show/${params.id}`;
fetch(url)
.then((response) => {
if (response.ok) {
return response.json();
}
throw new Error("Network response was not ok.");
})
.then((response) => setRecipe(response))
.catch(() => navigate("/recipes"));
}, [params.id]);
const addHtmlEntities = (str) => {
return String(str).replace(/</g, "<").replace(/>/g, ">");
};
const deleteRecipe = () => {
const url = `/api/v1/destroy/${params.id}`;
const token = document.querySelector('meta[name="csrf-token"]').content;
fetch(url, {
method: "DELETE",
headers: {
"X-CSRF-Token": token,
"Content-Type": "application/json",
},
})
.then((response) => {
if (response.ok) {
return response.json();
}
throw new Error("Network response was not ok.");
})
.then(() => navigate("/recipes"))
.catch((error) => console.log(error.message));
};
const ingredientList = () => {
let ingredientList = "No ingredients available";
if (recipe.ingredients.length > 0) {
ingredientList = recipe.ingredients
.split(",")
.map((ingredient, index) => (
<li key={index} className="list-group-item">
{ingredient}
</li>
));
}
return ingredientList;
};
const recipeInstruction = addHtmlEntities(recipe.instruction);
return (
<div className="">
<div className="hero position-relative d-flex align-items-center justify-content-center">
<img
src={recipe.image}
alt={`${recipe.name} image`}
className="img-fluid position-absolute"
/>
<div className="overlay bg-dark position-absolute" />
<h1 className="display-4 position-relative text-white">
{recipe.name}
</h1>
</div>
<div className="container py-5">
<div className="row">
<div className="col-sm-12 col-lg-3">
<ul className="list-group">
<h5 className="mb-2">Ingredients</h5>
{ingredientList()}
</ul>
</div>
<div className="col-sm-12 col-lg-7">
<h5 className="mb-2">Preparation Instructions</h5>
<div
dangerouslySetInnerHTML={{
__html: `${recipeInstruction}`,
}}
/>
</div>
<div className="col-sm-12 col-lg-2">
<button
type="button"
className="btn btn-danger"
onClick={deleteRecipe}
>
Delete Recipe
</button>
</div>
</div>
<Link to="/recipes" className="btn btn-link">
Back to recipes
</Link>
</div>
</div>
);
};
export default Recipe;
Speichern und verlassen Sie die Datei.
Starten Sie den Anwendungsserver neu und navigieren Sie zur Startseite. Klicken Sie auf die Schaltfläche Rezepte anzeigen, um alle vorhandenen Rezepte anzuzeigen, öffnen Sie dann ein bestimmtes Rezept und klicken Sie auf die Schaltfläche Rezept löschen auf der Seite, um das Rezept zu löschen. Sie werden zur Rezepte-Seite umgeleitet, und das gelöschte Rezept wird nicht mehr vorhanden sein.
Mit der funktionierenden Löschen-Schaltfläche haben Sie jetzt eine voll funktionsfähige Rezeptanwendung!
Schlussfolgerung
In diesem Tutorial haben Sie eine Lebensmittelrezeptanwendung mit Ruby on Rails und einem React-Frontend erstellt, wobei PostgreSQL als Datenbank und Bootstrap für das Styling verwendet wurden. Wenn Sie weiterhin mit Ruby on Rails entwickeln möchten, sollten Sie unserem Tutorial Sichere Kommunikation in einer Rails-Anwendung mit drei Ebenen unter Verwendung von SSH-Tunneln folgen oder unsere Serie Wie man in Ruby codiert besuchen, um Ihre Ruby-Fähigkeiten aufzufrischen. Um tiefer in React einzutauchen, versuchen Sie Wie man Daten von der DigitalOcean API mit React anzeigt.