अपने एप्लिकेशंस के पूरे फ्रंट-एंड पक्ष को बनाने वाली कंपनियां अक्सर बैक-एंड बनाने के लिए उसी फ्रेमवर्क, जैसे रेल्स, को चुनती हैं। कई वर्षों से, यह सबसे अच्छा और सबसे विश्वसनीय विकल्प रहा है।
आज, लगातार विकसित हो रहे फ्रंट-एंड यूनिवर्स में टनों लाइब्रेरीज और फ्रेमवर्क्स डेवलपर्स को बैक और फ्रंट-एंड दोनों के लिए अलग-अलग प्लेटफॉर्म चुनने और उन्हें आसानी से इंटीग्रेट करने की अनुमति देते हैं।
रिएक्ट फ्रंट-एंड पैंजिया का टाइटन बन गया है। यदि आप रूबी ऑन रेल्स के साथ काम करते हैं, तो संभावना है कि आपने एक बार डिफ़ॉल्ट रेल्स पेज को रिएक्ट कोड (या किसी अन्य फ्रंट फ्रेमवर्क) में बदलने की आवश्यकता महसूस की हो। शायद, आप बस रेल्स + रिएक्ट फीचर्स से प्यार करते हैं और दोनों टेक्नोलॉजीज की शक्ति को एक ही ऐप में इंटीग्रेट करना पसंद करेंगे।
और, यह ठीक है! इस लेख का उद्देश्य एक रियल-वर्ल्ड उदाहरण का पता लगाना है: एक CRUD एप्लिकेशन जो बीयर उत्पादों की इन्वेंट्री को मैनेज करता है। इसे शुरू में रेल्स के साथ बनाया जाएगा, और फिर रिएक्ट के साथ बना एक नया फ्रंट-एंड आगे इंटीग्रेट किया जाएगा।
बीयर CRUD एप्लिकेशन।
कुछ चरणों में, आप रिएक्ट के मुख्य टुकड़ों को समझेंगे, रेल्स इसे कैसे अपनाता है, और आप अपने भविष्य के प्रोजेक्ट्स में रेल्स और रिएक्ट दोनों को इंटीग्रेट करना कैसे शुरू कर सकते हैं।
सेटअप
पूर्वापेक्षाओं के रूप में, आपको रूबी (और रेल्स), Node.js, और Yarn के साथ सेट अप एक एनवायरनमेंट की आवश्यकता है।
आप फ्रंट पैकेजेस को मैनेज करने के लिए npm को प्राथमिकता दे सकते हैं, लेकिन हम सरलता के लिए Yarn से चिपके रहेंगे।
अपनी पसंद के फोल्डर में, निम्नलिखित कमांड चलाएं:
rails new crud-rails-react यह हमारा रेल्स प्रोजेक्ट शुरू करेगा। फिर, बने प्रोजेक्ट को अपने IDE में खोलें और सीधे Gemfile पर जाएं।
हमें एक बग ठीक करने की आवश्यकता है जो यह कमांड SQLite जेम के लिए उत्पन्न करता है। इसलिए, sqlite3 जेम का पता लगाना सुनिश्चित करें और इसे निम्नलिखित में बदलें:
gem 'sqlite3', '~> 1.3.10' यह कुछ ज्ञात एरर्स को डेटाबेस वर्जनिंग से संबंधित रोक देगा क्योंकि हम CRUD उदाहरण के लिए डिफ़ॉल्ट डेटाबेस के रूप में SQLite का उपयोग करेंगे।
चिंता न करें, हालांकि; अन्य डेटाबेस में, यह समस्या नहीं होगी।
डेटाबेस कॉन्फिग
मैं आमतौर पर चीजों को बेस से टॉप लेयर्स तक बनाना पसंद करता हूं, तो आइए डेटाबेस मॉडल्स बनाना शुरू करें।
हमें केवल एक की आवश्यकता होगी, इसलिए कार्य के लिए रेल्स स्कैफोल्डिंग से बेहतर कोई कमांड फीचर नहीं है:
rails g scaffold Beer brand:string style:string country:string quantity:integer & rake db:migrate यह मॉडल काफी बुनियादी है, इसलिए जैसे-जैसे आप आगे बढ़ें, अन्य एट्रिब्यूट्स और टाइप्स जोड़ने के लिए स्वतंत्र महसूस करें।
db/migrate फोल्डर के भीतर, " _create_beers.rb " में समाप्त होने वाले नाम के साथ एक नई फाइल है। यह वह रिकॉर्ड है जो रेल्स ने हमारे लिए एक बीयर को प्रतिबिंबित करने के लिए बनाया था।
मॉडल, बदले में, app/models फोल्डर के तहत जनरेट किया जाएगा। उन्हें वैसे ही छोड़ दें और db/seeds.rb फाइल में निम्नलिखित कोड लाइनें जोड़ें:
Beer.create(brand: 'Double Stout', style: 'Stout', country: 'England', quantity: 54) Beer.create(brand: 'Spaten', style: 'Helles', country: 'Germany', quantity: 3) Beer.create(brand: 'Newcastle', style: 'Brown ale', country: 'UK', quantity: 12) यह फाइल ऐप स्टार्ट होने पर डेटाबेस के लिए प्रारंभिक डेटा लोड स्टोर करेगी। उन्हें डेटाबेस में रिलीज करने के लिए, निम्नलिखित कमांड चलाएं:
rake db:seed बस! अब, आपकी टेबल पर कुछ बीयर हैं।
Webpacker सेटअप
Webpacker जावास्क्रिप्ट एप्लिकेशंस के लिए सबसे व्यापक रूप से उपयोग किए जाने वाले स्टेटिक मॉड्यूल बंडलर्स में से एक है। इस वजह से, यह मौजूदा एप्लिकेशन में रिएक्ट क्षमताओं को शामिल करने के लिए एकदम सही मैच है।
रेल्स भी हमें एक Webpacker बंडलर प्रदान करता है जो रेल्स के भीतर जावास्क्रिप्ट-जैसे एप्लिकेशंस को मैनेज करने के लिए पूरी तरह से अनुकूलित है।
इसे इंस्टॉल करने के लिए, अपने Gemfile में दूसरी लाइन जोड़ें, जैसा कि नीचे दिखाया गया है:
gem 'webpacker', '~> 4.3.x' बहुत बढ़िया! पूरे ऐप डेवलपमेंट के लिए हमें केवल यही जेम जोड़ने की आवश्यकता होगी। यह केवल इसलिए संभव है क्योंकि हम फ्रंट जिम्मेदारियों को Yarn को सौंप रहे हैं, जिसे इस लेख में बाद में सेट किया जाएगा।
अब, निम्नलिखित कमांड जारी करके अपडेट इंस्टॉल करने का समय है:
bundle install bundle exec rake webpacker:install bundle exec rake webpacker:install:react पहला अधिकांश रेल्स डेवलपर्स द्वारा अच्छी तरह से जाना जाता है। हम केवल Webpacker सहित सभी निर्भरताओं को इंस्टॉल कर रहे हैं।
एक बार Webpacker इंस्टॉल हो जाने पर, हम Rake के माध्यम से इसके कमांड्स का अनुकरण कर सकते हैं ताकि Webpacker निर्भरताओं के साथ-साथ रिएक्ट वालों को भी इंस्टॉल किया जा सके।
यह एक बहुत महत्वपूर्ण कदम है क्योंकि यहीं पर Webpacker सुनिश्चित करेगा कि आपकी सभी जावास्क्रिप्ट निर्भरताएं रेल्स एनवायरनमेंट के लिए ठीक से सेट हैं, इसलिए इसे छोड़ें नहीं या उन्हें सीधे चलाने की कोशिश न करें जैसा कि आप आमतौर पर npm या Yarn के साथ करते हैं, ठीक है?
जब कमांड पूरे हो जाते हैं, तो कुछ फोल्डर्स और फाइलें (जैसे node_modules और package.json) भी बनाई जाएंगी।
फ्रंट-एंड सेटअप
अब तक हमने जो सभी सेटिंग्स की हैं, वे केवल रेल्स के साथ CRUD एप्लिकेशन को काम करने के लिए पर्याप्त हैं। यदि आप rails s कमांड के माध्यम से रेल्स सर्वर शुरू करते हैं, तो यह परिणाम होगा:
रेल्स ऑटो-जेनरेटेड बीयर CRUD।
हालांकि, हम रिएक्ट के साथ अपना खुद का CRUD चाहते हैं।
सबसे पहले, आपको यह सुनिश्चित करने की आवश्यकता है कि Yarn के माध्यम से सभी आवश्यक फ्रंट-एंड निर्भरताएं कॉन्फिगर की गई हैं:
- रिएक्ट
- रिएक्ट राउटर रिएक्ट ऐप में नेविगेशन से निपटने के लिए
- रेडी-टू-यूज़ रिएक्ट कंपोनेंट्स के लिए Ant Design
Ant Design (जिसे antd के नाम से जाना जाता है) एंटरप्राइज-लेवल एप्लिकेशंस के लिए एक समृद्ध ओपन-सोर्स लाइब्रेरी है। यह अत्यधिक अनुकूलन योग्य रिएक्ट कंपोनेंट्स का एक गुच्छा प्रदान करता है जो वेब ऐप्स के विकास को बहुत सरल बनाता है।
सब कुछ इंस्टॉल करने के लिए, निम्नलिखित कमांड चलाएं:
yarn add antd react-router-dom हमें react लाइब्रेरी को स्पष्ट रूप से जोड़ने की आवश्यकता नहीं है क्योंकि react-router-dom यह करेगा।
इस बिंदु पर, जब आप package.json फाइल खोलते हैं, तो यह ऑटो-जेनरेटेड कंटेंट होगा:
{ "dependencies": { "@babel/preset-react": "^7.12.1", "@rails/webpacker": "4.3.0", "antd": "^4.7.2", "babel-plugin-transform-react-remove-prop-types": "^0.4.24", "prop-types": "^15.7.2", "react": "^17.0.0", "react-dom": "^17.0.0", "react-router-dom": "^5.2.0" }, "devDependencies": { "webpack-dev-server": "^3.11.0" } } यह रेल्स-रिएक्ट कॉम्बो के लिए सबसे बुनियादी सेटअप है। तो, हम कोड पर आगे बढ़ने के लिए तैयार हैं।
रेल्स साइड को अनुकूलित करना
फ्रंट पर आगे बढ़ने से पहले रेल्स पक्ष पर कुछ महत्वपूर्ण कार्यों को संबोधित किया जाना चाहिए।
सबसे पहले, हमें उस कंट्रोलर को परिभाषित करने की आवश्यकता है जो पेज रीडायरेक्ट को केंद्रीकृत करेगा। चूंकि हम एक सिंगल-पेज एप्लिकेशन (SPA) बना रहे हैं, केवल एक कंट्रोलर की आवश्यकता है, जो पहले से ही बनाया गया था: BeersController।
इसे app/controllers फोल्डर के तहत खोलें और इसकी सामग्री को निम्नानुसार बदलें:
class BeersController < ApplicationController def index end end चिंता न करें; हमने जो कोड हटाया है, उसे बनाए जाने वाले अगले कंट्रोलर में रखा जाएगा।
इस कंट्रोलर का एकमात्र कार्य रेल्स से रिएक्ट के लिए एक सीधा रास्ता प्रदान करना है, इसीलिए हम केवल index मेथड सेट कर रहे हैं।
इससे सीधे कनेक्ट करने के लिए, आइए config फोल्डर के तहत routes.rb फाइल खोलें और इसकी सामग्री को निम्नानुसार बदलें:
Rails.application.routes.draw do root 'beers#index' # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html end नए root कॉन्फिग पर ध्यान दें। हां, हम रूट के एंडपॉइंट को beers इंडेक्स मेथड पर मैप कर रहे हैं।
आपको app/views/beers फोल्डर के भीतर index.html.erb फाइल को भी खाली करना होगा क्योंकि हम रेल्स वेब कंटेंट को रेंडर नहीं करना चाहते हैं। यह एक ट्रिक है जिसका उपयोग हम रेल्स को केवल रिएक्ट कोड प्रदर्शित करने के लिए मजबूर करने के लिए कर सकते हैं।
बीयर API
अब, बीयर API निर्माण पर चलते हैं। API संरचना व्यावहारिक रूप से BeersController के समान होगी लेकिन कुछ मामूली बदलावों के साथ। इसे बनाने के लिए, निम्नलिखित कमांड चलाएं:
rails generate controller api/v1/Beers इसे वर्जनिंग सिस्टम का उपयोग करके बनाना सुनिश्चित करें। यह आपको भविष्य में अपने API को विकसित करने और इसे सामान्य कंट्रोलर्स से अलग करने की अनुमति देगा।
अब, app/controllers/api/v1/beers_controller.rb फाइल खोलें और कोड को निम्नलिखित से बदलें:
class Api::V1::BeersController < ApplicationController before_action :set_beer, only: [:show, :edit, :update, :destroy] # GET /beers # GET /beers.json def index @beers = Beer.all.order(brand: :asc) render json: @beers end # GET /beers/1 # GET /beers/1.json def show if @beer render json: @beer else render json: @beer.errors end end # GET /beers/new def new @beer = Beer.new end # GET /beers/1/edit def edit end # POST /beers # POST /beers.json def create @beer = Beer.new(beer_params) if @beer.save render json: @beer else render json: @beer.errors end end # PATCH/PUT /beers/1 # PATCH/PUT /beers/1.json def update end # DELETE /beers/1 # DELETE /beers/1.json def destroy @beer.destroy render json: { notice: 'Beer was successfully removed.' } end private # Use callbacks to share common setup or constraints between actions. def set_beer @beer = Beer.find(params[:id]) end # Only allow a list of trusted parameters through. def beer_params params.permit(:brand, :style, :country, :quantity) end end अधिकांश ऑपरेशंस पिछले कंट्रोलर से रीसायकल किए गए थे।
before_action स्निपेट रिक्वेस्ट्स के भीतर id पैरामीटर के अनुसार उचित बीयर ऑब्जेक्ट को पुनर्प्राप्त करने का ध्यान रखेगा। केवल :only क्लॉज के बाद सरणी में रखे गए ऑपरेशंस को इस ऑटो-रिकवरी फीचर की आवश्यकता होगी।
बाकी मेथड्स CRUD के प्रत्येक ऑपरेशन के बराबर हैं। बस हमेशा अपनी रिक्वेस्ट्स के रिस्पॉन्स के रूप में JSON वापस करना याद रखें क्योंकि यही वह फॉर्मेट है जिसका उपयोग हम अपने रिएक्ट कंपोनेंट्स के भीतर करेंगे।
अंत में, आपको नए बनाए गए रूट्स को शामिल करने के लिए config/routes.rb को फिर से अनुकूलित करना होगा। तो, फाइल की सामग्री को निम्नानुसार बदलना सुनिश्चित करें:
Rails.application.routes.draw do namespace :api do namespace :v1 do get 'beers/index' post 'beers/create' delete 'beers/:id', to: 'beers#destroy' end end root 'beers#index' # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html end डिस्ट्रॉय रूट दर्शाता है कि destroy मेथड पर कैसे मैप किया जाए यदि यह पथ में स्पष्ट रूप से सेट नहीं है।
रिएक्ट कंपोनेंट्स
रिएक्ट कंपोनेंट्स के माध्यम से काम करता है, जो वेब एप्लिकेशन के बिल्डिंग ब्लॉक्स की तरह कार्य करते हैं। प्रत्येक एक या अधिक कार्य करता है जो संदर्भ के रूप में समझ में आता है।
संक्षेप में, एक कंपोनेंट एक जावास्क्रिप्ट क्लास या फंक्शन से बना होता है जो पैरामीटर्स के रूप में प्रॉपर्टीज प्राप्त कर सकता है, इसके भीतर बिजनेस लॉजिक को प्रोसेस कर सकता है, और अंत में, स्क्रीन के एक हिस्से का प्रतिनिधित्व करने वाला एक फंक्शनल UI कंपोनेंट वापस कर सकता है।
अगले उदाहरणों से निकाले गए निम्नलिखित कोड स्निपेट को लें:
<Layout> <Header /> <Content>...</Content> <Footer>Honeybadger ©2020.</Footer> </Layout> हां, रिएक्ट कंपोनेंट्स JSX (जावास्क्रिप्ट XML) नामक एक कस्टम मार्कअप भाषा का उपयोग करते हैं जो HTML से बहुत मिलता-जुलता है। सभी HTML टैग JSX फाइलों के भीतर भी उपलब्ध हैं। आप JSX पर यहां और पढ़ सकते हैं।
उपरोक्त उदाहरण दर्शाता है कि antd एक संरचनात्मक लेआउट कंपोनेंट के साथ कैसे डील करता है। कंपोनेंट्स अन्य कंपोनेंट्स से बने होते हैं और एक दूसरे के ऊपर स्टैक्ड होते हैं, एक संपूर्ण रचना करते हैं। कुछ प्रॉपर्टीज (वैकल्पिक या नहीं) प्राप्त करते हैं, और कुछ में आंतरिक सामग्री हो सकती है।
कंपोनेंट्स क्लास- या फंक्शन-आधारित हो सकते हैं।
क्लास कंपोनेंट्स
क्लास-आधारित कंपोनेंट्स सामान्य जावास्क्रिप्ट क्लासेस के रूप में बनाए जाते हैं, जैसे नीचे दिखाया गया:
class Beers extends React.Component {} वे React.Component क्लास से इनहेरिट करते हैं, उनका एक जीवनचक्र होता है, और इनिशियलाइज़ेशन, रेंडरिंग, और नष्ट होने के चरणों से पहले कोड निष्पादित करने के लिए उपयोगिता मेथड्स प्रदान करते हैं।
हालांकि, सबसे महत्वपूर्ण (और आवश्यक) मेथड render() है, जिसे हर बार कंपोनेंट अपडेट होने पर कॉल किया जाता है।
फंक्शनल कंपोनेंट्स
फंक्शनल कंपोनेंट्स ES6 के एरो फंक्शन का उपयोग करते हैं और सिंटैक्स और जटिलता के संदर्भ में रिएक्ट कंपोनेंट्स को सरल बनाते हैं।
उपरोक्त समान Beer कंपोनेंट को एक फंक्शन में निम्नानुसार दर्शाया जाएगा:
const Beers = () => <div>My Beers</div>; यह बहुत सरल है, है ना?
रिएक्ट सेट अप करना
हम पहले ही रेल्स इंडेक्स पेज को खाली कर चुके हैं। अब, हम रेल्स को बताएंगे कि इसे रिएक्ट को अपना डिफ़ॉल्ट फ्रंट-एंड बनाने की आवश्यकता है।
इसे प्राप्त करने के लिए, आपको अपने app/views/layouts/application.html.erb फाइल के <head> टैग में निम्नलिखित कोड लाइन जोड़ने की आवश्यकता है:
<%= javascript_pack_tag 'index' %> यह हमारे एप्लिकेशन हेडर में जावास्क्रिप्ट पैक जोड़ देगा, जिससे सभी जावास्क्रिप्ट फाइलें, रिएक्ट वालों सहित, इंडेक्स पेज के भीतर निष्पादित होंगी।
हमें यह सुनिश्चित करने की आवश्यकता है कि index.jsx फाइल का वही नाम हो क्योंकि यह इम्पोर्ट पैक पर इंगित करता है।
इस उद्देश्य के लिए, आइए ऑटोजेनरेटेड app/javascript/packs/hello_react.jsx फाइल का नाम बदलकर index.jsx कर दें।
फिर, कोड को निम्नलिखित से बदलें:
import React from "react"; import { render } from "react-dom"; import App from "../components/App"; document.addEventListener("DOMContentLoaded", () => { render(<App />, document.body.appendChild(document.createElement("div"))); }); इस फाइल को रिएक्ट एप्लिकेशन फाइल समझने की गलती न करें, क्योंकि यह केवल वह फाइल है जो ReactDOM के render फंक्शन के माध्यम से पूरे रिएक्ट ऐप हायरार्की को DOM में लोड करेगी।
आमतौर पर, प्रत्येक रिएक्ट एप्लिकेशन एक index.js फाइल से शुरू होता है जो रिएक्ट सहित आवश्यक सभी चीजों को लोड करता है।
App टैग हमारे हायरार्की के टॉप कंपोनेंट को मैप करता है। तो, आइए इसे javascript/components फोल्डर के तहत index.jsx के रूप में बनाएं (यदि वे अभी तक मौजूद नहीं हैं तो मैन्युअल रूप से फोल्डर्स बनाएं) और इसमें निम्नलिखित कोड रखें:
import React from "react"; import Routes from "../routes/index"; import "antd/dist/antd.css"; export default () => <>{Routes}</>; वैकल्पिक रूप से, आप index.jsx के भीतर antd CSS फाइल इम्पोर्ट कर सकते हैं। कोई भी दृष्टिकोण काम करेगा।
रूट्स की सूची routes फोल्डर के तहत रखी गई है। उन्हें रिएक्ट राउटर लाइब्रेरी से निकाला गया है, जो हमारे लिए अधिकांश कठिन काम करता है। यह इसकी सामग्री है:
import React from "react"; import { BrowserRouter as Router, Route, Switch } from "react-router-dom"; import Home from "../components/Home"; export default ( <Router> <Switch> <Route path="/" exact component={Home} /> </Switch> </Router> ); आपके प्रत्येक रूट को एक अलग Route टैग के भीतर मैप किया जाना चाहिए। path पैरामीटर को प्रत्येक रूट एंडपॉइंट के URI से मेल खाना चाहिए, जबकि component पैरामीटर उस कंपोनेंट को इंगित करता है जिस पर रिएक्ट राउटर को रिक्वेस्ट को रीडायरेक्ट करना चाहिए।
याद रखें कि हमारे SPA के लिए हमारे पास एक सिंगल रूट होगा। आप यहां अन्य पथ भी जोड़ सकते हैं यदि आप उदाहरण के लिए बीयर्स की सूची के लिए /beers मैप करना चाहते हैं, लेकिन हम इसे सरल रखेंगे।
यह भी ध्यान दें कि हम यहां Home कंपोनेंट इम्पोर्ट कर रहे हैं, जो अभी तक मौजूद नहीं है। तो, आइए इसे components फोल्डर के तहत Home.jsx के रूप में बनाएं। फिर, इसमें निम्नलिखित कोड जोड़ें:
import { Layout } from "antd"; import React from "react"; import Beers from "./Beers"; import Header from "./Header"; const { Content, Footer } = Layout; export default () => ( <Layout className="layout"> <Header /> <Content style={{ padding: "0 50px" }}> <div className="site-layout-content" style={{ margin: "100px auto" }}> <h1>Beer Catalog</h1> <Beers /> </div> </Content> <Footer style={{ textAlign: "center" }}>Honeybadger ©2020.</Footer> </Layout> ); जब रिएक्ट कंपोनेंट्स की बात आती है, तो मैं उन्हें टॉप से बॉटम तक बनाना पसंद करता हूं। इस तरह, आप ऐप को एक पूरे के रूप में काम करने के लिए आवश्यक सभी आवश्यक कंपोनेंट्स पर समग्र नज़र रख सकते हैं।
Home कंपोनेंट एक असेंबलर की तरह व्यवहार करता है; यह ऐप के अन्य सभी कंपोनेंट टुकड़ों को समायोजित करता है, जैसे Layout, Header, Content, और Footer।
इनमें से प्रत्येक टुकड़े के स्रोत को ठीक से अलग करना महत्वपूर्ण है। Antd आपके स्क्रीन के हिस्सों की संरचना बनाने के लिए Layout, Content, और Footer जैसे तैयार कंपोनेंट्स का एक गुच्छा प्रदान करता है।
वे मुख्य रूप से पेज भागों की संरचना पर केंद्रित हैं, लेकिन कुछ बिल्ट-इन CSS स्टाइल्स भी प्रदान करते हैं, जो हमें बेहतर लुक के साथ लाभान्वित करेंगे।
हेडर कंपोनेंट
javascript/components फोल्डर के भीतर भी बनाई जाने वाली Header.jsx कंपोनेंट फाइल, हेडर की सामग्री को बनाए रखेगी। इसमें एक साधारण antd मेनू और Honeybadger लोगो के साथ एक div शामिल है, जैसा कि नीचे दिखाया गया है:
Antd मेनू आइटम्स।
नीचे, आप Header.jsx में रखने के लिए कोड पा सकते हैं:
import React from "react"; import { Layout, Menu } from "antd"; const { Header } = Layout; export default () => ( <Header> <div className="logo" /> <Menu theme="dark" mode="horizontal" defaultSelectedKeys={["1"]}> <Menu.Item key="1">Home</Menu.Item> <Menu.Item key="2">Our Services</Menu.Item> <Menu.Item key="3">Contact</Menu.Item> </Menu> </Header> ); Antd मेनू कंपोनेंट उपयोग करने में काफी सरल है लेकिन उपलब्ध अनुकूलन विकल्पों के संदर्भ में व्यापक है, जो हमें नेविगेशन ड्रॉअर्स, ड्रॉप-डाउन्स, ग्रुप्स, और सबग्रुप्स बनाने की अनुमति देता है।
ध्यान दें कि हम defaultSelectedKeys प्रदान कर रहे हैं, एक सरणी जो मेनू को बताती है कि कौन से आइटम सक्रिय हैं।
हमारे मेनू कहीं भी नेविगेट नहीं करेंगे; वे केवल लुक-एंड-फील को पूरा करने के लिए स्क्रीन पर कब्जा करेंगे। तो, आइए Beers कंपोनेंट पर आगे बढ़ें।
बीयर्स कंपोनेंट
यह कंपोनेंट बीयर्स की लिस्टिंग पर केंद्रित है, और टेबल के भीतर उपलब्ध एक्शन्स, जैसे डिलीशन, डेटा पेजिनेशन, और टेबल रीलोडिंग।
रिएक्ट एक्शन्स और कंपोनेंट्स का विजुअल प्रतिनिधित्व।
ऊपर की छवि पर एक नज़र डालें। हमने कंपोनेंट्स और एक्शन्स को निचले स्तर पर तोड़ दिया है, ताकि आप बेहतर ढंग से समझ सकें कि यहां क्या किया जाएगा।
रिएक्ट स्टेट
रिएक्ट कंपोनेंट्स एक state ऑब्जेक्ट के साथ बनाए जाते हैं। यह ऑब्जेक्ट दिए गए कंपोनेंट से सीधे जुड़ा एक स्टोर के रूप में कार्य करता है। प्रत्येक कंपोनेंट का अपना state ऑब्जेक्ट होता है, और हर बार जब आप स्टेट बदलते हैं, तो कंपोनेंट री-रेंडर हो जाता है।
हमारे Beers कंपोनेंट का पहला एक्शन टेबल पर लिस्टिंग प्रदर्शित करना है। इस उद्देश्य के लिए, हमें इस सूची को एक सरणी में रखने की आवश्यकता होगी:
state = { beers: [], }; बीयर्स लिस्टिंग
इस सरणी को भरने के लिए, हमें पहले बनाए गए API कंट्रोलर से सूची पुनः प्राप्त करने की आवश्यकता होगी। उस फंक्शन की समीक्षा करें जो इसे पुनः प्राप्त करेगा:
loadBeers = () => { const url = "api/v1/beers/index"; fetch(url) .then((data) => { if (data.ok) { return data.json(); } throw new Error("Network error."); }) .then((data) => { data.forEach((beer) => { const newEl = { key: beer.id, id: beer.id, brand: beer.brand, style: beer.style, country: beer.country, quantity: beer.quantity, }; this.setState((prevState) => ({ beers: [...prevState.beers, newEl], })); }); }) .catch((err) => message.error("Error: " + err)); }; सरलता के लिए, हम हर बार API से डेटा रिक्वेस्ट करने की आवश्यकता होने पर सभी आधुनिक ब्राउज़रों के लिए उपलब्ध Fetch API का उपयोग करेंगे।
उपरोक्त फंक्शन API से बीयर्स की सरणी को पुनः प्राप्त करने के लिए कुछ चरण लेता है:
- यह पहले /index एंडपॉइंट को असिंक्रोनसली रिक्वेस्ट करता है और
thenचेक करता है कि रिस्पॉन्स स्टेटस OK के बराबर है या नहीं। - यदि ऐसा है, तो हम डेटा को JSON के रूप में वापस करते हैं; अन्यथा, आइए एक
Errorफेंकें। Then, हम परिणामों की सरणी पर पुनरावृति करते हैं ताकि अपना खुद का बीयर ऑब्जेक्ट बना सकें और इसे स्टेट केbeersसरणी में जोड़ सकें।- यदि प्रक्रिया के दौरान कुछ गलत हुआ, तो
catchब्लॉक अपवाद को पकड़ लेगा और इसे मैसेज अलर्ट के रूप में प्रदर्शित करेगा।
अच्छा, है ना? यह लगभग वही चरण हैं जो हम अन्य सभी रिक्वेस्ट्स के लिए लेंगे।
लेकिन, antd डेटा को टेबल में कैसे प्रदर्शित करता है? अच्छा सवाल! आइए निम्नलिखित कोड पर एक नज़र डालें:
columns = [ { title: "Brand", dataIndex: "brand", key: "brand", }, ...{ title: "", key: "action", render: (_text, record) => ( <Popconfirm title="Are you sure to delete this beer?" onConfirm={() => this.deleteBeer(record.id)} okText="Yes" cancelText="No"> <a href="#" type="danger"> Delete{" "} </a> </Popconfirm> ), }, ]; मैंने इसे बेहतर समझ के लिए थोड़ा सरल बनाया है। यह एक सरणी है जो हमारी टेबल के कंकाल का प्रतिनिधित्व करती है। इस तरह antd टेबल काम करते हैं; उन्हें आपके टेबल संरचना (पंक्तियों और कॉलम) के बारे में मेटाडेटा जानकारी को एक सरणी के रूप में प्राप्त करने की आवश्यकता होती है।
सरणी के भीतर प्रत्येक कॉलम एक ऑब्जेक्ट है, और क्रम यहां महत्वपूर्ण है। title एट्रिब्यूट कॉलम का नाम प्राप्त करता है, जबकि dataIndex नाम यह है कि इसे रिएक्ट कंपोनेंट्स के भीतर कैसे जाना जाएगा, और key इसका यूनिक आइडेंटिफायर है।
अधिकांश कॉलम के लिए, कॉन्फिगरेशन समान है, सिवाय एक्शन कॉलम के। वहां, हमें उस एक्शन के लिंक को निर्दिष्ट करने की आवश्यकता है जब यूजर किसी आइटम को हटाना चाहता है। ध्यान दें कि हम antd के Popconfirm कंपोनेंट का उपयोग कर रहे हैं।
यह एक बहुत अच्छा कंपोनेंट है जो यूजर्स को कार्रवाई होने से पहले उसकी पुष्टि करने के लिए प्रेरित करने के काम को सुविधाजनक बनाता है। नीचे की छवि दर्शाती है कि यह कैसा दिखता है:
डिलीशन से पहले कन्फर्म डायलॉग प्रॉम्प्ट करना।
डिलीट एक्शन
एक आइटम को हटाने के लिए, हमें दो मुख्य ऑपरेशन करने की आवश्यकता होगी: API पर डिलीशन कॉल और टेबल रीलोडिंग।
डिलीट फंक्शन हमारे द्वारा किए गए पहले फेच के समान है:
deleteBeer = (id) => { const url = `api/v1/beers/${id}`; fetch(url, { method: "delete", }) .then((data) => { if (data.ok) { this.reloadBeers(); return data.json(); } throw new Error("Network error."); }) .catch((err) => message.error("Error: " + err)); }; देखें? यहां केवल नई चीज HTTP method है जिसे fetch मेथड के दूसरे पैरामीटर के रूप में पास किया गया है। साथ ही, then क्लॉज के भीतर, हम reloadBeers फंक्शन को कॉल करते हैं, जो एक बार फिर बैक-एंड से सभी बीयर्स को पुनः प्राप्त करेगा।
इस फंक्शन की सामग्री बहुत अधिक निम्नलिखित है:
reloadBeers = () => { this.setState({ beers: [] }); this.loadBeers(); }; हम स्टेट के beers सरणी को रीसेट कर रहे हैं और लोड फंक्शन को फिर से कॉल कर रहे हैं।
अंतिम कंपोनेंट
अंत में, हमें antd टैग्स को स्पष्ट रूप से कॉल करके कंपोनेंट को कंपोज करने की आवश्यकता है। आइए देखें कि यह अंतिम कंपोनेंट कोड के साथ कैसे चलता है:
import { Table, message, Popconfirm } from "antd"; import React from "react"; import AddBeerModal from "./AddBeerModal"; class Beers extends React.Component { columns = [ { title: "Brand", dataIndex: "brand", key: "brand", }, { title: "Style", dataIndex: "style", key: "style", }, { title: "Country", dataIndex: "country", key: "country", }, { title: "Quantity", dataIndex: "quantity", key: "quantity", }, { title: "", key: "action", render: (_text, record) => ( <Popconfirm title="Are you sure to delete this beer?" onConfirm={() => this.deleteBeer(record.id)} okText="Yes" cancelText="No"> <a href="#" type="danger"> Delete{" "} </a> </Popconfirm> ), }, ]; state = { beers: [], }; componentDidMount() { this.loadBeers(); } loadBeers = () => { const url = "api/v1/beers/index"; fetch(url) .then((data) => { if (data.ok) { return data.json(); } throw new Error("Network error."); }) .then((data) => { data.forEach((beer) => { const newEl = { key: beer.id, id: beer.id, brand: beer.brand, style: beer.style, country: beer.country, quantity: beer.quantity, }; this.setState((prevState) => ({ beers: [...prevState.beers, newEl], })); }); }) .catch((err) => message.error("Error: " + err)); }; reloadBeers = () => { this.setState({ beers: [] }); this.loadBeers(); }; deleteBeer = (id) => { const url = `api/v1/beers/${id}`; fetch(url, { method: "delete", }) .then((data) => { if (data.ok) { this.reloadBeers(); return data.json(); } throw new Error("Network error."); }) .catch((err) => message.error("Error: " + err)); }; render() { return ( <> <Table className="table-striped-rows" dataSource={this.state.beers} columns={this.columns} pagination={{ pageSize: 5 }} /> <AddBeerModal reloadBeers={this.reloadBeers} /> </> ); } } export default Beers; अब, आप सब कुछ एक साथ देख सकते हैं। रेंडर फंक्शन वहां इम्पोर्ट किए गए दो टैग्स को प्रदर्शित करेगा: antd का Table कंपोनेंट और AddBeerModal (मोडल फॉर्म जिसे हम कुछ मिनटों में बनाएंगे)।
टेबल कंपोनेंट इस मायने में बहुत समृद्ध है कि यह हमें pagination ऑब्जेक्ट सेट करके परिणामों के माध्यम से स्वचालित रूप से पेजिनेट करने की अनुमति देता है। यहां हम केवल एक प्रॉपर्टी जोड़ रहे हैं जो प्रत्येक पेज का आकार है (प्रति पेज 5 परिणाम)।
dataSource एट्रिब्यूट बैक-एंड से माउंट की गई बीयर्स की सूची प्राप्त करता है, और columns एट्रिब्यूट वह मेटाडेटा प्राप्त करता है जिसे हमने पहले ही बनाया है।
AddBeerModal कंपोनेंट
टेबल के नीचे, आप नई बीयर्स जोड़ने के लिए एक बटन देख सकते हैं। जब हम इस बटन पर क्लिक करते हैं, तो यह हमारे कैटलॉग में नई बीयर्स रजिस्टर करने के लिए एक फॉर्म के साथ एक मोडल खोलेगा, जैसा कि आप नीचे देख सकते हैं:
कैटलॉग में नई बीयर्स जोड़ना।
यह यह पता लगाने का एक शानदार तरीका है कि antd फॉर्म्स को कैसे संभालता है।
सबसे पहले, आइए इस कंपोनेंट में हमारे पास मौजूद एक्शन्स को तोड़ दें। ध्यान दें कि कंपोनेंट स्वयं दो से बना है: एक बटन और एक मोडल।
इसका मतलब है कि हमें उन दोनों से संबंधित ऑपरेशन्स को मैप करना होगा:
showModalऔरhandleCancelमोडल के खुलने और बंद होने से डील करते हैं।onFinishतब ट्रिगर होता है जब हम फॉर्म सबमिट करते हैं।
वे कंपोनेंट के स्टेट के साथ खेलेंगे, जो केवल मोडल टॉगल को स्टोर करेगा (यानी, यह विजिबल है या नहीं):
state = { visible: false, }; मोडल को दिखाने या छिपाने के लिए, हमें बस इस बूलियन को टॉगल करना होगा:
this.setState({ visible: true, }); बीयर के API को कॉल करके एक नई बीयर रजिस्टर करने के लिए, हमें फिर से Fetch API का उपयोग करना होगा:
onFinish = (values) => { const url = "api/v1/beers/"; fetch(url, { method: "post", headers: { "Content-Type": "application/json", }, body: JSON.stringify(values), }) .then((data) => { if (data.ok) { this.handleCancel(); return data.json(); } throw new Error("Network error."); }) .then(() => { this.props.reloadBeers(); }) .catch((err) => console.error("Error: " + err)); }; यह पहली बार है जब हम एक रिक्वेस्ट कॉल कर रहे हैं जिसमें हम सर्वर को डेटा भेजते हैं। इस मामले में, हमें API को स्पष्ट रूप से यह भी बताना होगा कि किस प्रकार की जानकारी जा रही है। इसलिए headers एट्रिब्यूट को सूचित किया जाना चाहिए।
यदि सब कुछ ठीक रहा, तो हम बस मोडल को बंद करते हैं और टेबल की लिस्टिंग को रीलोड करते हैं।
अब, कंपोनेंट रेंडर के साथ सब कुछ एक साथ देखते हैं:
import { Button, Form, Input, Modal, Select } from "antd"; import React from "react"; const { Option } = Select; class AddBeerModal extends React.Component { formRef = React.createRef(); state = { visible: false, }; onFinish = (values) => { const url = "api/v1/beers/"; fetch(url, { method: "post", headers: { "Content-Type": "application/json", }, body: JSON.stringify(values), }) .then((data) => { if (data.ok) { this.handleCancel(); return data.json(); } throw new Error("Network error."); }) .then(() => { this.props.reloadBeers(); }) .catch((err) => console.error("Error: " + err)); }; showModal = () => { this.setState({ visible: true, }); }; handleCancel = () => { this.setState({ visible: false, }); }; render() { return ( <> <Button type="primary" onClick={this.showModal}> Create New + </Button> <Modal title="Add New Beer ..." visible={this.state.visible} onCancel={this.handleCancel} footer={null}> <Form ref={this.formRef} layout="vertical" onFinish={this.onFinish}> <Form.Item name="brand" label="Brand" rules={[{ required: true, message: "Please input your beer brand!" }]}> <Input placeholder="Input your beer brand" /> </Form.Item> <Form.Item name="style" label="Style" rules={[{ required: true, message: "Please input your beer style!" }]}> <Input placeholder="Input your beer style" /> </Form.Item> <Form.Item name="country" label="Country" rules={[ { required: true, message: "Please input the country of the beer!", }, ]} > <Select showSearch placeholder="Select your beer country" optionFilterProp="children" style={{ width: "100%" }}> <Option value="Finland">Finland</Option> <Option value="Germany">Germany</Option> <Option value="Netherlands">Netherlands</Option> <Option value="UK">UK</Option> <Option value="USA">USA</Option> <Option value="Other">Other</Option> </Select> </Form.Item> <Form.Item name="quantity" label="Quantity" rules={[{ required: true, message: "Please input the quantity!" }]}> <Input type="number" placeholder="How many beers you desire?" /> </Form.Item> <Form.Item> <Button type="primary" htmlType="submit"> Submit </Button> </Form.Item> </Form> </Modal> </> ); } } export default AddBeerModal; Antd हमें प्रत्येक फॉर्म के आइटम नियमों को व्यक्तिगत रूप से निर्दिष्ट करने की अनुमति देता है। यदि कोई फील्ड आवश्यक है, तो बस rules एट्रिब्यूट प्रदान करके ऐसा कहें। आप उस संदेश को कस्टमाइज़ कर सकते हैं जो यह तब प्रदर्शित करेगा जब यूजर फॉर्म को ठीक से भरे बिना सबमिट करता है:
फॉर्म इनपुट्स को मान्य करना।
Select कंपोनेंट पर एक नज़र डालें, जो एक कॉम्बो बॉक्स का अनुवाद करता है। देखें कि सही एट्रिब्यूट्स प्रदान करके जटिल कंपोनेंट्स बनाना कितना आसान है। उदाहरण के लिए, यदि आप अपने सेलेक्ट को खोजने योग्य बनाना चाहते हैं, तो बस showSearch प्रॉपर्टी डालें, और यह हो गया:
एक Select के भीतर परिणामों को फ़िल्टर करना।
Antd आपके इनपुट के आधार पर सेलेक्ट विकल्पों को स्वचालित रूप से फ़िल्टर करेगा।
स्टाइलिंग
कभी-कभी, आपको उन कंपोनेंट्स को कुछ CSS स्टाइलिंग प्रदान करने की आवश्यकता होगी जो डिफ़ॉल्ट प्रदान नहीं करते (जैसे antd की टेबल) या उन लोगों को कस्टमाइज़ करें जो बिल्ट-इन आते हैं।
ऐसा करने के लिए, आप जितनी चाहें उतनी CSS फाइलें बना सकते हैं और उन्हें एक संरचना में व्यवस्थित कर सकते हैं जो आपको पसंद हो। रेल्स पहले से ही application.css फाइल बनाता है, app/assets/stylesheets फोल्डर के तहत। इसे खोलें और निम्नलिखित सामग्री जोड़ें:
.site-layout-content { background: #fff; padding: 24px; min-height: 380px; } .logo { width: 200px; min-height: 31px; margin: 16px 24px 16px 0; float: left; background-image: url(https://www.honeybadger.io/images/navbar_logo.svg?1602785015); background-repeat: no-repeat; } .table-striped-rows th, .table-striped-rows td { border-bottom: 1px solid #dedddd !important; } .table-striped-rows tr:nth-child(2n) td { background-color: #fbfbfb; } .table-striped-rows thead { background-color: #f1f1f1; } ये हमारी टेबल को स्ट्रिप्ड बनाने के लिए CSS नियम हैं, उदाहरण के लिए। यहां जितनी चाहें उतनी अतिरिक्त स्टाइल्स जोड़ने के लिए स्वतंत्र महसूस करें।
टेस्टिंग
टेस्ट पर जाने से पहले, हमें CSRF टोकन चेकिंग को अक्षम करने की आवश्यकता है जो रेल्स स्वचालित रूप से हमारे ऐप्स के लिए कॉन्फिगर करता है। ऐसा करने के लिए, app/controllers/application_controller.rb फाइल पर जाएं और इसे निम्नानुसार बदलें:
class ApplicationController < ActionController::Base protect_from_forgery with: :null_session end इस तरह, हमें हर बार रिक्वेस्ट करते समय टोकन्स को मान्य करने की आवश्यकता नहीं होगी।
बहुत बढ़िया! अब, rails s कमांड के माध्यम से अपना सर्वर शुरू करें, https://localhost:3000/ एड्रेस तक पहुंचें, और CRUD के साथ खेलें।
निष्कर्ष
होमवर्क टास्क के रूप में, मैं अनुशंसा करूंगा कि आप CRUD की अपडेट फंक्शनलिटी को लागू करने का प्रयास करें। आप API कंट्रोलर पर edit मेथड को अपडेटेड बीयर इन्फो प्राप्त करने और डेटाबेस पर अपडेट करने के लिए अनुकूलित कर सकते हैं। व्यू के लिए, एक और मोडल एडिट के फॉर्म को समायोजित करने के लिए बहुत उपयुक्त होगा।
आप इस ट्यूटोरियल के लिए सोर्स कोड यहां पा सकते हैं। अच्छे अध्ययन!