Эта статья продолжение статьи MastremindCMS2 - Как начать?. В ней я рассказывал как установить и настроить community-версию "безголовой" MastermindCMS2.
В этой статье речь пойдет о специальных тегах, которые вы можете использовать для сборки динамических шаблонов в MastermindCMS2.
За свою карьеру программиста я видел множество разных технологий и фреймворков от гигантов индустрии, таких как Oracle, Microsoft, IBM и т. п. Но в каждом из них было какое-то неудобство. А конкретнее у них у всех было одно общее, это необходимость реализовывать серверную логику чтобы можно было использовать ее в шаблонах. И это мне сильно не нравилось, приходилось делать одну и ту же работу из проекта в проект.
Один из таких подходов разнесения логики был паттерн программирования MVVM(Model-View-ViewModel). Его активно продвигали во фреймворках для C#. Структурно с точки зрения разнесения логики, я считаю этот паттерн самым удобным.
Когда я поставил задачу себе разработать удобный фреймворк для работы, то у меня в голове был только один концепт реализации. Который содержал в основе именно MVVM, но я не считал, что это то, что я хочу получить в итоге. Так как проблема написания серверной логики оставалась открытой. И я поставил себе задачу спроектировать фреймворк так чтобы не нужно было каждый раз писать бэк для очередного проекта. Ведь грубо говоря, мы с вами коллеги, решаем одни и те же задачи для разных проектов. Авторизация, управление каталогом продуктов, отправка писем по электронной почте, чаты и прочая энтерпрайз муть. Соответственно опираясь на эти задачи, я начал думать, что нужно сделать чтобы напрямую оперировать с этим на бэке. В результате я решил писать логику парсинга шаблонов на беке, а фронтенд часть делать гибкой и независимой.
Первым шагом я продумал маршрутизацию для обработки HTTP-запросов. Основной задачей было сделать обработку GET-запроса и при этом достать шаблон, где уже будет логика, состоящая из кастомных тегов, которые динамически соберут валидный HTML и отдадут его назад.
Основой для проектирования тегов я выбрал самые частые задачи, которые приходиться решать программисту разрабатывая пользовательский интерфейс. К частым задачам в проектировании пользовательского интерфейса я отношу следующие задачи:
Разбиение визуальных блоков на компоненты, и подключение их на страницы
Получение данных со сторонних микро-сервисов и отображение их на странице
Итеративное отображение данных
Отображения одиночных объектов из базы данных
Логические операции и отрисовка данных на основе выполнения условия отображения
Отображение данных в виде дерева
Расширение возможностей отображения выпадающего списка на основе HTML-элемента select
Отображение текстовых данных напрямую из базы данных
И в итоге я получил 8 специальных тегов которые решают вышеописанные задачи. А теперь по порядку я расскажу на примере этих задач как можно проектировать страницы с использованием тегов фреймворка MastermindCMS2.
Подключение фрагмента
Разработка фронтенд-приложения на MastermindCMS2 не отличается ничем от принципов разработки на других фреймворках. Фронтенд часть полностью автономна, и вам не нужно собирать приложение для того чтобы бекенд-логика заработала у вас в приложении. Вот несколько шагов демонстрирующих принципы разработки на нашей headless-cms.
<msm:fragment>
- тег предназначен для использования внешних фрагментов HTML-кода для подключения их на страницы.
Для начала создадим новый файл.
Скопируем все содержимое страницы в новый файл
Далее мы удалим некоторые части HTML, для того чтобы их перенести отдельно во внешние фрагменты.
Обратимся к документации на сайте MastermindCMS2. Для примера мы используем
<msm:fragment>
<msm:fragment id="navbar" path="admin/components/navbar.html"/>
- этот код вам нужно добавить на страницуСоздаем новый файл для фрагмента.
Подключаем созданный нами фрагмент на страницу.
Запросы на внешний REST API
<msm:rest>
- тег предназначен для выполнения запроса на внешний ресурс, который доступен по REST API и возвращает данные в виде JSON.
Исходный код:
<div class="container">
<h1>Star Wars Ships</h1>
<ul id="starWarsRestApiWrapper1" class="container">
<msm:rest id="starWarsRestApi"
endpoint="https://swapi.dev/api/starships/9/?format=json"
request="{}" method="GET" item-name="ship">
<msm:template><li>${ship|name}</li></msm:template>
</msm:rest>
</ul>
<h1>Locations</h1>
<ul id="locationsApiWrapper2" class="container">
<msm:rest id="locationsApi"
endpoint="https://ghibliapi.herokuapp.com/locations"
request="{}" method="GET" item-name="loc">
<msm:template><li>${loc|value.name}</li></msm:template>
</msm:rest>
</ul>
</div>
Итерирование - цикл foreach
msm:foreach
- тег позволяет сделать итерацию по элементам и отобразить их как повторяющийся HTML-элемент, который определен во вложенном теге msm:template
.
Исходный код:
<form>
<div class="form-group">
<label for="inputFirstName">First Name</label>
<input type="text" class="form-control" name="firstName" id="inputFirstName" autocomplete="off" placeholder="First Name">
</div>
<div class="form-group">
<label for="inputLastName">Last Name</label>
<input type="text" class="form-control" name="lastName" id="inputLastName" autocomplete="off" placeholder="Last Name">
</div>
<div class="form-group form-check">
<input type="checkbox" class="form-check-input" name="status" value="false" id="checkStatus">
<label class="form-check-label" for="checkStatus">Check me out</label>
</div>
<button type="button" class="btn btn-primary" onclick="app.addDocumentAndRender('foo','custom-users',this)">Submit</button>
</form>
<div id="userListWrapper" class="row gx-lg-5">
<msm:foreach id="userList"
database="foo"
collection="custom-users"
filter="{}"
mode="DATABASE"
item-name="user">
<msm:empty>
<p>No users</p>
</msm:empty>
<msm:template>
<div class="col-lg-6 col-xxl-4 mb-5">
<div class="card bg-light border-0 h-100">
<div class="card-body text-center p-4 p-lg-5 pt-0 pt-lg-0">
<div class="feature bg-primary bg-gradient text-white rounded-3 mb-4 mt-n4"><i class="bi bi-collection"></i></div>
<h2 class="fs-4 fw-bold">${user|firstName} ${user|lastName}</h2>
<p class="mb-0">${user|_id}</p>
</div>
</div>
</div>
</msm:template>
</msm:foreach>
</div>
Отображение объектов - элемент block
msm:block
- тег позволяет выводить объект на основе определенного HTML-элемента внутри вложенного тега msm:template
.
В этом примере видно как входящий параметр от внешнего тега может быть использован на внутренних тегах:
<div id="userBlockWrapper">
<msm:block id="userBlock"
value="{ fullName: '${user|firstName} ${user|lastName}', movie : 'Last Hope'}"
item-name="usr">
<msm:template><h6 style="text-align:center">${usr|fullName} - ${usr|movie}</h6></msm:template>
</msm:block>
</div>
Исходный код:
<div id="userListWrapper" class="row gx-lg-5">
<msm:foreach id="userList"
database="foo"
collection="custom-users"
filter="{}"
mode="DATABASE"
item-name="user">
<msm:empty>
<p>No users</p>
</msm:empty>
<msm:template>
<div class="col-lg-6 col-xxl-4 mb-5">
<div class="card bg-light border-0 h-100">
<div class="card-body text-center p-4 p-lg-5 pt-0 pt-lg-0">
<div class="feature bg-primary bg-gradient text-white rounded-3 mb-4 mt-n4"><i class="bi bi-collection"></i></div>
<h2 class="fs-4 fw-bold">${user|firstName} ${user|lastName}</h2>
<p class="mb-0">${user|_id}</p>
</div>
<div id="userBlockWrapper">
<msm:block id="userBlock"
value="{ fullName: '${user|firstName} ${user|lastName}', movie : 'Last Hope'}"
item-name="usr">
<msm:template><h6 style="text-align:center">${usr|fullName} - ${usr|movie}</h6></msm:template>
</msm:block>
</div>
</div>
</div>
</msm:template>
</msm:foreach>
</div>
Логический оператор "если"
msm:if
- тег позволяет выводить объект на основе условия, которое будет проверяться в атрибуте test.
Для примера изменим свойство в объектах следующим образом:
Исходный код:
<div id="userListWrapper" class="row gx-lg-5">
<msm:foreach id="userList"
database="foo"
collection="custom-users"
filter="{}"
mode="DATABASE"
item-name="user">
<msm:empty>
<p>No users</p>
</msm:empty>
<msm:template>
<div class="col-lg-6 col-xxl-4 mb-5">
<div class="card bg-light border-0 h-100">
<div class="card-body text-center p-4 p-lg-5 pt-0 pt-lg-0">
<div class="feature bg-primary bg-gradient text-white rounded-3 mb-4 mt-n4"><i class="bi bi-collection"></i></div>
<h2 class="fs-4 fw-bold">${user|firstName} ${user|lastName}</h2>
<p class="mb-0">${user|_id}</p>
</div>
<div id="showUserBlockWrapper">
<msm:if id="showUserBlock" test="${user|status}">
<div id="userBlockWrapper">
<msm:block id="userBlock"
value="{ fullName: '${user|firstName} ${user|lastName}', movie : 'Last Hope'}"
item-name="usr">
<msm:template><h6 style="text-align:center">${usr|fullName} - ${usr|movie}</h6></msm:template>
</msm:block>
</div>
</msm:if>
</div>
</div>
</div>
</msm:template>
</msm:foreach>
</div>
Отображение элемента в виде дерева
msm:tree
- тег позволяет выполнить итерацию по элементам и отобразить их в виде древовидной HTML-структуры. Каждый вложенный элемент, имеющий подэлементы, должен содержать свойство children для отображения в виде элемента вложенного поддерева. Элемент для элемента итерации определяется во вложенном теге msm:template
.
Исходный код:
<div class="dd" id="product-groups-tree">
<msm:tree id="productGroups"
database="msm2-application"
collection="categories"
filter="{}"
mode="DATABASE" item-name="cat">
<msm:template>
<ol class="dd-list">
<li class="dd-item dd3-item" data-id="${cat|id}" data-name="${cat|name}">
<div class="dd-handle dd3-handle"></div>
<div class="dd3-content">
<a href="#product-group/view/${cat|id}" class="truncate">${cat|name}</a>
</div>
<a class="dd3-right-handle modal-trigger" href="#remove-pgroup-dialog" data-item-id="${cat|id}"></a>
</li>
</ol>
</msm:template>
</msm:tree>
</div>
Выпадающий список
msm:select
- тег выполняет рендеринг для на основе определения HTML-шаблона во вложенном теге msm:template
в качестве дочернего элемента.
Исходный код:
<div id="selectTagWrapper" class="root">
<msm:select id="selectTag"
database="foo"
collection="custom-users"
filter="{}"
selected="Darth"
mode="DATABASE" item-name="option" class="select-file-types">
<msm:template><option class="item" value="${option|firstName}" ${option|selected}>${option|firstName} ${option|lastName}</option></msm:template>
</msm:select>
</div>
Получение текстовых значений из базы данных
msm:text
- тег позволяет отображать свойства объекта в виде текста, который определяется согласно шаблону объявленному внутри.
<div id="textTagWrapper">
<msm:text id="textTag"
bean="dummyBean"
function="getDummyUser"
scope="PROTOTYPE"
mode="BEAN" item-name="emp">
${emp|name} - ${emp|age}
</msm:text>
</div>
Заключение
Все 8 специальных тегов были рассмотрены в статье с примерами. Я пострарался максимально подробно рассказать как все-таки это работает. В следующей статье я расскажу как использовать эти теги в связке с JavaScript-частью от данной технологии. Дата-байдинг, роутинг уже реализованы на уровне фреймворка, поэтому вам не придется писать тонны JS чтобы сделать интеграции с базой данных, и вам не нужно писать бэкенд часть для REST API. Основные все инструменты уже реализованы в фреймворке.
Как обычно напишу планы на развитие данной технолгии. Я думаю основной задачей для меня сейчас, стоит задача это создание маркетплейса для всех программистов, которые смогут там продавать свой модули и шаблоны написанные на этом фреймворке.
Я делюсь этой информацией тут, потому-что я хочу чтобы рутинная разработка веб-приложений превратилась в искусство шаблонизации. Где вы можете собрать в короткие сроки сложные платформы с поиском и интеграциями как простой конструктор LEGO.
Я делюсь с вами прежде своим инструментом, который мне сейчас позволяет работать быстрее и эффективнее. Но без вас я не смогу сделать это. И это хоть на грамм, на капельку сделает нашу жизнь программиста проще.
Спасибо что дочитали мою статью до конца, продолжение следует...