Skip to main content

Express.js - creating simple site (hebrew)

איך יוצרים אתר פשוט עם express.js


Express הוא מודול של nodeJS שמאפשר להרים בקלות שרות ווב שמספק שרות REST או אתר.
אומנם קל להגדיר אבל לא פשוט בהתחלה להבין מה להגדיר...

בואו נקים אתר שמאפשר לנהל בצורה בסיסית רשימת משימות לביצוע. אפשר יהיה לראות את המשימות, להוסיף משימות ולסמן משימות שבוצעו.

קישור לקוד בשלמותו כקובץ זיפ



סוגי הקבצים בתיקייה

כדי לכתוב קוד ל nodejs שמממש את האתר הזה אז כדאי לדעת שלאתר כזה יש קבצים לצד ה client וקבצים לצד ה server.  כולם יושבים בתתי תיקיות בתוך התיקייה הראשית שנותנים ל nodejs. זה קצת מקשה על ההבנה איזה קבצים מגיעים לאן ...

קבצים עבור צד ה client כוללים דפי HTML סטטיים, קבצי JS של ספריות כמו jQuery ו bootstrap (לעיצוב), קבצי עיצוב (CSS) וקבצי JS של האפליקציה עצמה שרלבנטים לצד ה client.

קבצים עבור צד ה server כוללים קוד JS ל nodejs, ספריות של nodejs, קבצים נתונים לפלאג-ים השונים שנשתמש בהם עבור express ואולי עוד דברים.


מה האתר מספק?


אנחנו רוצים לכתוב קוד ל nodejs כך שכאשר הוא ירוץ, יהיה לנו שרת ווב שמבצע את הדברים הבאים:

  • כאשר פונים ל '/' אז יחזור דף HTML שיש בו כותרת "Home Page"
  • כאשר פונים ל 'tasks/' אז מקבים בחזרה דף HTML ובו:
    • רשימת המשימות שהוכנסו עד כה. על כל משימה שבוצעה יהיה קו (זה הדבר שיסמן שהיא בוצעה). כל משימה שלא בוצעה תהיה קישור שכאשר לוחצים עליו, המשימה תסומן שבוצעה.
    • שדה קלט + checkbox שיאפשר להכניס משימה חדשה (ולסמן האם בוצעה כבר). הטופס הזה ישתמש ב REST request שמתואר בנקודה הבאה.
  • כאשר שולחים פקודת POST לכתובת 'taskCompleted/' (פקודת REST) אז צריך לשלוח יחד עם הפקודה json ובו שדה title ושדה completed. 

בנייה בשלבים

את האתר נבנה בשלבים:
  • שלב ראשון: נריץ את nodejs ברמה הכי בסיסית כדי לראות שהוא עובד כשרת ווב תקין.
  • שלב שני: נכניס את מנגנון ה route של express. המנגנון הזה מאפשר לנו להגדיר די בפשטות איזה קוד להריץ עבור כל שורת URL.
  • שלב שלישי: נכניס את מנגנון ה view של express. המנגנון הזה מאפשר לנו להשתמש בתבניות HTML כדי להחזיר דף HTML ל client. נשתמש בפורמט jade כדי להגדיר את ה HTML template. זה הרבה יותר נוח מאפשר לבנות HTML בצורה פשטנית.
  • שלב רביעי: נכניס שתי ספריות JS שימושיות - jquery ו bootstrap.
  • נוסיף התייחסות לטופס (form) שמאפשר להוסיף משימה חדשה.

שלב ראשון – הרמת אתר שיש לו דף ראשי אחד

app.js
======
var express = require('express');
var path = require('path');
var app = express();

app.use('/', function (req, res, next) {
     res.send('Site homepage');
});

app.js הוא הקובץ הראשי של התוכנית ואותו נריץ ע"י node.

פקודת ה use אומרת ל express להריץ את הפונקציה על כל בקשה שה URL בה הוא ‘/’. הפונקציה יכולה לבצע משהו ואז או להחזיר תשובה לבקשה ע"י res.send או לקרוא לעוד פונקציות טיפול שאולי מוגדרות ע"י קריאה ל next().

עכשיו (לאחר הרצת node), נקבל את ההודעה Site homepage כאשר נגלוש לאתר.

שלב שני – הכנסת מנגנון route מקצועי

אפליקציית express מכילה בדר"כ הרבה דפים ואז יש הרבה routes (ניתוב לפונקציות שונות ע"פ מה שרשום ב URL). Express מספק מנגנון מרשים יותר לטפל בניתובים כאלה – router.

tasksRoutes.js
==============
var express = require('express');
var bodyParser = require('body-parser'); // parse json body
var cookieParser = require('cookie-parser'); // parse cookies

var router = express.Router();

// URL is relative to route base URL
// GET request (to handle POST request, write router.post)
router.get('/', function(req, res, next) {
     res.send('Tasks main page');
    
     // can return JSON response
     //res.render('index', { title: 'Express' });
});

module.exports = router;


app.js
======
var express = require('express');
var path = require('path');
var tasksRoutes = require('./routes/tasksRoutes');
var app = express();

app.use('/', function (req, res, next) {
     res.send('Site homepage');
});

app.use('/tasks', tasksRoutes);

את כל פונקציות הטיפול נרשום מעכשיו ב tasksRoutes. זו תהיה חלוקה מודולרית של הקוד.

ה router נותן לנו גישה נוחה לחלקים פרמטריים מה URL:

