
В предыдущей части мы разобрали, как заполнить инклуды контентом из дополнительных страниц, разместив на них компонент Primefaces Data Table Filter. Теперь в моем приложении по клику на пункте в левом меню в главной части страницы динамически подгружаются три разных списка, с разными источниками данных и разным набором колонок в таблицах. Но все они используют одну и ту же схему — компонент Data Table.
В таких таблицах хранят списки определенных сущностей — списки товаров, сотрудников, контрагентов и так далее. И очень часто бизнес-логика предполагает, что помимо списка должна существовать еще и карточка отдельной сущности, где можно было бы просмотреть или отредактировать ее данные.
Поскольку в моем приложении предполагается сравнительно простая ролевая модель безопасности, в которой просмотр всей карточки сущности и редактирование сущности разделяются по группам пользователей, я решил не усложнять разработку разделением настроек доступа и скрытием/отображением отдельных полей и компонентов на одной и той же странице, а просто сделать две отдельные страницы для двух отдельных карточек — карточки просмотра и карточки редактирования. Однако, так как данные, в основном, одни и те же, то управляемый бин с кодом компонентов я сделал один общий. Обратите внимание, что я написал «управляемый бин с кодом компонентов», а не «бин компонента», как было ранее в предыдущих частях статьи, потому что на самом деле в бине вполне можно размещать и настраивать код не одного, а нескольких компонентов, и даже из кода одного бина ссылаться на код других управляемых бинов. Впрочем, для тех, кто работал с фреймворком Spring, это привычная ситуация. Разница лишь в том, здесь я применяю инжектирования бина одного вида в бине другого вида, и таким образом, строго говоря, немного нарушаю классическую архитектуру MVC, превращая ее во что-то вроде M(VVV…..VVVV)C (цепочка вызываемых бинов вложенных видов может оказаться сколько угодно длинной, но обычно мне хватало двух, то есть я делал MVVC).
Начнем с карточки просмотра сущности. Вот пример одной из них, xhtml-страница:
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://xmlns.jcp.org/jsf/html" xmlns:f="http://xmlns.jcp.org/jsf/core" xmlns:p="http://primefaces.org/ui"> <f:metadata> <f:viewParam name="id" value="#{employeeCardView.id}"/> <f:viewAction action="#{employeeCardView.onload}"/> <f:viewParam name="id" value="#{employeeFileDownloadView.id}"/> <f:viewAction action="#{employeeFileDownloadView.onload}"/> <f:viewParam name="id" value="#{employeeSkillsSelectionView.id}"/> <f:viewAction action="#{employeeSkillsSelectionView.onload()}"/> <f:viewParam name="id" value="#{employeeCardView.employeeRatingView.id}"/> <f:viewAction action="#{employeeCardView.employeeRatingView.onload}"/> </f:metadata> <f:view contentType="text/html;charset=UTF-8" encoding="UTF-8"> <h:head> <h:outputStylesheet library="webjars" name="primeflex/3.2.0/primeflex.min.css"/> <h:outputStylesheet library="css" name="styles.css"/> <meta charset="utf-8"/> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/> <title>Заголовок страницы</title> </h:head> <h:body> <div class="card"> <h:form> <p:dataTable emptyMessage=""> <f:facet name="header"> <span>Карточка сотрудника</span> </f:facet> </p:dataTable> </h:form> <h:form id="form"> <div class="grid ui-fluid"> <div class="col-12 md:col-2"> <p:button href="/employee/edit/#{employeeCardView.id}" value="Редактировать"/> </div> <div class="col-12 md:col-2"> <p:button href="/" value="На главную"/> </div> </div> <p:dialog modal="true" widgetVar="statusDialog" header="Status" draggable="false" closable="false" resizable="false"> <i class="pi pi-spinner pi-spin" style="font-size:3rem"></i> </p:dialog> <h2>Общая информация</h2> <p:divider/> <div class="grid ui-fluid"> <div class="col-12 md:col-3"> <h3>Имя</h3> <h:outputText styleClass="fieldData" value="#{employeeCardView.firstName}"/> <h3>Фамилия</h3> <h:outputText styleClass="fieldData" value="#{employeeCardView.surname}"/> <h3>Отчество</h3> <h:outputText styleClass="fieldData" value="#{employeeCardView.secondName}"/> </div> <div class="col-12 md:col-3"> <h3>Подразделение</h3> <h:outputText styleClass="fieldData" value="#{employeeCardView.employeeDepartment.finDepartment.name}"/> <h3>Возраст</h3> <h:outputText styleClass="fieldData" value="#{employeeCardView.age}"/> <h3>Стаж работы</h3> <h:outputText styleClass="fieldData" value="#{employeeCardView.workExperience} лет"/> </div> <div class="col-12 md:col-3"> <h3>Ставка себестоимости</h3> <h:outputText styleClass="fieldData" value="#{employeeCardView.costPriceRate} в час"/> <h3>Уволен/работает</h3> <h:outputText styleClass="fieldData" value="#{employeeCardView.archived}"/> </div> <div class="col-12 md:col-3"> <h3>Профиль роли сотрудника</h3> <div class="col-12 md:col-12"> <h4>Основная роль</h4> <h:outputText styleClass="fieldData mb-4" value="#{employeeCardView.employeeRatingView.mainRoleName}"/> <p:rating id="rate_1" value="#{employeeCardView.employeeRatingView.mainGradeId}" readonly="false" stars="6"/> </div> <div class="col-12 md:col-12"> <h4>Дополнительные роли</h4> <p:dataList value="#{employeeCardView.employeeRatingView.extraRoleEntryList}" var="entry" styleClass="noBorders" itemType="none"> <p> #{entry.key.name} </p> <p:rating value="#{entry.value.id}" readonly="false" stars="6"/> </p:dataList> </div> <div class="col-12 md:col-8"> <p:button value="Редактировать" icon="pi pi-pencil" href="/employee/roles/edit/#{employeeCardView.id}"/> </div> </div> <p:divider/> <div class="col-12 md:col-3"> <h3>Локация</h3> <h:outputText styleClass="fieldData" value="#{employeeCardView.location.city}"/> , <h:outputText styleClass="fieldData" value="#{employeeCardView.location.personalAddress}"/> <h3>Контактные данные</h3> <div><h:outputText styleClass="fieldData" value="Email: #{employeeCardView.contactObj.email}"/> </div> <div><h:outputText styleClass="fieldData" value="Phone: #{employeeCardView.contactObj.phone}"/> </div> <div><h:outputText styleClass="fieldData" value="Mobile: #{employeeCardView.contactObj.mobile}"/> </div> </div> <div class="col-12 md:col-3"> <h3>Образование</h3> <h:outputText styleClass="fieldData" value="#{employeeCardView.education.organization}"/>, <h:outputText styleClass="fieldData" value="#{employeeCardView.education.educationGrade.name}"/> <h3>Грейд</h3> <h:outputText styleClass="fieldData" value="#{employeeCardView.grade.name}"/> <h3>Уровень владения английским языком</h3> <h:outputText styleClass="fieldData" value="#{employeeCardView.englishLevel.name}"/> </div> <div class="col-12 md:col-3"> <h3>Резюме на русском языке</h3> <h:outputText value="будет сгенерировано автоматически"/> <h:outputText styleClass="fieldData" value="#{employeeCardView.russianResume}"/> <h3>Резюме на английском языке</h3> <h:outputText value="будет сгенерировано автоматически"/> <h:outputText styleClass="fieldData" value="#{employeeCardView.englishResume}"/> </div> </div> <p:divider/> <h2>Дополнительная информация</h2> <p:divider/> <div class="grid ui-fluid"> <div class="col-12 md:col-4"> <h3>Квалификационный профиль сотрудника</h3> <p:dataList value="#{employeeCardView.skillNamesSet}" var="level" styleClass="noBorders" itemType="none"> <p> #{level} </p> </p:dataList> <div class="col-12 md:col-4"> <p:commandButton icon="pi pi-window-maximize" oncomplete="PF('detailDialog').show()" value="Подробности"/> </div> </div> <div class="col-12 md:col-4"> <h3>Дополнительная информация от сотрудника</h3> <h:outputText styleClass="fieldData" value="#{employeeCardView.selfInfo}"/> </div> <div class="col-12 md:col-4"> <h3>Дополнительная информация от руководителя</h3> <h:outputText styleClass="fieldData" value="#{employeeCardView.chefInfo}"/> </div> </div> <p:divider/> <div class="grid ui-fluid"> <div class="col-12 md:col-12"> <h3>Проектный опыт</h3> <h4>будет выгружаться из 1С</h4> <p:dataTable lazy="false" var="item" value="#{employeeCardView.satelProjectExperienceList}" widgetVar="itemsTable" emptyMessage="Проектный опыт у сотрудника отсутствует"> <p:column headerText="№ сделки"> <h:outputText value="#{item.contractNumber}"/> </p:column> <p:column headerText="Название проекта"> <h:outputText value="#{item.name}"/> </p:column> <p:column headerText="Проектная роль"> <h:outputText value="#{item.projectRole} часов"/> </p:column> <p:column headerText="Проектные работы"> <h:outputText value="#{item.projectWorks}"/> </p:column> </p:dataTable> </div> </div> <div class="grid ui-fluid"> <div class="col-12 md:col-6"> <h3>Файлы</h3> <p:dataList id="employee-files-list" lazy="false" var="entry" widgetVar="itemsTable" itemStyleClass="fileItem" emptyMessage="Файлы сотрудника не загружены" itemType="none" value="#{employeeCardView.employeeFileDownloadView.streamedContentEntrySet}"> <span class="fileButton"> <p:commandButton value="#{entry.value.name}" ajax="false" onclick="PrimeFaces.monitorDownload(start, stop);" icon="pi pi-arrow-down" styleClass="mr-2"> <p:fileDownload value="#{entry.value.streamedContent}"/> </p:commandButton> </span> </p:dataList> </div> </div> <div class="grid ui-fluid"> <div class="col-12 md:col-2"> <p:button href="/employee/edit/#{employeeCardView.id}" value="Редактировать"/> </div> <div class="col-12 md:col-2"> <p:button href="/" value="На главную"/> </div> </div> </h:form> <h:form id="dialogs"> <p:dialog header="Компетенции сотрудника" showEffect="fade" modal="true" widgetVar="detailDialog" responsive="true"> <p:outputPanel id="detail-skill-content" class="ui-fluid"> <p:dataList value="#{employeeCardView.skillNamesSet}" var="level" styleClass="noBorders" itemType="none"> <p> #{level} </p> <div> <p:rating value="3" readonly="false" stars="6"/> </div> <p><h:outputText>Здесь будут подробности оценки навыка</h:outputText></p> <p:divider/> </p:dataList> </p:outputPanel> <f:facet name="footer"> <p:button value="Редактировать" icon="pi pi-pencil" href="/employee/skills/edit/#{employeeCardView.id}"/> <p:commandButton value="Закрыть" icon="pi pi-times" onclick="PF('detailDialog').hide()" class="ui-button-secondary" type="button"/> </f:facet> </p:dialog> </h:form> </div> </h:body> </f:view> </html>
Разберем чуть подробнее некоторые поля (многие поля имеют одинаковые типы, поэтому нет смысла описывать их все)
Самое простое поле, которое я уже упоминал ранее, это h:outputText. Все, что нужно для его базового использования, это указать ссылку на поле в бине, к которому оно будет привязано, например:
<h:outputText styleClass="fieldData" value="#{employeeCardView.firstName}"/>
здесь поле будет привязано к полю firstName в бине с именем employeeCardView. Естественно, в бине это поле имеет какой-то из примитивных типов. Для вывода данных этого вполне достаточно. В случаях, если данные для вывода должны получаться из каких-то отдельных источников или генерироваться из других данных, ничто не мешает нам сделать это в самом бине через обращение к сервисам или написав непосредственно метод для генерации данных из нескольких полей или из источника, а затем записать полученный результат в новое отдельное поле бина, которое также будет привязано к полю xhtml файла.
Иногда бывает необходимо вывести некоторый список данных, которые хранятся в каком-то поле бина, имеющем тип одной из коллекций Java. В этом случае удобно использовать, например, компонент Data List, например:
<p:dataList value="#{employeeCardView.employeeRatingView.extraRoleEntryList}" var="entry" styleClass="noBorders" itemType="none"> <p> #{entry.key.name} </p> <p:rating value="#{entry.value.id}" readonly="false" stars="6"/> </p:dataList>
причем передаваемая коллекция может быть достаточно сложной. В этом примере в параметр компонента value передается ссылка на список элементов Map.Entry, извлеченных в компоненте из данных некоторой Map, привязанное поле бина выглядит следующим образом:
private List<Map.Entry<Role, Grade>> extraRoleEntryList;
параметр var компонета служит для передачи в список значения отдельной Map.Entry<Role, Grade> из списка, соответственно, мы видим, что в списке будет выводиться имя ключа, то есть поле name экземпляра класса Role.
Пожалуй, вывод данных из мапы — это самый сложный вариант вывода данных из коллекций, из простых списков по аналогии получить вывод намного легче, читатель легко с этим разберется самостоятельно. Но на всякий случай приведу ссылку на то, как это делается, в документции Primefaces:
http://www.primefaces.org:8080/showcase/ui/data/datalist/basic.xhtml?jfwid=513c8
Необязательно, чтобы поле было списком, оно может быть, например, и множеством:
<p:dataList value="#{employeeCardView.skillNamesSet}" var="level" styleClass="noBorders" itemType="none"> <p> #{level} </p> </p:dataList>
private Set<String> skillNamesSet;
В следующей части статьи мы продолжим разбор еще нескольких интересных способов вывода информации в разных типах полей для просмотра.
Традиционно в конце статьи рекомендую вам бесплатный урок от OTUS. В этот раз хочу порекомендовать урок, в рамках которого будет рассмотрена общая архитектура веб-приложений. Что происходит при клиент-серверном взаимодействии. И какое место отведено Spring MVC.
ссылка на оригинал статьи https://habr.com/ru/company/otus/blog/713844/
Добавить комментарий