Создание собственной страницы
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();
}
}
Данный контроллер будет существовать, пока открыта страница, которая его использует, т.к. у него указана аннотация @ViewScoped (javax.faces.view.ViewScoped). Когда абонент перейдет на другой пункт меню и обратно или нажмет F5 - будет создан новый объект данного контроллера. Если указать вместо @ViewScoped аннотацию @SessionScoped (javax.enterprise.context.SessionScoped), тогда контроллер будет существовать в течении жизни HTTP-сессии. Если же указать @RequestScoped (javax.enterprise.context.RequestScoped) - то только на протяжении обработки HTTP-запроса. Создание контроллера происходит при первом его использовании, например, в файле xhtml.
Аннотация @BGInject позволяет использовать веб-сервисы биллинга (но дополнительно требуется аннотация @BGInjection на классе).
Обычно для инициализации контроллера создают метод init с аннотацией @PostConstruct, однако в данном случае используем внутренний класс AbstractBean, который уже задействовал этот механизм, поэтому необходимо переопределить его метод init.
Метод populate заполняет контроллер данными, он вызывается при инициализации из метода init, но также может быть вызван и из xhtml.
Метод getContractNotificationList используется для получения списка объектов для последующего отображения в xhtml #{myNotificationBean.contractNotificationList}.
Метод 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>
Внутри блока <ui:define name="page-content-header"> находится заголовок текущей страницы, например, "Новости" или "Уведомления".
Внутри блока <ui:define name="page-content-data"> находится сама страница (её содержимое).
Атрибут jsf:rendered указывает, нужно ли отображать данный элемент.
Тэг ui:repeat осуществляет обработку списка #{myNotificationBean.contractNotificationList}, создавая новую переменную #{notification}, которая доступна внутри этого тэга-цикла.
Для того, чтобы работал вызов методов контроллера, а также тэг f:ajax, форма должна быть JSF-компонентом. Чтобы форма стала JSF-компонентом, достаточно указать атрибут jsf:id.
Обычно для отображения переменной достаточна запись вида #{notification.title}, однако, если необходимо форматирование, например, даты, то нужно использовать тэг h:outputText. Если содержимое переменной не нужно эскейпировать перед выводом (например, если внутри переменной-строки есть запись вида "<br/>" и ее нужно вывести как перевод строки, а не как строку "<br/>"), то также используется тэг h:outputText с атрибутом escape="false".
Тэг f:ajax с атрибутом event="click" внутри ссылки указывает, что на событие клика нужно вызвать метод #{myNotificationBean.markRead( notification.id )}. Обновление страницы здесь не происходит (только js на то же события клика меняет класс элемента для смены иконки).
Тэг f:ajax с атрибутом event="click" внутри кнопки указывает, что на событие нажатия кнопки нужно вызвать метод #{myNotificationBean.deleteContractNotification( notification.id ).
Тэг 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
>