router.get(‘/task/:name/:id?’, function (req,res,next) {
     var name = req.params.name;
     var id = req.params.id;
     var user = req.query.user;
     …
}
כאן, express, בונה לבד את האובייקט req.params ושם בו את הפרמטרים משורת ה URL. הוא גם בונה את req.query ורושם שם את ה query parameters. סימן השאלה ליד id אומר כי השדה הזה הוא אופציונלי.

שלב שלישי – שימוש ב views כדי להחזיר HTML בצורה יותר משוכללת

כאשר המשתמש מבקש דפים שונים אז רוצים להחזיר HTML. אפשר להחזיר ע"י res.send אבל זה מאוד מייגע לבנות HTML בצורה כזו.

לשם כך, express נותן מנגנון של HTML templates שבעזרתם בונים את התשובה. המנגנון הזה נקרא views.

כאשר משתמשים במנגנון הזה, אפשר להגדיר views שונים שלכל אחד יש שם. כל view מגדיר בצורה כלשהו פיסת HTML שאפשר לדחוף לתוכה מידע. כאשר השרת רוצה להחזיר HTML אז יכול להגיד שהוא מחזיר view בשם מסויים ע"י res.render(view name).

כדי להשתמש במנגנון כזה, צריך להגיד ל express את סוג השיטה לתאור ה HTML ואיפה יושבים הקבצים של ה views השונים:

app.js
======
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

השיטה השכיחה ב express לתאר view היא jade. בשיטה זו, במקום HTML רושמים את התגים ללא סימני ‘<’ או '>'.

layout.jade
===========
doctype html
html
  head
    title= title
    link(rel='stylesheet', href='/stylesheets/style.css')
  body
    block content
    
    
index.jade
==========
extends layout

block content
  h1= title
  p Welcome to #{title}

 
error.jade
==========
extends layout

block content
  h1= message
  h2= error.status
  pre #{error.stack}
 
כמה דברים לדעת על פורמט Jade:

-        אפשר לשים תגית block בקובץ. זה אומר שאפשר למלא את הבלוק מקובץ Jade אחר.
-        בקובץ האחר רושמים שהוא מרחיב את הקובץ המקורי ע"י extends

 ועכשיו כאשר רוצים להחזיר HTML אפשר להשתמש ב HTML template:

tasks.jade
==========
extends layout
block content
     ul.list-group
           each task in tasks
                li.list-item
                      li.list-group-item
                      if task.completed
                                      s(id=task.id || inx , data-completed=’true’) = task.title
                              else
                                      a(id=task.id || inx , data-completed=’false’) = task.title
    

tasksRoute.js
=============
var router = require(‘express’).Router();

router.get(‘/’, function(req, res) {
     res.render(‘tasks’);
});

module.exports=router;


שלב רביעי – נשתמש ב jquery ו bootstrap

כדי שהיישום יראה יותר ויהיה יותר קל, נשתמש ב jquery ו bootstrap:
tasks.jade
==========
html
     head
           title This is a Simple Todo List
             link(rel="stylesheet",href="/bower_components/bootstrap/dist/css/bootstrap.min.css")
               link(rel="stylesheet",href="/bower_components/bootstrap/dist/css/bootstrap-theme.min.css")
               script(src="/bower_components/jquery/dist/jquery.min.js")
     body
           div.container
                div.page-Header
                      h1 Express ToDo List
                block content

כדי שהשרת יביא את הקבצים הסטטיים האלה ל client צריך להגיד ל express איפה הקבצים הסטטיים:

app.js
======
app.use(express.statuc(path.join(__dirname,’public’));

זה אומר ל express לחפש קבצים סטטיים בתיקיית public.

שלב חמישי – הוספת טופס להוספת משימות

נוסיף טופס בעזרתו מוסיפים משימות.

נשים את הטופס ב tasks.jade

tasks.jade
==========
extends layout
block content
     ul.list-group
           each task,inx in tasks
                li.list-group-item
                      if task.completed
                              s(id=task._id || inx, data-completed='true')= task.title
                      else
                              a(id=task._id || inx,data-completed='false')= task.title
     div       
       form.form-inline(method="post")
         div.form-group
                 input.form-control(placeholder="Task title",type="text",name="title",required="")
         div.form-group.checkbox
           label
             input(type="checkbox", value="true",name="completed")
      |Completed?
           button.btn.btn-default(type="submit") Submit
     script(src="/javascripts/tasks.js")

ועכשיו נטפל במידע שמגיע מהטופס
tasks.js
=========
router.post("/",function(req,res){
     service.addTask(req.body.title,req.body.completed ? true : false)
     res.render("tasks",{tasks:service.getTasks()});
})



ההקוד המלא: קישור לקוד

Comments

Popular posts from this blog

Best freeware - XML editor

As a software developer, I open XML files all the time. I a heavy commercial XML editor. But nothing can compare to a small, thin and free XML editor like 'foxe'. A great feature is has is the alignment of long XML strings to readable XML format (Shift-F8). It help lot of times when the XML file was generated by some tool and was not readable. Homepage: http://www.firstobject.com/dn_editor.htm

Jenkins error: groovy.lang.MissingPropertyException

I tried to run groovy build step and got below error. This post will describe how I solved the problem. Caught: groovy.lang.MissingPropertyException: No such property: hudson for class: script

SSL in pictures

Here is my summary on SSL (or as I like to call it 'SSL for dummies')