در قسمت قبل یک Modal خلاصه سفارش ساختیم که خلاصه ای از گزارش را به کاربر نمایش می داد اما مشکل آن جا بود که این modal همیشه در وسط صفحه قرار داشت و جلوی دید کاربر را می گرفت. در این قسمت می خواهیم کاری کنیم که تا قبل از فشرده شدن دکمه ی ORDER NOW هیچ modal ای نمایش داده نشود. برای شروع کار ابتدا باید یک خصوصیت دیگر به State اضافه کنیم:
state = { ingredients: { salad: 0, bacon: 0, cheese: 0, meat: 0 }, purchasable: false, totalPrice: 4, purchasing: false }
مقدار purchasing (به معنی «در حال خرید») را به state اضافه کرده ایم و آن را در حالت پیش فرض روی false قرار داده ایم. این مقدار مشخص خواهد کرد که آیا روی دکمه ی ORDER NOW کلیک شده است یا خیر. حالا یک متد دیگر به نام purchaseHandler را ایجاد می کنیم:
purchaseHandler() { this.setState({ purchasing: true }); }
این متد مقدار purchasing را به true تغییر می دهد. حالا به BuildControls.js میرویم و در قسمت JSX دکمه، یک onClick اضافه می کنیم:
<button className={classes.OrderButton} disabled={!props.purchasable} onClick={props.ordered} >ORDER NOW</button>
در واقع قرار است با کلیک روی این دکمه یک متد اجرا شود که آن را از طریق props پاس داده ایم و نام ordered را برایش انتخاب کرده ایم. حالا می توانیم به BurgerBuilder برگردیم تا این prop را به کامپوننتش پاس دهیم:
<BuildControls ingredientAdded={this.addIngredientHandler} ingredientRemoved={this.removeIngredientHandler} disabled={disabledInfo} purchasable={this.state.purchasable} price={this.state.totalPrice} ordered={this.purchaseHandler} />
حالا باید کاری کنیم که نمایش modal بر اساس وضعیت purchasing انجام داده شود. برای این کار یک خصوصیت به نام دلخواه به <Modal> اضافه کنید:
<Modal show={this.state.purchasing}>
سپس به فایل Modal.js میرویم و بر اساس این prop نمایش آن را تنظیم می کنیم:
const Modal = (props) => ( <div className={classes.Modal} style={{ transform: props.show ? 'translateY(0)' : 'translateY(-100vh)', opacity: props.show ? '1' : '0' }}> {props.children} </div> );
در اینجا تنها کاری که کرده ایم اضافه کردن دو استایل به صورت inline است. قبلا هم گفته بودیم که در react برای اضافه کردن استایل های inline می توانید مانند بالا یک شیء جاوا اسکریپت را به خصوصیت style بدهید (ر.ک به جلسات استایل دهی در react). در اینجا گفته ایم در صورتی که props.show برابر با true بود مقدار translateY را صفر درجه قرار بده (یعنی سر جایش بماند) و اگر false بود مقدار translateY را -100vh قرار بده (یعنی از دید کاربر خارج شود). علامت سوال نشان می دهد که از اپراتور ternary استفاده کرده ایم. سپس با همین منطق مقدار opacity یا شفافیت را نیز 1 یا صفر قرار می دهیم.
حالا اگر فایل ها را ذخیره کرده و به مرورگر بروید و کد را تست کنید به خطا برخورد می کنید. در واقع با کلیک روی دکمه ی ORDER NOW انواع خطا ها به شما نمایش داده می شود. آیا می دانید چرا؟ اگر یادتان باشد در قسمت های اولیه ی این دوره برایتان توضیح داده بودم که کلیدواژه ی this در جاوا اسکریپت باعث مشکلات فراوانی می شود و گفته بودم که اگر از شکل زیر برای تعریف متد هایتان استفاده کنید و آن متد را توسط event handler ها (مثلا onClick) اجرا کنید به مشکل خواهید خورد:
purchaseHandler() { this.setState({ purchasing: true }); }
در این کد کلیدواژه ی this به کلاس برنمیگردد. دقیقا به همین دلیل است که برای متد های removeIngredientHandler و addIngredientHandler هیچ مشکلی به وجود نیامده، ما از روش دیگری برای نوشتن این متد ها استفاده کرده ایم:
removeIngredientHandler = (type) => { const oldCount = this.state.ingredients[type]; ادامه ی کدها در این قسمت // this.updatePurchaseState(updatedIngredients); }
یعنی آن ها را به صورت property (خصوصیت) تعریف کرده ام و سپس یک arrow function را به آن ها داده ام! این ها در آخر همگی متد هستند اما روش دوم باعث می شود از مشکلات this دوری کنیم. بنابراین می توانیم purchaseHandler را نیز به صورت یک خصوصیت تعریف کنیم:
purchaseHandler = () => { this.setState({ purchasing: true }); }
حالا اگر به مرورگر برویم و روی دکمه ی ORDER NOW کلیک کنیم، Modal ما برایمان به نمایش در می آید اما مشکل دیگر وجود دارد: هیچ راهی برای بستن modal نداریم! اینجا جایی است که backdrop وارد کار می شود. برای شروع وارد فایل Backdrop.js شوید و طرح اولیه ی کامپوننت را بنویسید:
import React from "react"; const backdrop = (props) => ( ); export default backdrop;
حالا کافی است بگوییم اگر props.show صحیح بود یک div را برگردان و در غیر این صورت کاری نکن:
const backdrop = (props) => ( props.show ? <div></div> : null );
برای قدم بعد یک فایل به نام Backdrop.module.css ایجاد کنید تا استایل های div بالا را در آن قرار دهیم:
.Backdrop { width: 100%; height: 100%; position: fixed; z-index: 100; left: 0; top: 0; background-color: rgba(0, 0, 0, 0.5); }
این استایل ها به سلیقه ی ما انتخاب شده است و شما می توانید آن ها را تغییر دهید. نکته ی مهم اینجاست که backdrop طول و عرض صفحه را بگیرد (100 درصد) و همچنین z-index بالا داشته باشد تا بالاتر از بقیه ی عناصر صفحه قرار بگیرد. اگر یادتان باشد در استایل های Modal مقدار z-index را روی 500 قرار دادیم چرا که می خواهیم modal بالای backdrop باشد اما backdrop از بقیه ی عناصر بالاتر باشد. این ها منطق ابتدایی CSS است بنابراین آشنایی با CSS برای تمرین این دوره ضروری خواهد بود.
حالا به فایل Backdrop.js برگردید و این کلاس را روی div اعمال کنید:
import classes from './Backdrop.module.css'; const backdrop = (props) => ( props.show ? <div className={classes.Backdrop}></div> : null );
در این مرحله باید سوالی از خودمان بپرسیم: Backdrop را کجای برنامه قرار دهیم؟ این مسئله به سلیقه ی شما بستگی دارد (به طور مثال می توانید آن را درون Layout قرار دهید) اما همانطور که می دانید Backdrop به Modal وابسته است و من دوست دارم آن را درون Modal قرار دهم تا اگر Modal نمایش داده شد Backdrop نیز نمایش داده شود. بنابراین به فایل Modal رفته و بدین شکل عمل کنید:
import React from 'react'; import classes from './Modal.module.css'; import Aux from '../../../hoc/Auxx'; import Backdrop from '../Backdrop/Backdrop'; const Modal = (props) => ( <Aux> <Backdrop show={props.show} /> <div className={classes.Modal} style={{ transform: props.show ? 'translateY(0)' : 'translateY(-100vh)', opacity: props.show ? '1' : '0' }}> {props.children} </div> </Aux> ); export default Modal;
تغییراتی که در این فایل داده ایم بدین شرح است:
الان اگر به مرورگر بروید و کدها را تست کنید متوجه حضور backdrop خواهید شد. backdrop در فارسی چیزی شبیه به «پس زمینه» است. در قدم بعدی باید کاری کنیم که با کلیک روی این backdrop، صفحه ی modal بسته شود. برای انجام این کار ابتدا به فایل Backdrop.js رفته و یک event handler به آن اضافه می کنیم:
const backdrop = (props) => ( props.show ? <div className={classes.Backdrop} onClick={props.clicked}></div> : null );
شما می توانید به جای clicked هر نام دیگری را انتخاب کنید. حالا به فایل Modal.js میرویم و این prop را پاس می دهیم:
<Backdrop show={props.show} clicked={props.modalClosed} />
سپس modalClosed را در فایل BurgerBuilder.js اضافه می کنیم اما قبل از آن باید متد بستن Modal را در همان فایل BurgerBuilder.js بنویسیم:
purchaseCancelHandler = () => { this.setState({ purchasing: false }); }
حالا آن را به قسمت JSX اضافه می کنیم:
<Modal show={this.state.purchasing} modalClosed={this.purchaseCancelHandler}>
حالا اگر به مرورگر بروید و کدها را تست کنید میبینید که همه چیز بدون مشکل اجرا می شود. امیدوارم از این قسمت لذت برده باشید، در قسمت بعد یک کامپوننت برای دکمه ها خواهیم ساخت.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.