با استفاده از رویکرد ایجاد فرم داینامیک، قصد داریم یک کامپوننت form جدید برای post action ها و همچنین component های modal را برای نمایش پیغامهای موفقیت آمیز و خطا ایجاد کنیم. قصد داریم از این component های modal در هر component والدی که به آنها نیاز دارند، استفاده نماییم (reuse). علاوه بر این، قصد داریم تمام فیلدهای input از فایل config را به صورت داینامیک ایجاد کنیم، زیرا ما میخواهیم کد ما قابلیت استفاده مجدد (reusable) را داشته باشد. قسمت reusability یکی از عالیترین مزیتهای رویکرد ایجاد form به صورت داینامیک است.
پس بریم که شروع کنیم.
اگر میخواهید تمام دستورالعملهای پایه و راهنمای کامل برای آموزش سریالی NET Core. را ببینید، این لینک را بررسی کنید: دستورالعملهای کار با NET Core.
این مقاله، قسمتی از مجموعه آموزشی زیر میباشد:
- آماده سازی پروژه و ایجاد component ها
- Navigation و مسیریابی (Routing)
- Axios ،HTTP و Redux
- Lazy Loading و HOC Component
- مدیریت خطا و component های اضافی
- ایجاد form داینامیک و component های modal
- اعتبارسنجی Form و Handle کردن درخواست Post
- مدیریت درخواست PUT
- مدیریت درخواست DELETE
اگر میخواهید تمام دستورالعملهای پایه و راهنمای کامل برای آموزش سریالی React را ببینید، به این لینک مراجعه کنید: مقدمه معرفی آموزش سریالی React
برای بررسی قسمت قبل، به این لینک مراجعه کنید: مدیریت خطا در React
جهت دانلود سورس بر روی این لینک کلیک کنید: سری آموزشی React – قسمت 6
این مقاله به قسمتهای زیر تقسیم میشود:
- Component های Success Modal و Error Modal
- ایجاد پیکربندی Input
- ایجاد عناصر Input به صورت داینامیک
- کامپوننت CreateOwner
- تکمیل CreateOwner View
- نتیجه گیری
Component های پنجره Success Modal و Error Modal
با ایجاد ساختار پوشه ای زیر در پوشه component
، کار خود را شروع میکنیم:
اول از همه قصد داریم فایل SuccessModal.js را اصلاح کنیم:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
import React from 'react'; import Aux from '../../../hoc/Auxiliary/Auxiliary'; import { Modal, Button } from 'react-bootstrap'; import '../ModalStyles.css'; const successModal = (props) => { return ( <Aux> <Modal show={props.show} backdrop='static'> <Modal.Header> {props.modalHeaderText} </Modal.Header> <Modal.Body> <p>{props.modalBodyText}</p> </Modal.Body> <Modal.Footer> <Button bsStyle="success" onClick={props.successClick}>{props.okButtonText}</Button> </Modal.Footer> </Modal> </Aux> ) } export default successModal; |
این کد کاملا واضح است. ما اینجا داریم داخل این کامپوننت functional، از کامپوننتهای react-bootstrap برای modal استفاده میکنیم. ما از طریق آبجکت props
، پارامترهای مختلف و همچنین یک event را به modal خود ارسال میکنیم. این event قرار است زمانی که ما بر روی button کلیک میکنیم، modal را ببندد.
حالا فایل ErrorModal.js را به همین شیوه اصلاح میکنیم:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
import React from 'react'; import { Modal, Button } from 'react-bootstrap'; import Aux from '../../../hoc/Auxiliary/Auxiliary'; import '../ModalStyles.css'; const errorModal = (props) => { return ( <Aux> <Modal show={props.show} backdrop='static'> <Modal.Header> {props.modalHeaderText} </Modal.Header> <Modal.Body> <p>{props.modalBodyText}</p> </Modal.Body> <Modal.Footer> <Button bsStyle="danger" onClick={props.closeModal}>{props.okButtonText}</Button> </Modal.Footer> </Modal> </Aux> ) } export default errorModal; |
در آخر باید فایل ModalStyles.css را اصلاح کنیم:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
.modal-header { margin: 0; line-height: 1.42857143; font-size: 30px; text-align: center; } .modal-body p { text-align: center; margin-top: 10px; } @media (min-width: 768px){ .modal-dialog { width: 500px !important; margin: 20% auto !important; } } |
عالی.
حالا ما component های modal خود را داریم که میتوانیم از آنها برای component های Create، Update یا هر component دیگری استفاده کنیم. ما عمداً دو کامپوننت modal ایجاد کردهایم، حتی اگرچه آنها تقریباً یکسان هستند، اما زمانیکه از کامپوننتهای modal در داخل component های والد استفاده میکنیم خودشان را بیشتر نشان میدهند. فقط با نگاه کردن به نام modal می توانید فوراً تشخیص دهید که هدف modal چیست.
ایجاد تنظیمات Input
داخل پوشه src
، قصد داریم پوشه Utility
و سپس داخل آن یک فایل جدید به نام InputConfiguration.js را ایجاد کنیم. در این فایل، ما قصد داریم تمام تنظیمات پیکربندی خود برای عناصر (name, address, dateOfBirth) input که قرار است در form های create و update مورد استفاده قرار بگیرند را ذخیره کنیم.
ما قبلا کتابخانه moment را نصب کرده ایم. در پست قبلی، اگر هنوز آن را نصب نکرده اید، لطفا با استفاده از دستور زیر آن را نصب کنید (ما به آن برای کنترل datepicker نیاز داریم):
1 |
npm install --save moment |
حالا فایل InputConfiguration.js را اصلاح میکنیم:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
import moment from 'moment'; export const returnInputConfiguration = () => { return { name: { element: 'input', type: 'text', value: '', validation: { required: true }, valid: false, touched: false, errorMessage: '', label: 'Name:' }, address: { element: 'input', type: 'text', value: '', validation: { required: true, maxLength: 60 }, valid: false, touched: false, errorMessage: '', label: 'Address:' }, dateOfBirth: { element: 'datePicker', type: 'text', value: moment(), valid: true, touched: false, errorMessage: '', label: 'Date of birth:' } } } |
بعداً، در کامپوننت create خود، میخواهیم این آبجکت را به آرایهای از آبجکتها تبدیل کنیم و آن را به کامپوننت Input
بفرستیم تا تمام فیلدهای input مورد نیاز در form ایجاد شود. این آرایه قرار است شامل آبجکتها (زوجهای key-value) باشد که key قرار است name یا address یا dateOfBirth (ویژگیهای آبجکت بالا) و value قرار است قسمت کامل تنظیمات همین آبجکت (type, value, element…) باشد.
ایجاد عناصر Input به صورت داینامیک
اولین کاری که باید انجام دهیم نصب کتابخانه react-datepicker است. زیرا برای کنترل dateOfBirth به آن نیاز داریم.
برای این منظور، این دستور را اجرا کنید:
1 |
npm install react-datepicker@1.4.0 --save |
حال در پوشه src، پوشه UI
را ایجاد میکنیم. داخل آن، قصد داریم یک پوشه جدید به نام Input
و داخل این پوشه، فایلهای Input.js و Input.css را ایجاد میکنیم:
فایل Input.js قرار است یک کامپوننت functional باشد، پس آن را طبق روال زیر اصلاح میکنیم:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
import React from 'react'; import Aux from '../../hoc/Auxiliary/Auxiliary'; import { FormGroup, Col, FormControl, ControlLabel } from 'react-bootstrap'; import DatePicker from 'react-datepicker'; import 'react-datepicker/dist/react-datepicker.css'; import './Input.css'; const input = (props) => { let inputField = null; let errorMessage = null; if(props.invalid && props.shouldValidate && props.touched){ errorMessage = (<em>{props.errorMessage}</em>); } return ( <Aux> {inputField} </Aux> ) } export default input; |
در حال حاضر، ما فقط میخواهیم تمام resource های مورد نیاز خود را import کرده، فیلد input و error message را initialize کنیم و مقدار را برای error message در صورتی که control نامعتبر است و باید اعتبارسنجی شود و همچنین در صورتی که touche شده باشد، تنظیم میکنیم. اگر کاربر اصلاً نشانگر ماوس را داخل آن component قرار نداده باشد، نمیخواهیم خطا را نشان دهیم. در نهایت، ما این فیلد input (که فعلاً null است) را به component والد برمیگردانیم.
در زیر دستور if و بالای بلوک return
، کد زیر را برای پر کردن ویژگی inputField اضافه می کنیم:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
switch (props.elementType) { case 'input': inputField = ( <FormGroup controlId={props.id}> <Col componentClass={ControlLabel} sm={2}> {props.label} </Col> <Col sm={6}> <FormControl type={props.type} value={props.value} onChange={props.changed} onBlur={props.blur} /> </Col> <Col> <em>{errorMessage}</em> </Col> </FormGroup> ) break; case 'datePicker': inputField = ( <FormGroup controlId={props.id}> <Col componentClass={ControlLabel} sm={2}> {props.label} </Col> <Col sm={6}> <DatePicker selected={props.value} dateFormat="MM/DD/YYYY" readOnly onChange={props.changed} className='datePickerControl' showYearDropdown dropdownMode="select"/> </Col> <Col> <em>{errorMessage}</em> </Col> </FormGroup> ) break; default: inputField = null; } |
اینجا ما با بین نوع element ها switch کردیم و اگر element مورد نظر از نوع input باشد، در نتیجه فیلد input را به همراه تمام ویژگیها و event های مورد نیاز آن ایجاد میکنیم. ما همین کار را برای کنترل datePicker نیز انجام دادیم. برای form های ما، این دو نوع input کافی میباشد. اما اگر هریک از پروژه های شما، به کنترلهای بیشتری نیاز داشته باشند، خیلی ساده میتوانید عبارت case را برای آن کنترلهای مورد نظر خود نیز اینجا اضافه کنید.
یک کار دیگر که باید انجام دهید اصلاح فایل Input.css است:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
em{ color: red; margin: 5px 0; } .react-datepicker__day-name, .react-datepicker__day, .react-datepicker__time-name { display: inline-block; width: 30px!important; height: 30px; line-height: 30px; text-align: center; margin: 0.166rem; font-size: 14px; } .react-datepicker__month-container select { font-size: 12px!important; } .datePickerControl { display: block; width: 100%; height: 34px; padding: 6px 12px; font-size: 14px; line-height: 1.42857143; color: #555; background-color: #fff; background-image: none; border: 1px solid #ccc; border-radius: 4px; } .react-datepicker__input-container { position: relative; display: inline-block; width: 100%; } .react-datepicker-wrapper { display: inline-block; width: 100%; } |
در این class، ما برخی از کلاسهای ذاتی خود datePicker
را override کرده و یک class سفارشی را به آن اضافه کرده ایم (.datePickerControl
).
تمام. اکنون می توانیم با کامپوننت CreateOwner به کار خود ادامه دهیم.
کامپوننت CreateOwner
در پوشه containers
و سپس داخل پوشه Owner
، یک پوشه جدید ساخته و نام آن را CreateOwner بگذاریم. داخل این پوشه، یک فایل جدید به نام CreateOwner.js ایجاد میکنیم.
حالا اجازه دهید این فایل را به صورت زیر اصلاح کنیم:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
import React, { Component } from 'react'; import Input from '../../../UI/Inputs/Input'; import { Form, Well, Button, FormGroup, Col } from 'react-bootstrap'; import { returnInputConfiguration } from '../../../Utility/InputConfiguration'; class CreateOwner extends Component { state = { ownerForm: {}, isFormValid: false } componentWillMount = () =>{ this.setState({ ownerForm: returnInputConfiguration() }); } render() { return ( <Well> </Well> ) } } export default CreateOwner; |
در کد بالا، ما تمام component های ضروری react-bootstrap و تابع returnInputConfiguration از فایل InputConfiguration.js را import کرده ایم. این component یک class component یا یک stateful component است و در componentWillMount
lifecycle hook، ما local state خود را با تمام تنظیمات form آپدیت میکنیم. compnentWillMount
hook بلافاصله قبل از تمام component mount ها trigger میشود.
اجازه دهید کد دیگری را بین بلاکهای render و return
اضافه کنیم:
1 |
const formElementsArray = formUtilityActions.convertStateToArrayOfFormObjects({ ...this.state.ownerForm }); |
در تابع convertStateToArrayOfFormObjects
funcition، میخواهیم آبجکت ownerForm را به آرایه ای از آبجکتها، برای ارسال آن به کامپوننت Input
تبدیل کنیم. پس اجازه دهید این تابع را در یک فایل جداگانه اضافه کنیم.
داخل پوشه Utility
یک فایل جدید به نام FormUtility.js ایجاد کنید. این فایل را با اضافه کردن function زیر برای تبدیل آبجکت به آرایه اصلاح کنید:
1 2 3 4 5 6 7 8 9 10 11 |
export const convertStateToArrayOfFormObjects = (formObject) => { const formElementsArray = []; for (let key in formObject) { formElementsArray.push({ id: key, config: formObject[key] }); } return formElementsArray; } |
ما باید این function را به داخل فایل CreateOwner.js ایمپورت کنیم:
1 |
import * as formUtilityActions from '../../../Utility/FormUtility'; |
ما میخواهیم action های بیشتری را داخل فایل FormUtility.js داشته باشیم. از این جهت، تمام آن action ها را داریم در کامپوننت CreateOwner.js ایمپورت میکنیم.
ما اینجا formElementsArray را پر کرده ایم. پس بیایید از آن برای ارسال تمام ویژگیها به سمت کامپوننت Input
استفاده کنیم.
آبجکت تنظیمات عناصر Form
کد زیر را به تگ Well
اضافه کنیم:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<Form horizontal onSubmit={this.createOwner}> { formElementsArray.map(element => { return <Input key={element.id} elementType={element.config.element} id={element.id} label={element.config.label} type={element.config.type} value={element.config.value} changed={(event) => this.handleChangeEvent(event, element.id)} errorMessage={element.config.errorMessage} invalid={!element.config.valid} shouldValidate={element.config.validation} touched={element.config.touched} blur={(event) => this.handleChangeEvent(event, element.id)} /> }) } <br /> </Form> |
در این کد، ما داریم بر روی تمام object های (تنظیمات input) درون formElementsArray
حلقه میزنیم و کامپوننت Input
را به همراه تمام ویژگیهای ضروری آن و event های مورد نیازش برمیگردانیم. اینجا یک تابع handleChangeEvent وجود دارد و ما قصد داریم این تابع را برای فعال کردن validation و bind کردن دوطرفه ایجاد کنیم. اما بعدا در مورد آن بیشتر توضیح خواهیم داد.
برای دیدن نتیجه اقدامات فعلی، فایل App.js را با اضافه کردن یک route دیگر به کامپوننت CreateOwner
زیر OwnerDetails
route اصلاح میکنیم. ما همچنین عبارت import را نیز نباید اینجا فراموش کنیم:
1 |
import CreateOwner from './Owner/CreateOwner/CreateOwner'; |
1 |
<Route path="/createOwner" component={CreateOwner} /> |
حالا اگر به صفحه CreateOwner برویم، نتیجه زیر را میبینیم:
بسیار عالی.
ما control های خود را ایجاد کردیم، اما هنوز نمیتوانیم کاری با آنها انجام دهیم. اما به زودی تابع handleChangeEvent
را پیاده سازی میکنیم. ما در این تابع قادر خواهیم بود input های خود را اصلاح و validate نماییم.
تکمیل CreateOwner View
اجازه دهید دکمه ها را برای تکمیل قسمت view به component خود اضافه کنیم.
داخل تگ Form و زیر تگ <br>، این کد را اضافه کنید:
1 2 3 4 5 6 7 8 |
<FormGroup> <Col mdOffset={6} md={1}> <Button type='submit' bsStyle='info' disabled={!this.state.isFormValid}>Create</Button> </Col> <Col md={1}> <Button bsStyle='danger' onClick={this.redirectToOwnerList}>Cancel</Button> </Col> </FormGroup> |
ما اینجا دو دکمه اضافه کردیم و تا زمانی که form نامعتبر باشد، دکمه Create غیرفعال خواهد بود.
عالی.
حالا form ما به شکل زیر میباشد:
همه چیز طبق انتظار کار می کند.
نتیجه گیری
با خواندن این پست، شما یاد گرفتید:
- نحوه استفاده از عناصر Bootstrap modal برای ایجاد component های پنجره modal به صورت reusable
- استفاده از object ساده تنظیمات برای ایجاد form و نحوه انتقال آن به آرایه ای از object ها
- نحوه انتقال آرایه configuration با استفاده از یک کد ساده JSX به یک Form View
بابت خواندن این مقاله از شما تشکر میکنیم و امیدواریم که برای شما مفید واقع قرار گرفته باشد.
در قسمت بعدی این سری آموزشی، قصد داریم نحوه اعتبارسنجی عناصر input خود را یاد بگیریم. علاوه بر این، قصد داریم از CreateComponent برای ارسال یک درخواست POST به سمت سرور NET Core Web API. خود استفاده کنیم.