Создание собственной страницы

Controller

Создайте в директории WEB-INF/dyn файл MyNotificationBean.java с содержимым:

import java.io.Serializable;
 
import javax.annotation.PostConstruct;
import javax.enterprise.context.RequestScoped;
import javax.enterprise.context.SessionScoped;
import javax.faces.view.ViewScoped;
import javax.inject.Inject;
import javax.inject.Named;
 
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
 
import ru.bitel.mybgbilling.kernel.common.AbstractBean;
import ru.bitel.mybgbilling.kernel.common.inject.BGInject;
import ru.bitel.mybgbilling.kernel.common.inject.BGInjection;
 
import ru.bitel.mybgbilling.kernel.contract.NotificationBean;
import ru.bitel.bgbilling.common.BGException;
import ru.bitel.bgbilling.kernel.contract.api.common.bean.ContractNotification;
import ru.bitel.bgbilling.kernel.contract.api.common.service.ContractNotificationService;
 
@Named
@ViewScoped // 1.
@BGInjection // 2.
public class MyNotificationBean
extends AbstractBean
implements Serializable
{
private static final Logger logger = LoggerFactory.getLogger( MyNotificationBean.class );
 
@BGInject // 2.
private ContractNotificationService contractNotificationService;
 
private List<ContractNotification> contractNotificationList;
 
@Override
protected void init() // 3.
throws BGException
{
logger.info( "init" );
 
populate();
}
 
public void populate() // 4.
{
logger.info( "populate" );
 
contractNotificationList = contractNotificationService.contractNotificationList( getContractId() );
}
 
public List<ContractNotification> getContractNotificationList() // 5.
{
return contractNotificationList;
}
public void markRead( int id ) // 6.
throws BGException
{
contractNotificationService.contractNotificationMarkRead( getContractId(), id );
 
populate();
}
 
public void deleteContractNotification( int id )
throws BGException
{
contractNotificationService.contractNotificationDelete( getContractId(), id );
 
populate();
}
}
  1. Данный контроллер будет существовать, пока открыта страница, которая его использует, т.к. у него указана аннотация @ViewScoped (javax.faces.view.ViewScoped). Когда абонент перейдет на другой пункт меню и обратно или нажмет F5 - будет создан новый объект данного контроллера. Если указать вместо @ViewScoped аннотацию @SessionScoped (javax.enterprise.context.SessionScoped), тогда контроллер будет существовать в течении жизни HTTP-сессии. Если же указать @RequestScoped (javax.enterprise.context.RequestScoped) - то только на протяжении обработки HTTP-запроса. Создание контроллера происходит при первом его использовании, например, в файле xhtml.

  2. Аннотация @BGInject позволяет использовать веб-сервисы биллинга (но дополнительно требуется аннотация @BGInjection на классе).

  3. Обычно для инициализации контроллера создают метод init с аннотацией @PostConstruct, однако в данном случае используем внутренний класс AbstractBean, который уже задействовал этот механизм, поэтому необходимо переопределить его метод init.

  4. Метод populate заполняет контроллер данными, он вызывается при инициализации из метода init, но также может быть вызван и из xhtml.

  5. Метод getContractNotificationList используется для получения списка объектов для последующего отображения в xhtml #{myNotificationBean.contractNotificationList}.

  6. Метод markRead вызывает веб-сервис биллинга, а затем вызывает populate для обновления данных.

View

Создайте в директории WEB-INF/content папку, например, provider и в ней файл myNotifications.xhtml:

<?xml version="1.0" encoding="UTF-8"?>
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html" xmlns:jsf="http://xmlns.jcp.org/jsf"
xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:f="http://java.sun.com/jsf/core"
xmlns:c="http://java.sun.com/jsp/jstl/core" xmlns:fn="http://java.sun.com/jsp/jstl/functions"
xmlns:b="http://java.sun.com/jsf/composite/composite" xmlns:namespace="http://java.sun.com/jsf/composite/namespace"
xmlns:bts="http://java.sun.com/jsf/composite/bts" xmlns:a="http://xmlns.jcp.org/jsf/passthrough"
template="/WEB-INF/template/page-wrapper.xhtml">
 
<ui:define name="page-content-header"> <!-- 1. -->
<h1>Заголовок</h1>
</ui:define>
 
<ui:define name="page-content-data"> <!-- 2. -->
 
<div class="panel panel-default">
<div class="panel-body">
 
<em jsf:rendered="#{empty myNotificationBean.contractNotificationList}">Оповещений нет</em> <!-- 3. -->
 
