Skip to content

Instantly share code, notes, and snippets.

@bamboospirit
Last active December 31, 2020 12:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bamboospirit/33f6b32f21db22fd48e3724648cf33dc to your computer and use it in GitHub Desktop.
Save bamboospirit/33f6b32f21db22fd48e3724648cf33dc to your computer and use it in GitHub Desktop.
weblocks parallel rendering issue
(ql:quickload '(:weblocks :weblocks-ui :find-port))
#|
how to reproduce the bug/issue:
Click on the "render many" button and wait for it to finish rendering.
Then click again on "render many" and without any delay click on "render few"
You will see that AFTER "render few" (the last action from the UI) has finished doing its work, 
the results will be replaced by an older action from the UI ... 
How to avoid this? In a filtering widget for example, typing a word fastly will result in the wrong filtration of a long list. 
(after if finishes rendering the newest results according to the last input word, it will then render the previous results from the previous UI action when
the word was only partially typed into the box)
|#
(defpackage todo
           (:use #:cl
                 #:weblocks-ui/form
                 #:weblocks/html)
           (:import-from #:weblocks/widget
                    #:render
                    #:update
                    #:defwidget)
           (:import-from #:weblocks/actions
                    #:make-js-action)
           (:import-from #:weblocks/app
                    #:defapp))
(in-package todo)
(defapp tasks)
(defapp tasks
         :prefix "/")
(weblocks/debug:on)
(defparameter *port* 4001)
(weblocks/server:start :port *port*)
(defmethod weblocks/session:init ((app tasks))
            "Hello world!")
(defwidget task ()
        ((title
          :initarg :title
          :accessor title)
         (done
          :initarg :done
          :initform nil
          :accessor done)))
(defun make-task (title &key done)
        (make-instance 'task :title title :done done))
(defwidget task-list ()
        ((tasks
          :initarg :tasks
          :accessor tasks)))
(defmethod render ((task task))
        "Render a task."
        (with-html
              (:span (if (done task)
                         (with-html
                               (:s (title task)))
                       (title task)))))
(defmethod render ((widget task-list))
        "Render a list of tasks."
        (with-html
              (:h1 "Tasks")
              (:ul
                (loop for task in (tasks widget) do
                      (:li (render task))))))
(defun make-task-list (&rest rest)
        (let ((tasks (loop for title in rest
                        collect (make-task title))))
          (make-instance 'task-list :tasks tasks)))
(defmethod add-task ((task-list task-list) title)
        (push (make-task title)
              (tasks task-list))
  (update task-list))
(defvar +ascii-alphabet+
  "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
  "All letters in 7 bit ASCII.")
(defun random-string (&optional (length 32) (alphabet +ascii-alphabet+))
  "Returns a random alphabetic string.
The returned string will contain LENGTH characters chosen from
the vector ALPHABET.
"
  (loop with id = (make-string length)
        with alphabet-length = (length alphabet)
        for i below length
        do (setf (cl:aref id i)
                 (cl:aref alphabet (random alphabet-length)))
        finally (return id)))
(defmethod render ((task-list task-list))
        (with-html
          (:h1 "Tasks")
      (:input :type "button" :value "render many" :onclick
          (weblocks/actions:make-js-action
           (lambda (&rest args)
             (setf (tasks task-list)
               (loop for i from 0 to 1000 collect
                               (make-task (format nil "~A. ~A" i (random-string )))))
             (update task-list))))
      (:input :type "button" :value "render few" :onclick
          (weblocks/actions:make-js-action
           (lambda (&rest args)
             (setf (tasks task-list)
               (list (make-task "task1")
                 (make-task "task2")
                 (make-task "task3")))
             (update task-list))))
          (loop for task in (tasks task-list) do
            (render task))
      (with-html-form (:POST (lambda (&key title &allow-other-keys)
                                         (add-task task-list title)))
            (:input :type "text"
                    :name "title"
                    :placeholder "Task's title")
            (:input :type "submit"
                    :value "Add"))))
(defparameter *task-list* (make-task-list "Make my first Weblocks app"
                         "Deploy it somewhere"
                         "Have a profit"))
(defmethod weblocks/session:init ((app tasks))
         (declare (ignorable app))
         *task-list*)
(weblocks/debug:reset-latest-session)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment