Как стать автором
Обновить
831.69
OTUS
Цифровые навыки от ведущих экспертов

Интеграция Primefaces в приложение на Spring Boot. Часть 3 — динамическое обновление контента страницы

Время на прочтение4 мин
Количество просмотров1.7K

Во второй части мы сосредоточились главным образом на создании меню на основе компонента Tree ContextMenu. Пришло время показать, как можно этим меню воспользоваться. В моем случае сценарий такой - необходимо по клику на одном из трех пунктов меню обновить содержание главной части страницы, в которой должно отобразиться каждый раз разное содержимое. Метод, который я покажу, достаточно универсальный, его можно применять самыми разными способами. (Вообще, основной текст xhtml страницы и бина компонента уже показаны в предыдущей части статьи, кто-то, возможно, уже и сам разобрался в этом, но все равно, разберем этот момент подробнее)

Итак, вот еще раз часть кода страницы:

<p:splitter>
	<p:splitterPanel :size="20" styleClass="flex align-items-center justify-content-center flex-error-left-1">
		<p:tree id="docs" value="#{treeContextMenuView.root}" var="doc" selectionMode="single" selection="#{treeContextMenuView.selectedNode}" dynamic="true">
			<p:treeNode expandedIcon="pi pi-folder-open" collapsedIcon="pi pi-folder">
				<h:outputText value="#{doc.name}" /> </p:treeNode>
			<p:treeNode type="ips" icon="pi pi-folder">
				<h:outputText value="#{doc.name}" /> </p:treeNode>
			<p:treeNode id="testid" type="contragent" icon="pi pi-file">
				<h:outputText value="#{doc.name}" /> </p:treeNode>
			<p:ajax event="select" listener="#{treeContextMenuView.setSrc()}" /> </p:tree>
	</p:splitterPanel>
	<p:splitterPanel :size="80" styleClass="flex align-items-center justify-content-center flex-error-right-1">
		<h:panelGroup id="list">
			<h:panelGroup rendered="true">
				<ui:include src="#{treeContextMenuView.getSrc()}" /> </h:panelGroup>
		</h:panelGroup>
	</p:splitterPanel>
</p:splitter>

Компонент p:splitter очень простой, он просто разделяет страницу на две области в заданной пропорции. В левой части, собственно, размещен наш компонент меню p:tree. В правой - размещаем компонент ui:include из библиотеки http://xmlns.jcp.org/jsf/facelets. Обратите внимание, что я дважды завернул его в компонент h:panelGroup и включил во внутренней обертке rendered="true", это сделано для того, чтобы при частичном рендеринге страницы вложенная в него страница также обновлялась. Далее, указываем источник, где размещена вложенная страница <ui:include src="#{treeContextMenuView.getSrc()}" />. getSrc() - это у меня метод бина компонента, который получает адрес вложенной страницы. В бине устанавливаем адрес по дефолту, который указывает на вложенную страницу, загружаемую при первом заходе на главную страницу. Затем нам необходимо написать метод setSrc(), который будет загружать в поле бина listSrc новый адрес при клике по пункту меню. Приведем еще раз класс бина компонента, который уже публиковался во второй части статьи, чтобы вы его не искали:

import jakarta.faces.view.ViewScoped;
import jakarta.inject.Inject;
import org.primefaces.PrimeFaces;
import org.primefaces.model.TreeNode;

import javax.annotation.ManagedBean;
import javax.faces.component.UIComponent;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import java.io.Serializable;
import java.util.List;

@ManagedBean("treeContextMenuView")
@ViewScoped
public class PageMenuView implements Serializable {
    private final TreeNode<Page> root;

    private TreeNode<Page> selectedNode;
    private final String DEFAULT_LIST = "employees.xhtml";
    private String listSrc;

    @Inject
    public PageMenuView(PageService service) {
        root = service.createPages();
        listSrc = DEFAULT_LIST;
    }


    public TreeNode<Page> getRoot() {
        return root;
    }

    public TreeNode<Page> getSelectedNode() {
        return selectedNode;
    }

    public void setSelectedNode(TreeNode<Page> selectedNode) {
        this.selectedNode = selectedNode;
    }

    public void setSrc() {
        if (selectedNode.getData().getLink() != null) {
            listSrc = selectedNode.getData().getLink();

            UIViewRoot view = FacesContext.getCurrentInstance().getViewRoot();
            List<UIComponent> uiComponents = view.getChildren();
            UIComponent uiComponent = uiComponents.get(2).getChildren().get(0).getChildren().get(3).getChildren().get(1)
                    .getChildren().get(0);
            PrimeFaces.current().ajax().update(uiComponent.getClientId());
        }
    }

    public String getSrc() {
        return listSrc;
    }

}

Чтобы метод сработал, его нужно вызвать ajax запросом из компонента меню, привязав событие select к обработчику setSrc()

<p:ajax event="select" listener="#{treeContextMenuView.setSrc()}" />

Обработчик считывает выбранный узел в виде объекта TreeNode<Page>, извлекает из данных объекта новый линк, привязанный к соответствующему пункту меню и загружает линк в переменную listSrc. После чего необходимо программным способом вызвать обновление компонента <ui:include src="#{treeContextMenuView.getSrc()}" />, что мы и делаем. В моем случае я выбрал самый упрощенный путь поиска компонента в дереве DOM страницы, так как не предполагаю динамически менять ее никогда. При ре-рендеринге перезагружается другая вложенная страница с другим содержимым.

Однако, в Интернет вы найдете массу описаний, как получить компонент или просто любой другой элемент в проектах на Primefaces по id элемента или компонента. Не удивляйтесь, что в большинстве случаев они работать не будут, так как они используют другую конструкцию - FacesContext.getCurrentInstance().getViewRoot().findComponent(id) и последний метод в нем, как правило, не может найти компонент по id, просто потому, что на странице часто много динамики, много разных компонентов, инклудов и так далее, из-за которых вы фактически будете вызывать компонент из того места кода, где контекст или его часть фактически уже потеряны. И разбираться во всем этом хитросплетении чаще бывает намного дольше и сложнее, чем просто пройтись по DOM страницы и найти нужный компонент в режиме дебага. Или еще можно написать или нагуглить какой-то метод для поиска компонента по id рекурсивным поиском по DOM, например, как здесь https://stackoverflow.com/questions/14378437/find-component-by-id-in-jsf. Я не стал мучиться, и просто нашел компонент вручную.

В следующей части статьи мы посмотрим, как я делал списки для вывода из БД в компоненте DataTable Filter (список табличных данных с фильтром)

Данный цикл статей подготовлен в преддверии старта курса "Java Developer. Professional". Также рекомендую к посещению бесплатный урок курса по теме: "Реактивное подключение к Postgresql в приложениях на Java".

Теги:
Хабы:
+5
Комментарии2

Публикации

Информация

Сайт
otus.ru
Дата регистрации
Дата основания
Численность
101–200 человек
Местоположение
Россия
Представитель
OTUS