Статьи

ClojureScript Маршрутизация и создание шаблонов с секретарем и Enfocus

Некоторое время назад я искал хорошие способы выполнения клиентской маршрутизации и шаблонов в ClojureScript. Я исследовал, используя несколько JavaScript-фреймворков из ClojureScript, из которых Angular, вероятно, дал наиболее многообещающие результаты, но все еще чувствовал себя немного грязным и тяжелым. Я даже реализовал свой собственный механизм маршрутизации / goog.History , основанный на Pedestal и goog.History , но что-то все равно не так

Ситуация изменилась, и сегодня есть много шума о библиотеках на основе React, таких как Reagent и Om . Я подозреваю, что React на передней панели с кучей «нативных» библиотек ClojureScript может быть лучшим способом.

Прежде чем я доберусь туда, я хочу вернуться к маршрутизации и шаблонам. Давайте посмотрим, как мы можем объединить две симпатичные библиотеки: секретарь для маршрутизации и Enfocus для шаблонов.

Допустим, у нашего приложения есть два экрана, которые заполняют всю страницу. Нет различных «фрагментов», из которых можно составить страницу. Мы хотим видеть одну страницу, когда перейдем к /#/add а другую в /#/browse . Страница «Обзор» будет немного более продвинутой и поддерживает параметры пути. Например, для /#/browse/Stuff мы хотим проанализировать «Stuff» и отобразить заголовок с этим словом.

Основной HTML может выглядеть так:

01
02
03
04
05
06
07
08
09
10
<!DOCTYPE html>
<html>
<body>
    <div class="container-fluid">
        <div id="view">Loading...</div>
    </div>
 
    <script src="js/main.js"></script>
</body>
</html>

Тогда у нас есть два шаблона.

add.html:

1
2
3
4
<h1>Add things</h1>
<form>
  <!-- boring, omitted -->
</form>

browse.html:

1
2
3
4
<h1></h1>
<div>
  <!-- boring, omitted -->
</div>

Теперь все, что мы хотим сделать, это заполнить элемент #view на главной странице одним из шаблонов при изменении местоположения. Полный код для этого ниже.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
(ns my.main
  (:require [secretary.core :as secretary :include-macros true :refer [defroute]]
            [goog.events :as events]
            [enfocus.core :as ef])
  (:require-macros [enfocus.macros :as em])
  (:import goog.History
           goog.History.EventType))
 
(em/deftemplate view-add "templates/add.html" [])
 
(em/deftemplate view-browse "templates/browse.html" [category]
  ["h1"] (ef/content category))
 
(defroute "/" []
  (.setToken (History.) "/add"))
 
(defroute "/add" []
  (em/wait-for-load
    (ef/at
      ["#view"] (ef/content (view-add)))))
 
(defroute "/browse/:category" [category]
  (em/wait-for-load
    (ef/at
      ["#view"] (ef/content (view-browse category)))))
 
(doto (History.)
  (goog.events/listen EventType/NAVIGATE #(secretary/dispatch! (.-token %)))
  (.setEnabled true))

Что происходит?

  1. Мы определяем два шаблона Enfocus. view-add тривиален и просто возвращает весь шаблон. view-browse немного интереснее: учитывая имя категории, измените шаблон, заменив содержимое тега h1 именем категории.
  2. Затем мы определяем маршруты секретаря для фактического использования этих шаблонов. Все, что они сейчас делают — это заменяют содержимое элемента #view шаблоном. В случае маршрута «Обзор» он передает проанализированное имя категории из пути к шаблону.
  3. Существует маршрут по умолчанию, который перенаправляет с / на /add . Это не приводит к example.com/add , а только устанавливает фрагмент: example.com/#/add .
  4. Наконец, мы подключаем секретаря к goog.History . Я не уверен, почему это не в коробке, но это достаточно просто.
  5. Обратите внимание, что в обработчике истории есть вызов em/wait-for-load . Это необходимо для Enfocus, если вы загружаете шаблоны с вызовами AJAX.

Вот и все, очень просто и понятно.

Обновление: исправлено размещение em/wait-for-load , большое спасибо Адриану!