صفحة البحث
جزء من قسم أساسيات Express في رحلة Node.js على Coddy. الدرس 23 من 23.
صفحة البحث هي نموذج، والخادم يستجيب له مثل أي طلب GET آخر.
<form action="/books" method="get">
<input name="author">
<button>Find</button>
</form>يؤدي إرسال هذا النموذج إلى نقل المتصفح إلى /books?author= متبوعًا بما كُتب في الحقل المسمى author. يقرأ المسار القيمة من req.query. تؤدي كتابة الكلمة مجددًا كقيمة value للحقل إلى إبقائها ظاهرة بعد البحث؛ فهي قادمة من الزائر، لذا عليك تهريبها.
التحدي
سهلأصبح لدى Recipe Box صفحة بحث. يكتب الزائر كلمة، وتعرض الصفحة الوصفات التي تحتوي عناوينها عليها.
- في
server.js، أضف مسارًا غير متزامنًا لـ/searchفوق معالج 404. يقرأ المسار الكلمة منreq.query.q، أو يستخدم سلسلة فارغة إذا لم توجد كلمة. - تحتوي الصفحة دائمًا على نموذج بحث:
<form action="/search" method="get">مع حقل نصي اسمهqوزر. يؤدي إرسال النموذج إلى فتح/search?q=متبوعًا بالكلمة. - يعرض الحقل الكلمة التي تم البحث عنها: بعد البحث عن
curry، يحتوي الحقل علىcurry. اكتب الكلمة في السمةvalueللحقل، بعد تمريرها عبرescapeHtml، لأنها جاءت من الزائر. - أسفل النموذج، اعرض رابطًا لكل وصفة من
loadRecipes()يحتوي عنوانها على الكلمة، مع تجاهل حالة الأحرف الكبيرة والصغيرة: سيؤدي كل منsoupوSOUPإلى العثور على Tomato Soup. يؤدي كل رابط إلى صفحة الوصفة ويعرض عنوانها بعد تهريبه، كما في الصفحة الرئيسية:<a href="/recipes/1">Tomato Soup</a>. - عند البحث عن كلمة لا يحتوي أي عنوان عليها، اعرض النص
No recipes foundبدلًا من القائمة.
جرّب ذلك: اضغط على «تشغيل» وأرسل /search: ستعرض الصفحة النموذج الفارغ. اكتب SOUP في الحقل واضغط على الزر. ستعرض الصفحة Tomato Soup، وسيظل الحقل يعرض SOUP. ابحث عن pizza: ستظهر No recipes found. ابحث عن fish: ستظهر Fish & Chips بالشكل الصحيح. بعد ذلك، أرسل طلب POST يتضمن {"title": "Pea Soup", "tags": ["soup"]} إلى /api/recipes، ثم ابحث عن soup مرة أخرى: ستظهر Pea Soup أيضًا في القائمة.
جرّب بنفسك
import { escapeHtml } from './html.js';
import { dayOfYear } from './day-of-year.js';
import express from 'express';
import { randomUUID } from 'node:crypto';
import { recipes } from './data.js';
import recipesRouter, { loadRecipes } from './routes/recipes.js';
import reviewsRouter from './routes/reviews.js';
const app = express();
app.use((req, res, next) => {
console.log(`${req.method} ${req.url}`);
next();
});
app.use((req, res, next) => {
res.set('X-Request-Id', randomUUID());
next();
});
app.use(express.json());
app.use(express.static('public'));
app.get('/', (req, res) => {
const items = recipes.map((r) => `<li><a href="/recipes/${r.id}">${escapeHtml(r.title)}</a></li>`);
res.send(`<link rel="stylesheet" href="/style.css"><h1>Recipe Box</h1><ul>${items.join('')}</ul>`);
});
// ترسل type('text') نصًا عاديًا، لذا يظهر كل عنوان في سطر مستقل.
app.get('/recipes', (req, res) => {
const { tag } = req.query;
const list = tag ? recipes.filter((recipe) => recipe.tags.includes(tag)) : recipes;
res.type('text').send(list.map((recipe) => recipe.title).join('\n'));
});
app.get('/recipes/:id', (req, res) => {
const recipe = recipes.find((r) => r.id === Number(req.params.id));
if (!recipe) {
res.status(404).send('No such recipe');
return;
}
res.send(recipe.title);
});
app.use('/api/recipes', recipesRouter);
app.use('/api/recipes/:id/reviews', reviewsRouter);
app.get('/api/today', async (req, res) => {
const day = dayOfYear(req.query.date);
if (day === null) {
res.status(400).json({ error: 'Use a date like 2026-03-01' });
return;
}
const recipes = await loadRecipes();
res.json(recipes[day % recipes.length]);
});
app.get('/about', (req, res) => {
res.send('Recipe Box collects family recipes.');
});
app.get('/menu', (req, res) => {
res.redirect(301, '/recipes');
});
app.get('/menu/:id', (req, res) => {
res.redirect(301, `/recipes/${req.params.id}`);
});
app.use((req, res) => {
if (req.path.startsWith('/api/')) {
res.status(404).json({ error: 'Not found' });
return;
}
res.status(404).send('<h1>Page not found</h1><p><a href="/">Back to Recipe Box</a></p>');
});
app.use((err, req, res, next) => {
console.error(err);
res.status(500).json({ error: 'Something went wrong' });
});
app.listen(3000, () => {
console.log('Recipe Box is running on port 3000');
});
جميع دروس أساسيات Express
تدرّب بنفسك: Playground لـ Express