مقدمه

در درس قبل دیدیم که چطور برای اپلیکیشن‌های Express سیستم مسیریابی ایجاد می‌کنیم. در یک سیستم مسیریابی، هر route به یک تابع handler نگاشته می‌شود و آن تابع، شامل کدی است که پاسخ درخواست را تولید می‌کند. پاسخ‌هایی که تا الان برای درخواست‌ها ایجاد کردیم، یا متن ساده (plain text) بودند و یا عناصر HTML که در هر دو صورت، با استفاده از متدی به نام res.send() برگردانده می‌شدند. اما حالا قصد داریم ببینیم چطور می‌توان یک سند HTML را به عنوان پاسخ برگرداند و چطور می‌توان ترتیبی داد که سند برگشتی شامل محتوای دینامیک باشد. برای این منظور، باید با یک مؤلفه‌ی کلیدی دیگر با نام Templating Engine آشنا شویم که امکان رندر دینامیک اسناد وب را فراهم می‌کند.

نقش موتورهای Templating

در تکنولوژی Backend، اصطلاح View به ساختاری اشاره می‌کند که پاسخ نهایی را برای کلاینت‌هایی مثل مرورگر تولید می‌کند. در واقع، یک view فایلی است که می‌تواند شامل ترکیبی از کدهای HTML و داده‌های دینامیک باشد. ما به ابزاری نیاز داریم که اولاً روشی را برای درج داده در view فراهم کند و ثانیاً view را رندر کرده و آن را به فرمتی تبدیل کند که برای مرورگر قابل درک باشد. این ابزار Templating Engine نام دارد.

برای اپ‌های Node.js و Express چند موتور Templating مانند Pug، Handlebars و EJS در دسترس است که هر کدام مزایا و معایبی نسبت به سایرین دارند. انتخاب ما EJS است که نامش از روی عبارت Embedded JavaScript گرفته شده است. این ابزار، همانطور که نامش نشان می‌دهد، برای درج داده‌های دینامیک و منطق برنامه از سینتکس آشنای جاوااسکریپت استفاده می‌کند و در مورد نمایش محتوا هم به HTML متکی است. بنابراین، بر خلاف سایر گزینه‌ها، EJS به یادگیری سینتکس جدیدی نیاز ندارد. تنها چیزی که باید بدانیم این است که تگ <%= %> برای درج متغیرها و تگ <% %> برای درج گزاره‌ها و عبارات جاوااسکریپتی مثل حلقه‌ها و ساختارهای شرطی، کاربرد دارند.

در ادامه، با روش نصب و استفاده از EJS در اپ‌های Express آشنا می‌شویم.

استفاده از EJS در اپ‌های Express

قبل از هر چیز، با استفاده از کامند زیر، پکیج EJS را نصب می‌کنیم.

$ npm install ejs

حالا باید در فایل app.js دو کار را انجام دهیم. اول اینکه یک دایرکتوری را به عنوان محل نگهداری ویوها تعیین کنیم و دوم اینکه EJS را به عنوان موتور view به Express معرفی کنیم. این دو کار را با استفاده از متدی به نام app.set() انجام می‌دهیم که برای اعمال برخی کانفیگ‌های خاص روی اپ‌های Express کاربرد دارد.

Copy Icon app.js
const app = express();

app.set('views', './views');
app.set('view engine', 'ejs');

حالا دایرکتوری views را که در بالا به عنوان محل نگهداری ویوها تعیین شده، ایجاد می‌کنیم و یک فایل با نام index.ejs با محتوای زیر در آن قرار می‌دهیم.

Copy Icon views/index.ejs
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Document</title>
  </head>
  <body>
    <h1><%= message %></h1>
    <p>Response Created Using EJS</p>
  </body>
</html>

همانطور که می‌بینید، فایل index.ejs شامل کدهای HTML و البته یک تگ <%= %> است که قبلاً گفتیم که برای درج متغیرها در view کاربرد دارد. پس، در اینجا محتوای عنصر h1 از روی مقدار متغیری به نام message تعیین می‌شود. چنین متغیری هنوز تعریف نشده اما در ادامه، این کار را انجام خواهیم داد.

رندر View توسط Express

برای رندر View یا تمپلت EJS باید از متدی به نام res.render() استفاده کنیم. این کار را درون توابع handler انجام می‌دهیم تا به این ترتیب، روت‌ها به ویوها متصل شوند. روت‌های زیر را به فایل app.js اضافه کنید.

Copy Icon app.js
app.get('/', (req, res) => {
  res.render('index', { message: 'Hello From Node.js' });
});
            
app.get('/contact', (req, res) => {
  res.render('index', { message: 'The Contact Page' });
});
            
app.get('/about', (req, res) => {
  res.render('index', { message: 'The About Page' });
});

app.get('*', (req, res) => {
  res.status(404).render('index', { message: 'Not Found' });
});

همانطور که می‌بینید، متد res.render() دارای دو آرگومان است. اولی تمپلتی را که باید رندر شود، مشخص می‌کند و می‌بینید که نیازی به نوشتن پسوند .ejs هم نیست. اما آرگومان دوم یک شیء جاوااسکریپتی است که شامل آپشن‌هایی است که باید برای تمپلت ارسال شوند. پراپرتی‌های این شیء در تمپلت به صورت متغیر در دسترس خواهند بود. با این حساب، تکلیف متغیر message که از آن در تمپلت استفاده کرده‌ایم، روشن می‌شود.

سرور را استارت کنید و هر یک از روت‌های بالا را آزمایش کنید و نتیجه را ببینید. محتوای عنصر h1 به صورت دینامیک رندر می‌شود.

استفاده از تمپلت‌های Partial

دیدیم که با استفاده از EJS می‌توانیم یک فایل تمپلت داشته باشیم که چندین خروجی مختلف را تولید می‌کند. با تکیه بر قابلیتی به نام تمپلت‌های Partials حتی می‌توانیم از تکرار کدها نیز جلوگیری کنیم. وقتی کدی داشته باشیم که در چند تمپلت دیده می‌شود، می‌توانیم این کد را در یک تمپت Partial قرار دهیم و با استفاده از تابعی به نام include() آن را در هر تمپلت دیگر جاسازی کنیم.

درون دایرکتوری views یک دایرکتوری با نام partials ابرای نگهداری تمپلت‌های Partial ایجاد کنید. یک فایل با نام head.ejs درون دایرکتوری partials ایجاد کنید که دارای محتوای زیر باشد.

Copy Icon views/partials/head.ejs
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Document</title>
</head>

حالا می‌توانیم از این تمپلت در هر تمپلت دیگر استفاده کنیم. در فایل index.ejs عنصر head و محتوای آن را با خط زیر تعویض کنید.

Copy Icon views/index.ejs
<%- include('./partials/head') %>

به این ترتیب، محتوای تمپلت head.ejs در تمپلت index.ejs جاسازی می‌شود. دقت داشته باشید که تابع include() باید درون تگ <%- %> قرار بگیرد.

محتواهایی مانند منوها و فوترها معمولاً باید در همه‌ی صفحات یک سایت وجود داشته باشند. تمپلت‌های Partial به ما امکان می‌دهند که کد این بخش‌ها را فقط یک بار بنویسیم و در هر تعداد صفحه که مایلیم، قرار دهیم.