<ui:repeat value="#{myNotificationBean.contractNotificationList}" var="notification" varStatus="status"> <!-- 4. -->
<div style="status .last?'margin-bottom: 5px ':''">
 
<form jsf:id="notificationReadForm" role="form" class="form-inline"> <!-- 5. -->
<a onclick="if($('#collapse#{notification.id}').hasClass('in')) return false; else {$('#collapsea#{notification.id}').removeClass('fa-envelope').addClass('fa-envelope-o')} "
data-toggle="collapse" href="#collapse#{notification.id}"
aria-expanded="true" aria-controls="collapseOne" class="noupdate">
<i id="collapsea#{notification.id}" class="fa #{notification.read?'fa-envelope-o':'fa-envelope'} fa-fw"></i>
<h:outputText value=" " />
<h:outputText value="#{notification.dateTime}"> <!-- 6. -->
<f:convertDateTime type="both" dateStyle="medium" />
</h:outputText>
#{notification.title}
<f:ajax listener="#{myNotificationBean.markRead( notification.id )}" event="click" /> <!-- 6. -->
</a>
</form>
 
<div class="collapse" id="collapse#{notification.id}">
<div class="well well-sm">
<h:outputText value="#{notification.message}" escape="false" /> <!-- 6. -->
<br /> <br />
 
<form jsf:id="notificationDeleteForm" role="form" class="form-inline"> <!-- 5. -->
<button class="btn btn-primary confirm" update-target="page-content-data">#{msg['action.delete']}
<f:ajax listener="#{myNotificationBean.deleteContractNotification( notification.id )}"
event="click" render=":page-content-data" /> <!-- 8. --> <!-- 9. -->
</button>
</form>
 
</div>
</div>
</div>
</ui:repeat>
 
</div>
</div>
 
</ui:define>
</ui:composition>
  1. Внутри блока <ui:define name="page-content-header"> находится заголовок текущей страницы, например, "Новости" или "Уведомления".

  2. Внутри блока <ui:define name="page-content-data"> находится сама страница (её содержимое).

  3. Атрибут jsf:rendered указывает, нужно ли отображать данный элемент.

  4. Тэг ui:repeat осуществляет обработку списка #{myNotificationBean.contractNotificationList}, создавая новую переменную #{notification}, которая доступна внутри этого тэга-цикла.

  5. Для того, чтобы работал вызов методов контроллера, а также тэг f:ajax, форма должна быть JSF-компонентом. Чтобы форма стала JSF-компонентом, достаточно указать атрибут jsf:id.

  6. Обычно для отображения переменной достаточна запись вида #{notification.title}, однако, если необходимо форматирование, например, даты, то нужно использовать тэг h:outputText. Если содержимое переменной не нужно эскейпировать перед выводом (например, если внутри переменной-строки есть запись вида "<br/>" и ее нужно вывести как перевод строки, а не как строку "<br/>"), то также используется тэг h:outputText с атрибутом escape="false".

  7. Тэг f:ajax с атрибутом event="click" внутри ссылки указывает, что на событие клика нужно вызвать метод #{myNotificationBean.markRead( notification.id )}. Обновление страницы здесь не происходит (только js на то же события клика меняет класс элемента для смены иконки).

  8. Тэг f:ajax с атрибутом event="click" внутри кнопки указывает, что на событие нажатия кнопки нужно вызвать метод #{myNotificationBean.deleteContractNotification( notification.id ).

  9. Тэг f:ajax с атрибутом render=":page-content-data" указывает, что после выполнения запроса нужно обновить область c id page-content-data (в данном случае это область внутри блока <ui:define name="page-content-data">).

Меню

Для того, чтобы страница отобразилась, на нее должна указывать специальная ссылка. Чтобы добавить ссылку в главное меню, нужно указать в WEB-INF/mybgbilling-menu.groovy:

menu( page: "provider/myNotifications", subPage: "", icon: "fa-envelope-o", title: "menu.notifications", show: !isCustomer() ),

Или можно указать ссылку прямо на странице:

<form jsf:id="linkForm" role="form" class="form-inline">
<a jsf:action="#{navigationBean.setPage('provider/myNotifications', 0)}" update-target="page-wrapper">
<i class="fa fa-rub fa-fw"></i> Ссылка
<f:ajax render=":page-wrapper" />
</a>
</form>

В виде кнопки:

<form jsf:id="linkForm" role="form" class="form-inline">
<button class="btn btn-primary confirm" update-target="page-wrapper">Ссылка
<f:ajax listener="#{navigationBean.setPage( 'provider/myNotifications', 0 )}"
event="click" render=":page-wrapper" />
</button>
</form>