echo Impossible|sed 's/Im/To be /'

December 12, 2017

Lisp Web - Parenscript Vue.js Hunchentoot - Hello World

想用 Lisp 寫 Web 所以有了這篇,以下內容主要參考下列文件並加上其它資料匯整而來。

本篇適合入門快速體驗、開發測試。實務上線建議搭其它 web framework 如 caveman2 node.js 或 Npm Browserify 等工具使用。

Parenscript Tutorial

(ql:quickload "hunchentoot")
(ql:quickload "cl-who")
(ql:quickload "parenscript")
(ql:quickload "css-lite")

(defpackage "PS-TUTORIAL"
  (:use "COMMON-LISP" "HUNCHENTOOT" "CL-WHO" "PARENSCRIPT"))
(in-package "PS-TUTORIAL")

;; web server 開啟於 127.0.0.1 8080 port 上
(defparameter *server*
  (start (make-instance 'easy-acceptor :address "localhost" :port 8080)))

;; 關閉/重啟 server
;; (stop *server*)
;; (start *server*)

(setf *js-string-delimiter* #\')
;; (setf *js-string-delimiter* #\")

(setf *prologue* "<!DOCTYPE html>")
(setf (html-mode) :HTML5)

;; 排版設定
(setq *PS-Print-pretty* t)
(setq *INdent-num-spaces* 4)

(define-easy-handler (tutorial1 :uri "/tutorial1") ()
  (with-html-output-to-string (s nil :prologue t :indent t) ;; prologue/indent 設定 cl-who 輸出排版後 HTML
    (:html :lang "en"
     (:head (:title "Parenscript tutorial: 1st example"))
     (:body (:h2 "Parenscript tutorial: 1st example")
        "Please click the link below." :br
        (:a :href "#" :onclick (ps-inline (alert "Hello World"))
            "Hello World")))))

(define-easy-handler (tutorial2 :uri "/tutorial2") ()
  (with-html-output-to-string (s nil :prologue t :indent t)
    (:html :lang "en"
     (:head
      (:title "Parenscript tutorial: 2nd example")
      (:script :type "text/javascript"
           (str (ps
                  (defun greeting-callback ()
                    (alert "Hello World"))))))
     (:body
      (:h2 "Parenscript tutorial: 2nd example")
      (:a :href "#" :onclick (ps (greeting-callback))
      "Hello World")))))

(define-easy-handler (tutorial3 :uri "/tutorial3") ()
  (with-html-output-to-string (s nil :prologue t :indent t)
    (:html
     (:head
      (:title "Parenscript tutorial: 3nd example")
      (:script :src "/tutorial3.js"))
     (:body
      (:h2 "Parenscript tutorial: 3nd example")
      (:a :href "#" :onclick (ps (greeting-callback))
      "Hello World")))))

(define-easy-handler (tutorial3-javascript :uri "/tutorial3.js") ()
  (setf (content-type*) "text/javascript")
  (ps
    (defun greeting-callback ()
      (alert "Hello World"))))

以上引用 Parenscript Tutorial 的程式來說明,其中幾個套件說明如下:

  • hunchentoot : 高效能 web server
  • cl-who:with-heml-output-to-string : html code 翻譯/產生器
  • parenscript:ps : javascript code 翻譯/產生器

測試可用 curl/wget/firefox/chrome

http://127.0.0.1:8080/tutorial1
http://127.0.0.1:8080/tutorial2
http://127.0.0.1:8080/tutorial3

Parenscript Vue.js - Json & Symbol Coversion

接續 Parenscript Tutorial,以下引入 Vue.js 作為前端開發 framework,其內容參考下列文件改寫並加上其它資料匯整。

;; https://nowills.blogspot.tw/2016/08/vue-js.html
(define-easy-handler (vue-hello :uri "/vue-hello") ()
  (with-html-output-to-string (s nil :prologue t :indent t)
    (:html
     (:head (:meta :charset "UTF-8")(:title "0 Hello World"))
     (:body
      (:div :id "app" "{{ message }}")      ;; div tag id="app"
      (:script :src "https://cdnjs.cloudflare.com/ajax/libs/vue/2.0.1/vue.min.js")
      (:script
       :type "text/javascript"
       (str (ps
          (new (*vue (create el "#app"  ;; 建立 Vue function 把 json 資料傳進去
                             data (create message "Hello World!")))))))))))


;; https://nowills.blogspot.tw/2016/08/vue-js-real-time.html
(define-easy-handler (vue-hello-input :uri "/vue-hello-input") ()
  (with-html-output-to-string (s nil :prologue t :indent t)
    (:html
     (:head (:meta :charset "UTF-8") (:title "0 Hello World"))
     (:body
      (:div :id "app" (:h1 "{{ message }}")  ;; div tag id="app"
        (:input :type "text" :v-model "message")
        (:div "{{ $data | json }}"))     ;; {{ $data | json }}
      (:script :src "https://cdnjs.cloudflare.com/ajax/libs/vue/2.0.1/vue.min.js")
      (:script
       :type "text/javascript"
       (str (ps
          (let ((data (create message "Hello World!"))) ;; 建立名為 data 的 json 資料
            (new (*vue (create el "#app"
                               data data)))))))))))     ;; 把 data 引入 data


;; https://nowills.blogspot.tw/2016/08/vue-js-if-elsev-show.html
(define-easy-handler (vue-v-show :uri "/vue-v-show") ()
  (with-html-output-to-string (s nil :prologue t :indent t)
    (:html
     (:head (:meta :charset "UTF-8") (:title "2 v-show"))
     (:body
      (:div :id "app"
        (:h1 :class "error" "{{ score }}分")
        (:div :v-show "score"
              (:p :v-if "score >= 6" "Vue.js so easy")
              (:p :v-else t "still learning Vue.js"))
        (:p "你覺得 vue.js 簡單嗎?請輸入 1-10 分")
        (:input :type "text" :v-model "score"))
      (:script :src "https://cdnjs.cloudflare.com/ajax/libs/vue/2.0.1/vue.min.js")
      (:script
       :type "text/javascript"
       (str (ps
          (new (*vue (create el "#app"
                             data (create score "0")))))))))))


;; https://nowills.blogspot.tw/2016/08/vue-js-vuejsv-onclick.html
(define-easy-handler (vue-click :uri "/vue-click") ()
  (with-html-output-to-string (s nil :prologue t :indent t)
    (:html
     (:head (:meta :charset "UTF-8") (:title "3 click"))
     (:body
      (:div :id "app"
        (:p "目前以點擊:{{count}}次")
        (:input :type "submit" :value "立即送出" :v-on\:click "clickme"))
      (:script :src "https://cdnjs.cloudflare.com/ajax/libs/vue/2.0.1/vue.min.js")
      (:script
       :type "text/javascript"
       (str (ps
          (new (*vue (create el "#app"
                             data (create count 0)
                             methods (create clickme
                                             (lambda () (incf (@ this count))))))))))))))

(define-easy-handler (vue-click-1 :uri "/vue-click-1") ()
  (with-html-output-to-string (s nil :prologue t :indent t)
    (:html
     (:head (:meta :charset "UTF-8") (:title "3 click"))
     (:body
      (:div :id "app"
        (:button :v-on\:click "handleIt(\"uh\")" "I say uh")
        (:button :v-on\:click "handleIt(\"ah\")" "you say ah"))
      (:script :src "https://cdnjs.cloudflare.com/ajax/libs/vue/2.0.1/vue.min.js")
      (:script
       :type "text/javascript"
       (str (ps
          (new (*vue (create el "#app"
                             data (create count 0)
                             methods (create handle-it
                                             (lambda (msg) (alert msg)))))))))))))

幾個教學範例後,您首先會面臨的是如何用 PS (Parenscript) 來建立 json - javascript object。另外會困擾人的是 JS function name 大小寫的問題。 由於 CL (common lisp) 語言預設 function/變數 全部大寫。故請詳閱 Symbol conversion 這個章節,用 「-/*」 來區分 JS function/變數 大小寫的問題。

Parenscript Vue.js - css-lite v-for v-bind

餘下的範例引入 css-lite:css

;;https://nowills.blogspot.tw/2016/10/vue-js-for-loop.html
(define-easy-handler (9x9 :uri "/9x9") ()
  (with-html-output-to-string (s)
    (:html (:head (:meta :charset "UTF-8") (:title "9 x 9"))
     (:body (:div :id "app"
                  (:div :v-for "i in 9"
                        (:h3 "{{ i }}")
                        (:div :v-for "j in 9" "{{ i }} x {{ j }} = {{ i * j }}")))
            (:script :src "https://cdnjs.cloudflare.com/ajax/libs/vue/2.0.1/vue.min.js")
            (:script
             :type "text/javascript"
             (str (ps
                    (new (*vue (create el "#app"))))))))))

(define-easy-handler (fruit-list :uri "/fruit-list") ()
  (with-html-output-to-string (s)
    (:html
     (:head (:meta :charset "UTF-8") (:title "fruit list"))
     (:body
    (:div :id "app"
      (:ul
       (:li :v-for "item in items" "{{ item.message }}")))
    (:script :src "https://cdnjs.cloudflare.com/ajax/libs/vue/2.0.1/vue.min.js")
    (:script
     :type "text/javascript"
     (str (ps
        (new (*vue (create el "#app"
                           data (create items
                                        (array (create message "Apple")
                                               (create message "Banana")
                                               (create message "Coconut")))))))))))))


(define-easy-handler (index-items :uri "/index-items") ()
  (with-html-output-to-string (s)
    (:html
     (:head (:meta :charset "UTF-8") (:title "index items"))
     (:body
    (:div :id "app"
      (:ul
       (:li :v-for "(item, index) in items" "{{ parentMessage }} - {{ index }} - {{ item.message }}")))

    (:script :src "https://cdnjs.cloudflare.com/ajax/libs/vue/2.0.1/vue.min.js")
    (:script
     :type "text/javascript"
     (str (ps
        (new (*vue (create el "#app"
                           data (create parent-message "台北市"
                                        items
                                        (array (create message "中正區")
                                               (create message "中山區")
                                               (create message "大同區")))))))))))))

;;https://nowills.blogspot.tw/2016/12/vue-js-v-bind.html
(define-easy-handler (red-green-123 :uri "/red-green-123") ()
  (with-html-output-to-string (s nil :prologue t :indent t)
    (:html (:head (:meta :charset "UTF-8") (:title "red green 123")
            (:style (str (css-lite:css ((".active")(:color "red" :font-size "30px")) ;; add css-lite:css
                                       ((".normal")(:color "green" :font-size "30px"))))))
     (:body (:div :id "app"
                  (:div :v-bind\:class "{ active: myCheck , normal: myCheck2 }" "123")

            (:script :src "https://cdnjs.cloudflare.com/ajax/libs/vue/2.0.1/vue.min.js")
            (:script
             :type "text/javascript"
             (str (ps
                    (new (*vue (create el "#app"
                                       data (create my-check nil
                                                    my-check2 t))))))))))))

Posted by Lloyd Huang in on December 12, 2017