Статьи

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

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

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

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

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

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

<!DOCTYPE html>
<html>
<body>
<div class="container-fluid">
<div id="view">Loading...</div>
</div>
<script src="js/main.js"></script>
</body>
</html>

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

add.html:

<h1>Add things</h1>
<form>
<!-- boring, omitted -->
</form>

browse.html:

<h1></h1>
<div>
<!-- boring, omitted -->
</div>

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

(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. Обратите внимание, что все шаблоны заключены в  em/wait-for-load. Этот бит необходим для Enfocus, если вы загружаете шаблоны с помощью вызовов AJAX, и я пока не нашел способа сделать это один раз для каждого приложения. Это была самая трудная часть, чтобы добраться до работы, и я все еще смущен этим.
  5. Наконец, мы подключаем к секретарю  goog.History. Я не уверен, почему это не в коробке, но это достаточно просто.