-
جمعه, ۳۰ مهر ۱۴۰۰، ۰۴:۳۹ ب.ظ
-
۷۱۰
سلام دوستان :)
توی پست امروز قراره که قسمت دوم ساخت پلتفرم mail با جنگو یعنی بخش front-end رو با هم ببینیم.
توی پست قبلی model های دیتابیس، view ها و از همه مهمتر API ها رو ساختیم و دیدیم که با فرستادن یک http request از نوع POST یا GET سرور ما یک response به ما برمیگردونه. حالا می خوایم یک صفحه HTML طراحی کنیم و با javascript بتونیم از اون API هایی که تعریف کردیم استفاده کنیم.
خب اول از همه توی فولدر mail(اونجایی که فایل های views.py, models.py و... هستن) یک فولدر به نام templates درست کنین(توجه کنین که اسم فولدر فقط باید templates باشه) و داخل فولدر templates یک فولدر به اسم mail درست کنین(این یکی هم حتما باید mail باشه) که بتونیم فایل های HTML که بعدا درست میکنیم رو داخلش قرار بدیم.
خب حالا توی فولدر mail(اونجایی که فایل های models.py, views.py و... هستن) یک فولدر به نام static درست کنین(توجه کنین که اسم فولدر فقط باید static باشه) و داخل فولدر static یک فولدر دیگه به نام mail درست کنین(این یکی هم حتما mail باشه) که بتونیم فایل های CSS و javascript رو داخلش قرار بدیم.
خب بعد از درست کردن این فولدر ها برین داخل فولدر templates/mail چندین فایل به این اسم ها درست کنین:
1- inbox.html
2- layout.html
3- login.html
4- register.html
خب حالا فایل inbox.html رو باز کنین و کد های زیر رو داخلش وارد کنین:
{% extends "mail/layout.html" %} {% load static %} {% block body %} <h2>{{ request.user.email }}</h2> <button class="btn btn-sm btn-outline-primary" id="inbox">Inbox</button> <button class="btn btn-sm btn-outline-primary" id="compose">Compose</button> <button class="btn btn-sm btn-outline-primary" id="sent">Sent</button> <button class="btn btn-sm btn-outline-primary" id="archived">Archived</button> <a class="btn btn-sm btn-outline-primary" href="{% url 'logout' %}">Log Out</a> <hr> <div id="emails-view"> </div> <div id="compose-view"> <h3>New Email</h3> <div id="message"></div> <form id="compose-form"> <div class="form-group"> From: <input disabled class="form-control" value="{{ request.user.email }}" id="from"> </div> <div class="form-group"> To: <input id="compose-recipients" class="form-control" name="to"> </div> <div class="form-group"> <input class="form-control" id="compose-subject" placeholder="Subject"> </div> <textarea class="form-control" id="compose-body" placeholder="Body"></textarea> <input type="submit" class="btn btn-primary my-3" value="Send"> </form> </div> {% endblock %} {% block script %} <script src="{% static 'mail/inbox.js' %}"></script> {% endblock %}
توضیح: اول با استفاده از extends، فایل layout.html رو extend کردیم که به این معنی هست که دیگه لازم نیست یک سری تگ ها مثل head, body و... رو بذاریم.
بعدش فایل های static(فایل های javascript و css مون که بعدا بهتون میگم) رو load کردیم.
حالا توی بلاک body اومدیم چندتا دکمه برای جابجایی بین inbox, compose, send و archived گذاشتیم.
حالا یک div با آیدی emails-view درست کردیم که بعدا بتونیم ایمیل ها رو با استفاده از جاوااسکریپت داخلش قرار بدیم.
بعد توی یک div دیگه فرم فرستادن ایمیل رو تعریف کردیم که البته کاربر توی صفحه اصلی اون رو نمیبینه و باید روی دکمه compose کلیک کنه تا بتونه فرمی که درست کردیم رو ببینه.
توی سه خط آخر هم script هامون رو load کردیم.
خب حالا فایل layout.html رو باز کنید و کد های زیر رو داخلش قرار بدین:
{% load static %} <!DOCTYPE html> <html lang="en"> <head> <title>{% block title %}Mail{% endblock %}</title> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous"> <link rel="stylesheet" href="{% static 'mail/styles.css' %}"> {% block script %} {% endblock %} </head> <body> <div class="container"> {% block body %} {% endblock %} </div> </body> </html>
توضیح: خب اینجا کار خاصی نکردیم فقط یک سری بلاک تعریف کردیم و با استفاده از تگ link فریم ورک bootstrap رو لود کردیم.
حالا فایل login.html رو باز کنین و کد های زیر رو داخلش قرار بدین:
{% extends "mail/layout.html" %} {% block body %} <h2>Login</h2> {% if message %} <div>{{ message }}</div> {% endif %} <form action="{% url 'login' %}" method="post"> {% csrf_token %} <div class="form-group"> <input autofocus class="form-control" type="email" name="email" placeholder="Email"> </div> <div class="form-group"> <input class="form-control" type="password" name="password" placeholder="Password"> </div> <input class="btn btn-primary" type="submit" value="Login"> </form> Don't have an account? <a href="{% url 'register' %}">Register here.</a> {% endblock %}
توضیح: این جا هم یک فرم login با HTML درست کردیم.
حالا فایل register.html رو باز کنین و کد های زیر رو داخلش قرار بدین:
{% extends "mail/layout.html" %} {% block body %} <h2>Register</h2> {% if message %} <div>{{ message }}</div> {% endif %} <form action="{% url 'register' %}" method="post"> {% csrf_token %} <div class="form-group"> <input class="form-control" type="email" name="email" placeholder="Email Address"> </div> <div class="form-group"> <input class="form-control" type="password" name="password" placeholder="Password"> </div> <div class="form-group"> <input class="form-control" type="password" name="confirmation" placeholder="Confirm Password"> </div> <input class="btn btn-primary" type="submit" value="Register"> </form> Already have an account? <a href="{% url 'login' %}">Log In here.</a> {% endblock %}
توضیح: این هم باز چیز خاصی نداره و یک فرم register با HTML درست کردیم.
حالا فایل هایی که قرار بود توی templates/mail قرار بدیم تموم شدن.
الان میرسیم به فایل های static/mail.
خب حالا میرسیم به درست کردن اصلی ترین فایل یعنی inbox.js.
فایل inbox.js رو داخل فولدر static/mail درست کنین و مقادیر زیر رو داخلش قرار بدین:
document.addEventListener( "DOMContentLoaded", function () { const form = document.querySelector("#compose-form"); const msg = document.querySelector("#message"); form.addEventListener("submit", (event) => { event.preventDefault(); to = document.querySelector("#compose-recipients"); subject = document.querySelector("#compose-subject"); body = document.querySelector("#compose-body"); if (from.length == 0 && to.length == 0) return; fetch("/emails", { method: "POST", body: JSON.stringify({ recipients: to.value, subject: subject.value, body: body.value, }), }) .then((response) => response.json()) .then((result) => { console.log(result.status); if (result.status == 201) { load_mailbox("sent"); } else { msg.innerHTML = `<div class="alert alert-danger" role="alert"> ${result.error} </div>`; } }); }); }, false ); document.addEventListener("DOMContentLoaded", function () { // Use buttons to toggle between views document.querySelector("#inbox").addEventListener("click", () => load_mailbox("inbox")); document.querySelector("#sent").addEventListener("click", () => load_mailbox("sent")); document.querySelector("#archived").addEventListener("click", () => load_mailbox("archive")); document.querySelector("#compose").addEventListener("click", compose_email); // By default, load the inbox load_mailbox("inbox"); }); function compose_email() { // Show compose view and hide other views document.querySelector("#emails-view").style.display = "none"; document.querySelector("#compose-view").style.display = "block"; // Clear out composition fields document.querySelector("#compose-recipients").value = ""; document.querySelector("#compose-subject").value = ""; document.querySelector("#compose-body").value = ""; } function load_mailbox(mailbox) { // Show the mailbox and hide other views document.querySelector("#emails-view").style.display = "block"; document.querySelector("#compose-view").style.display = "none"; // Show the mailbox name document.querySelector("#emails-view").innerHTML = `<h3>${mailbox.charAt(0).toUpperCase() + mailbox.slice(1)}</h3>`; if (mailbox == "show_mail") { show_mail(); return; } fetch(`/emails/${mailbox}`) .then((response) => response.json()) .then((emails) => { emails.forEach((element) => { if (mailbox != "sent") { sender_recipients = element.sender; } else { sender_recipients = element.recipients; } if (mailbox == "inbox") { if (element.read) is_read = "read"; else is_read = ""; } var item = document.createElement("div"); item.className = `card ${is_read} my-1 items`; item.innerHTML = `<div style="border: 1px solid black; height: 35px;" id="item-${element.id}"> <strong style="font-size: 14pt;"> ${sender_recipients}</strong> ${element.subject} <div style="float: right; font-weight: 150">${element.timestamp}</div> <br> </div>`; document.querySelector("#emails-view").appendChild(item); item.addEventListener("click", () => { show_mail(element.id, mailbox); }); }); }); } function show_mail(id, mailbox) { fetch(`/emails/${id}`) .then((response) => response.json()) .then((email) => { // Print email // console.log(email); document.querySelector("#emails-view").innerHTML = ""; var item = document.createElement("div"); item.innerHTML = `<div style="white-space: pre-wrap;"> <strong>From:</strong> ${email.sender} <strong>To:</strong> ${email.recipients} <strong>Subject:</strong> ${email.subject} <strong>Timestamp:</strong> ${email.timestamp} </div> `; document.querySelector("#emails-view").appendChild(item); if (mailbox == "sent") return; let archive = document.createElement("btn"); archive.className = `btn btn-outline-primary my-2`; archive.addEventListener("click", () => { toggle_archive(id, email.archived); if (archive.innerText == "Archive") archive.innerText = "Unarchive"; else archive.innerText = "Archive"; }); if (!email.archived) archive.textContent = "Archive"; else archive.textContent = "Unarchive"; document.querySelector("#emails-view").appendChild(archive); let reply = document.createElement("btn"); reply.className = `btn btn-outline-primary m-2`; reply.textContent = "Reply"; reply.addEventListener("click", () => { reply_mail(email.sender, email.subject, email.body, email.timestamp); }); document.querySelector("#emails-view").appendChild(reply); make_read(id); let hr = document.createElement("HR"); document.querySelector("#emails-view").appendChild(hr); let message = document.createElement("div"); message.innerHTML = `${email.body}`; document.querySelector("#emails-view").appendChild(message); }); } function toggle_archive(id, state) { fetch(`/emails/${id}`, { method: "PUT", body: JSON.stringify({ archived: !state, }), }); } function make_read(id) { fetch(`/emails/${id}`, { method: "PUT", body: JSON.stringify({ read: true, }), }); } function reply_mail(sender, subject, body, timestamp) { compose_email(); if (!/^Re:/.test(subject)) subject = `Re: ${subject}`; document.querySelector("#compose-recipients").value = sender; document.querySelector("#compose-subject").value = subject; pre_fill = `On ${timestamp} ${sender} wrote:\n${body}\n`; document.querySelector("#compose-body").value = pre_fill; }
توضیح: خب توابع برای فرستادن ایمیل، لود کردن ایمیل، reply دادن و... هست. حالا چجوری میتونیم از API استفاده کنیم؟ با استفاده از تابع fetch.
ما با استفاده از تابع fetch میتونیم انواع request ها رو به یک آدرس بفرستیم و respose اون رو دریافت کنیم و هر کار میخوایم باهاش بکنیم. این هم دقیقا همون چیزیه که ما میخوایم :)
خب حالا که بخش جاوااسکریپت رو نوشتیم می رسیم به css. توی فولدر static/mail، یک فایل به اسم styles.css درست کنین و مقادیر زیر رو داخلش بذارین:
textarea { min-height: 200px; } .items { cursor: pointer; } .read { background-color: #e6e6e6; }
خب این هم از این.
حالا کارمون تموم شد :))
فقط مونده اجرا کردن پروژه و تست کردنش!
خب cmd یا ترمینال رو باز کنین و وارد فولدر پروژتون بشین.
حالا دستور زیر رو وارد کنین:
python manage.py makemigrations
بعد هم این دستور:
python manage.py migrate
حالا در نهایت برای اجرا کردن پروژه دستور زیر رو وارد کنین:
python manage.py runserver
الان مرورگرتون رو باز کنین و وارد آدرس زیر بشین:
http://127.0.0.1:8000
حالا میتونین صفحه رو ببینین!
اگر با هر مشکلی مواجه شدین من براتون اینجا فایل zip رو که کل محتویات پروژه داخلش هست رو میذارم که اگه خواستین بتونین از روی اون پروژه رو run کنین.
برای دانلود کلیک کنین.
امیدوارم براتون مفید بوده باشه D: