Sortable Table in the Database
Subscribe to a paid plan to get instant access to this lesson.
Download  Video time: 24m52s

Sortable Table in the Database

Try it out yourself (editable code)


(ns student.sortable-table-db
  (:require [reagent.core :as reagent]
            [re-frame.core :as rf]))

(def data
  [["Name" "Weapon" "Side" "Height (m)"]
   ["Luke Skywalker" "Blaster" "Good" 1.72]
   ["Leia Organa" "Blaster" "Good" 1.5]
   ["Han Solo" "Blaster" "Good" 1.8]
   ["Obi-Wan Kenobi" "Light Saber" "Good" 1.82]
   ["Chewbacca" "Bowcaster" "Good" 2.28]
   ["Darth Vader" "Light Saber" "Bad" 2.03]
   ])

(rf/reg-event-db
  :initialize
  (fn [_ _]
    {:tables {:new-hope {:header (first data)
                         :rows (rest data)}}}))

(rf/reg-sub
  :table
  (fn [db [_ key]]
    (get-in db [:tables key])))

(defn sortable-table [table-key]
  (let [s (reagent/atom {})]
    (fn [table-key]
      (let [table @(rf/subscribe [:table table-key])
            key (:sort-key @s)
            dir (:sort-direction @s)
            rows (cond->> (:rows table)
                          key (sort-by #(nth % key))
                          (= :ascending dir) reverse)
            sorts [key dir]]
        [:table {:style {:font-size "80%"}}
         [:tr
          (for [[i h] (map vector (range) (:header table))]
            [:th
             {:on-click #(cond
                           (= [i :ascending] sorts)
                           (swap! s dissoc
                                  :sort-direction :sort-key)
                           (= [i :descending] sorts)
                           (swap! s assoc
                                  :sort-direction :ascending)
                           :else
                           (swap! s assoc
                                  :sort-key i
                                  :sort-direction :descending))}
             [:div {:style {:display :inline-block}}
              h]
             [:div {:style {:display :inline-block
                            :line-height :1em
                            :font-size :60%}}
              [:div
               {:style {:color (if (= [i :descending] sorts)
                                 :black
                                 "#aaa")}}
               "▲"]
              [:div
               {:style {:color (if (= [i :ascending] sorts)
                                 :black
                                 "#aaa")}}
               "▼"]]])]
         (for [row rows]
           [:tr
            (for [v row]
              [:td v])])]))))

(defn ui []
  [:div
   [sortable-table :new-hope]])

(defonce _init (rf/dispatch-sync [:initialize]))
(reagent/render [ui] (js/document.getElementById "student-container"))

Here's my version (editable code)


(ns teacher.sortable-table-db
  (:require [reagent.core :as reagent]
            [re-frame.core :as rf]))

(def data
  [["Name" "Weapon" "Side" "Height (m)"]
   ["Luke Skywalker" "Blaster" "Good" 1.72]
   ["Leia Organa" "Blaster" "Good" 1.5]
   ["Han Solo" "Blaster" "Good" 1.8]
   ["Obi-Wan Kenobi" "Light Saber" "Good" 1.82]
   ["Chewbacca" "Bowcaster" "Good" 2.28]
   ["Darth Vader" "Light Saber" "Bad" 2.03]
   ])

(rf/reg-event-db
 :initialize
 (fn [_ _]
   {:tables {:new-hope {:header (first data)
                        :rows (rest data)}}}))

(rf/reg-sub
 :table
 (fn [db [_ key]]
   (get-in db [:tables key])))

(rf/reg-sub
 :table-sorted
 (fn [[_ key] _]
   (rf/subscribe [:table key]))
 (fn [table]
   (let [key (:sort-key table)
         dir (:sort-direction table)
         rows (cond->> (:rows table)
                key (sort-by #(nth % key))
                (= :ascending dir) reverse)]
     (assoc table :rows rows))))

(rf/reg-event-db
 :table-sort-by
 (fn [db [_ key i dir]]
   (update-in db [:tables key]
              assoc :sort-key i :sort-direction dir)))

(rf/reg-event-db
 :table-clear-sort
 (fn [db [_ key]]
   (update-in db [:tables key]
              dissoc :sort-key :sort-direction)))

(rf/reg-event-fx
 :table-rotate-sort
 (fn [{:keys [db]} [_ key i]]
   (let [{:keys [sort-key sort-direction]} (get-in db [:tables key])
         sorts [sort-key sort-direction]]
     {:dispatch (cond
                  (= [i :ascending] sorts)
                  [:table-clear-sort key]

                  (= [i :descending] sorts)
                  [:table-sort-by key i :ascending]

                  :else
                  [:table-sort-by key i :descending])})))

(defn sortable-table [table-key]
  (let [table @(rf/subscribe [:table-sorted table-key])
        sorts [(:sort-key table) (:sort-direction table)]]
    [:table {:style {:font-size "80%"}}
     [:tr
      (for [[i h] (map vector (range) (:header table))]
        [:th
         {:on-click #(rf/dispatch [:table-rotate-sort table-key i])
          :style {:cursor :default}}
         [:div {:style {:display :inline-block}}
          h]
         [:div {:style {:display :inline-block
                        :line-height :1em
                        :font-size :60%}}
          [:div
           {:style {:color (if (= [i :descending] sorts)
                             :black
                             "#aaa")}}
           "▲"]
          [:div
           {:style {:color (if (= [i :ascending] sorts)
                             :black
                             "#aaa")}}
           "▼"]]])]
     (for [row (:rows table)]
       [:tr
        (for [v row]
          [:td v])])]))

(defn ui []
  [:div
   [sortable-table :new-hope]
   [sortable-table :new-hope]])

(defonce _init (rf/dispatch-sync [:initialize]))
(reagent/render [ui] (js/document.getElementById "teacher-container"))

Next lesson: Markdown Editor with Live Preview

We build a small markdown editor with live preview. Read more.

Sortable Table in the Database