Некоторое время назад я искал хорошие способы выполнения клиентской маршрутизации и шаблонов в 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))
Что происходит?
- Мы определяем два шаблона Enfocus.
view-add
тривиально и просто возвращает весь шаблон.view-browse
немного интереснее: учитывая имя категории, измените шаблон, заменив содержимоеh1
тега именем категории. - Затем мы определяем маршруты секретаря для фактического использования этих шаблонов. Все, что они делают сейчас, это заменяют содержимое
#view
элемента шаблоном. В случае маршрута «Обзор» он передает проанализированное имя категории из пути к шаблону. - Существует маршрут по умолчанию, который перенаправляет с
/
на/add
. Это не приводит кexample.com/add
, но только задает фрагмент:example.com/#/add
. - Обратите внимание, что все шаблоны заключены в
em/wait-for-load
. Этот бит необходим для Enfocus, если вы загружаете шаблоны с помощью вызовов AJAX, и я пока не нашел способа сделать это один раз для каждого приложения. Это была самая трудная часть, чтобы добраться до работы, и я все еще смущен этим. - Наконец, мы подключаем к секретарю
goog.History
. Я не уверен, почему это не в коробке, но это достаточно просто.