[doc] Updated development documentation to new BaseCRUDController class
FEA: ItEr75S03CommunityMaterial
This commit is contained in:
parent
8da7b717f5
commit
2381b4e709
1 changed files with 297 additions and 109 deletions
|
|
@ -5,8 +5,8 @@ How To Develop A Use Case In NavalPlan
|
||||||
.. sectnum::
|
.. sectnum::
|
||||||
|
|
||||||
:Author: Manuel Rego Casasnovas
|
:Author: Manuel Rego Casasnovas
|
||||||
:Contact: mrego@igalia.com
|
:Contact: rego@igalia.com
|
||||||
:Date: 10/03/2011
|
:Date: 15/08/2011
|
||||||
:Copyright:
|
:Copyright:
|
||||||
Some rights reserved. This document is distributed under the Creative
|
Some rights reserved. This document is distributed under the Creative
|
||||||
Commons Attribution-ShareAlike 3.0 licence, available in
|
Commons Attribution-ShareAlike 3.0 licence, available in
|
||||||
|
|
@ -83,7 +83,7 @@ this behaviour and use it in all the tasks they want.
|
||||||
instructions:
|
instructions:
|
||||||
|
|
||||||
* Create a PostgreSQL database called ``navaldev`` with permissions for a
|
* Create a PostgreSQL database called ``navaldev`` with permissions for a
|
||||||
user ``naval`` with password ``naval`` (see ``INSTALL`` file for other
|
user ``naval`` with password ``naval`` (see ``HACKING`` file for other
|
||||||
databases and more info).
|
databases and more info).
|
||||||
|
|
||||||
* Compile NavalPlan with the following command from project root folder::
|
* Compile NavalPlan with the following command from project root folder::
|
||||||
|
|
@ -243,7 +243,8 @@ shown):
|
||||||
*
|
*
|
||||||
* @author Manuel Rego Casasnovas <mrego@igalia.com>
|
* @author Manuel Rego Casasnovas <mrego@igalia.com>
|
||||||
*/
|
*/
|
||||||
public class StretchesFunctionTemplate extends BaseEntity {
|
public class StretchesFunctionTemplate extends BaseEntity implements
|
||||||
|
IHumanIdentifiable {
|
||||||
|
|
||||||
public static StretchesFunctionTemplate create(String name) {
|
public static StretchesFunctionTemplate create(String name) {
|
||||||
return create(new StretchesFunctionTemplate(name));
|
return create(new StretchesFunctionTemplate(name));
|
||||||
|
|
@ -260,7 +261,24 @@ shown):
|
||||||
protected StretchesFunctionTemplate() {
|
protected StretchesFunctionTemplate() {
|
||||||
}
|
}
|
||||||
|
|
||||||
...
|
...
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getHumanId() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
.. NOTE::
|
||||||
|
|
||||||
|
``IHumanIdentifiable`` is an interface that needs a human identifier to show
|
||||||
|
in application UI. It defines the method ``getHumanId`` that returns a text
|
||||||
|
identifier of the entity.
|
||||||
|
|
||||||
|
As this entity is going to be edited from NavalPlan web interface, it
|
||||||
|
implements ``IHumanIdentifiable``.
|
||||||
|
|
||||||
|
|
||||||
* ``StretchTemplate.java``:
|
* ``StretchTemplate.java``:
|
||||||
|
|
||||||
|
|
@ -675,20 +693,26 @@ the same identifier in ``.zul`` and Java. For example:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
package org.navalplanner.web.planner.allocation.streches;
|
package org.navalplanner.web.common;
|
||||||
|
|
||||||
...
|
...
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CRUD controller for {@link StretchesFunctionTemplate}.
|
* Abstract class defining common behavior for controllers of CRUD screens. <br />
|
||||||
*
|
*
|
||||||
* @author Manuel Rego Casasnovas <mrego@igalia.com>
|
* Those screens must define the following components:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link #messagesContainer}: A {@link Component} to show the different
|
||||||
|
* messages to users.</li>
|
||||||
|
* <li>{@link #listWindow}: A {@link Window} where the list of elements is
|
||||||
|
* shown.</li>
|
||||||
|
* <li>{@link #editWindow}: A {@link Window} with creation/edition form.</li>
|
||||||
|
*
|
||||||
|
* @author Manuel Rego Casasnovas <rego@igalia.com>
|
||||||
*/
|
*/
|
||||||
public class StretchesFunctionTemplateCRUDController extends GenericForwardComposer {
|
@SuppressWarnings("serial")
|
||||||
|
public abstract class BaseCRUDController<T extends IHumanIdentifiable> extends
|
||||||
private Window listWindow;
|
GenericForwardComposer {
|
||||||
private Window editWindow;
|
|
||||||
|
|
||||||
...
|
...
|
||||||
|
|
||||||
This matching is automatic and is done by ZK. In order that this works it is
|
This matching is automatic and is done by ZK. In order that this works it is
|
||||||
|
|
@ -704,7 +728,7 @@ steps to do this are the following ones:
|
||||||
* Your controller will override method ``doAfterCompose``.
|
* Your controller will override method ``doAfterCompose``.
|
||||||
* This method receives a component which is the window associated to the
|
* This method receives a component which is the window associated to the
|
||||||
controller through ``apply`` attribute.
|
controller through ``apply`` attribute.
|
||||||
* In ``Window`` you will use ``setVariable`` method in order to create a
|
* In ``Window`` you will use ``setAttribute`` method in order to create a
|
||||||
variable called ``controller`` that will contain a reference to controller.
|
variable called ``controller`` that will contain a reference to controller.
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
@ -712,7 +736,9 @@ steps to do this are the following ones:
|
||||||
@Override
|
@Override
|
||||||
public void doAfterCompose(Component comp) throws Exception {
|
public void doAfterCompose(Component comp) throws Exception {
|
||||||
super.doAfterCompose(comp);
|
super.doAfterCompose(comp);
|
||||||
comp.setVariable("controller", this, true);
|
comp.setAttribute("controller", this);
|
||||||
|
|
||||||
|
...
|
||||||
}
|
}
|
||||||
|
|
||||||
After that from ``.zul``, you will make reference to a variable called
|
After that from ``.zul``, you will make reference to a variable called
|
||||||
|
|
@ -729,6 +755,31 @@ controller. For example with the following lines::
|
||||||
As you can see in last example, when an event is launched is not needed to use
|
As you can see in last example, when an event is launched is not needed to use
|
||||||
data binding.
|
data binding.
|
||||||
|
|
||||||
|
``BaseCRUDController`` is a generic class with common behaviour for controllers
|
||||||
|
of CRUD screens. It defines a set of methods with a common functionality and
|
||||||
|
delegates on some abstract methods that should be implemented in the subclasses.
|
||||||
|
|
||||||
|
For this example you will create a new controller
|
||||||
|
``StretchesFunctionTemplateCRUDController`` as a subclass of
|
||||||
|
``BaseCRUDController``.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
package org.navalplanner.web.planner.allocation.streches;
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CRUD controller for {@link StretchesFunctionTemplate}.
|
||||||
|
*
|
||||||
|
* @author Manuel Rego Casasnovas <mrego@igalia.com>
|
||||||
|
*/
|
||||||
|
public class StretchesFunctionTemplateCRUDController extends
|
||||||
|
BaseCRUDController<StretchesFunctionTemplate> {
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
ZK macro components
|
ZK macro components
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
|
|
@ -753,11 +804,12 @@ controller, you can not access components defined in ``list`` or ``edit``. For
|
||||||
example, ``list`` contains a ``Grid`` called
|
example, ``list`` contains a ``Grid`` called
|
||||||
``listStretchesFunctionTemplates``::
|
``listStretchesFunctionTemplates``::
|
||||||
|
|
||||||
public class StretchesFunctionTemplateCRUDController extends GenericForwardComposer {
|
public class StretchesFunctionTemplateCRUDController extends
|
||||||
|
BaseCRUDController<StretchesFunctionTemplate> {
|
||||||
|
|
||||||
...
|
...
|
||||||
|
|
||||||
private NewDataSortableGrid listStretchesFunctionTemplates;
|
private Grid listStretchesFunctionTemplates;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void doAfterCompose(Component comp) throws Exception {
|
public void doAfterCompose(Component comp) throws Exception {
|
||||||
|
|
@ -773,18 +825,17 @@ in main window namespace. But, you could access indirectly to component from
|
||||||
controller through ``list`` component, because this is accessible from
|
controller through ``list`` component, because this is accessible from
|
||||||
controller. For example::
|
controller. For example::
|
||||||
|
|
||||||
public class StretchesFunctionTemplateCRUDController extends GenericForwardComposer {
|
public class StretchesFunctionTemplateCRUDController extends
|
||||||
|
BaseCRUDController<StretchesFunctionTemplate> {
|
||||||
private Window listWindow;
|
|
||||||
|
|
||||||
...
|
...
|
||||||
|
|
||||||
private NewDataSortableGrid listStretchesFunctionTemplates;
|
private Grid listStretchesFunctionTemplates;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void doAfterCompose(Component comp) throws Exception {
|
public void doAfterCompose(Component comp) throws Exception {
|
||||||
...
|
...
|
||||||
listStretchesFunctionTemplates = (NewDataSortableGrid) listWindow
|
listStretchesFunctionTemplates = (Grid) listWindow
|
||||||
.getFellowIfAny("listStretchesFunctionTemplates");
|
.getFellowIfAny("listStretchesFunctionTemplates");
|
||||||
listStretchesFunctionTemplates.getModel();
|
listStretchesFunctionTemplates.getModel();
|
||||||
}
|
}
|
||||||
|
|
@ -795,25 +846,12 @@ Another important issue when implementing CRUD use cases is that general view
|
||||||
contains both ``list`` and ``edit`` component. These components are rendered
|
contains both ``list`` and ``edit`` component. These components are rendered
|
||||||
and shown when page is loaded. Class ``OnlyOneVisible`` is used in controller to
|
and shown when page is loaded. Class ``OnlyOneVisible`` is used in controller to
|
||||||
manage which one will be visible at a given time. You can find the following
|
manage which one will be visible at a given time. You can find the following
|
||||||
pieces of code in all CRUD controllers already working in NavalPlan::
|
pieces of code in ``BaseCRUDController``::
|
||||||
|
|
||||||
private OnlyOneVisible visibility;
|
private OnlyOneVisible visibility;
|
||||||
|
|
||||||
...
|
...
|
||||||
|
|
||||||
private void showListWindow() {
|
|
||||||
showWindow(listWindow);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showEditWindow(String title) {
|
|
||||||
editWindow.setTitle(title);
|
|
||||||
showWindow(editWindow);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showWindow(Window window) {
|
|
||||||
getVisibility().showOnly(window);
|
|
||||||
}
|
|
||||||
|
|
||||||
private OnlyOneVisible getVisibility() {
|
private OnlyOneVisible getVisibility() {
|
||||||
if (visibility == null) {
|
if (visibility == null) {
|
||||||
visibility = new OnlyOneVisible(listWindow, editWindow);
|
visibility = new OnlyOneVisible(listWindow, editWindow);
|
||||||
|
|
@ -821,9 +859,27 @@ pieces of code in all CRUD controllers already working in NavalPlan::
|
||||||
return visibility;
|
return visibility;
|
||||||
}
|
}
|
||||||
|
|
||||||
And usually at the end of ``doAfterCompose`` method there will be a call to
|
/**
|
||||||
``showListWindow``, that shows the list view and use ``OnlyOneVisible`` class to
|
* Show list window and reload bindings
|
||||||
hide edit/creation form.
|
*/
|
||||||
|
protected void showListWindow() {
|
||||||
|
getVisibility().showOnly(listWindow);
|
||||||
|
Util.reloadBindings(listWindow);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show edit form with different title depending on controller state and
|
||||||
|
* reload bindings
|
||||||
|
*/
|
||||||
|
protected void showEditWindow() {
|
||||||
|
getVisibility().showOnly(editWindow);
|
||||||
|
updateWindowTitle();
|
||||||
|
Util.reloadBindings(editWindow);
|
||||||
|
}
|
||||||
|
|
||||||
|
And at the end of ``doAfterCompose`` method there is a call to
|
||||||
|
``showListWindow``, that shows the list view and use ``OnlyOneVisible`` class
|
||||||
|
to hide edit/creation form.
|
||||||
|
|
||||||
|
|
||||||
Messages for users
|
Messages for users
|
||||||
|
|
@ -839,7 +895,7 @@ class called ``MessagesForUser`` which is used in all controllers to show
|
||||||
messages to users in a similar way for the whole application.
|
messages to users in a similar way for the whole application.
|
||||||
|
|
||||||
Apart from previous line on ``.zul`` file you will see the following lines
|
Apart from previous line on ``.zul`` file you will see the following lines
|
||||||
inside ``doAfterCompose`` method in controller::
|
inside ``doAfterCompose`` method in ``BaseCRUDController``::
|
||||||
|
|
||||||
private IMessagesForUser messagesForUser;
|
private IMessagesForUser messagesForUser;
|
||||||
|
|
||||||
|
|
@ -851,7 +907,6 @@ inside ``doAfterCompose`` method in controller::
|
||||||
public void doAfterCompose(Component comp) throws Exception {
|
public void doAfterCompose(Component comp) throws Exception {
|
||||||
...
|
...
|
||||||
messagesForUser = new MessagesForUser(messagesContainer);
|
messagesForUser = new MessagesForUser(messagesContainer);
|
||||||
comp.setVariable("controller", this, true);
|
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -876,16 +931,15 @@ following content:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
<window id="${arg.id}" title="${i18n:_('Stretches Function Templates List')}">
|
<window id="${arg.id}" title="${i18n:_('Stretches Function Templates List')}">
|
||||||
|
|
||||||
<newdatasortablegrid id="listStretchesFunctionTemplates"
|
<grid id="listStretchesFunctionTemplates"
|
||||||
model="@{controller.stretchesFunctionTemplates}"
|
model="@{controller.stretchesFunctionTemplates}"
|
||||||
mold="paging" pageSize="10" fixedLayout="true">
|
mold="paging" pageSize="10" fixedLayout="true">
|
||||||
|
|
||||||
<columns>
|
<columns>
|
||||||
<newdatasortablecolumn label="${i18n:_('Name')}"
|
<column label="${i18n:_('Name')}" sort="auto(lower(name))" />
|
||||||
sort="auto(lower(name))" sortDirection="ascending" />
|
<column label="${i18n:_('Operations')}" />
|
||||||
<newdatasortablecolumn label="${i18n:_('Operations')}" />
|
|
||||||
</columns>
|
</columns>
|
||||||
<rows>
|
<rows>
|
||||||
<row self="@{each='stretchesFunctionTemplate'}"
|
<row self="@{each='stretchesFunctionTemplate'}"
|
||||||
|
|
@ -900,11 +954,11 @@ following content:
|
||||||
<button sclass="icono" image="/common/img/ico_borrar1.png"
|
<button sclass="icono" image="/common/img/ico_borrar1.png"
|
||||||
hoverImage="/common/img/ico_borrar.png"
|
hoverImage="/common/img/ico_borrar.png"
|
||||||
tooltiptext="${i18n:_('Delete')}"
|
tooltiptext="${i18n:_('Delete')}"
|
||||||
onClick="controller.remove(self.parent.parent.value)"/>
|
onClick="controller.confirmDelete(self.parent.parent.value)"/>
|
||||||
</hbox>
|
</hbox>
|
||||||
</row>
|
</row>
|
||||||
</rows>
|
</rows>
|
||||||
</newdatasortablegrid>
|
</grid>
|
||||||
|
|
||||||
<button label="${i18n:_('Create')}" onClick="controller.goToCreateForm()"
|
<button label="${i18n:_('Create')}" onClick="controller.goToCreateForm()"
|
||||||
sclass="create-button global-action"/>
|
sclass="create-button global-action"/>
|
||||||
|
|
@ -915,11 +969,10 @@ In the next paragraphs different parts of the file will be reviewed.
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
<newdatasortablegrid id="listStretchesFunctionTemplates"
|
<grid id="listStretchesFunctionTemplates"
|
||||||
model="@{controller.stretchesFunctionTemplates}"
|
model="@{controller.stretchesFunctionTemplates}"
|
||||||
|
|
||||||
``NewDataSortableGrid`` is a special component defined in NavalPlan, that
|
``Grid`` is a visual ZK component with some sorting features. As you can see,
|
||||||
extends ``Grid`` component adding sorting feature for columns. As you can see,
|
|
||||||
``model`` attribute is set, which means that a method called
|
``model`` attribute is set, which means that a method called
|
||||||
``getStretchesFunctionTemplates`` in controller will be called. This method
|
``getStretchesFunctionTemplates`` in controller will be called. This method
|
||||||
will have the responsibility to communicate with model layer in order to get the
|
will have the responsibility to communicate with model layer in order to get the
|
||||||
|
|
@ -927,8 +980,7 @@ list of ``StretchesFunctionTemplate`` from database.
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
<newdatasortablecolumn label="${i18n:_('Name')}"
|
<column label="${i18n:_('Name')}" sort="auto(lower(name))" />
|
||||||
sort="auto(lower(name))" sortDirection="ascending" />
|
|
||||||
|
|
||||||
Thanks to this custom component you are able to define that *Name* column will
|
Thanks to this custom component you are able to define that *Name* column will
|
||||||
by sorted by default in ascending order.
|
by sorted by default in ascending order.
|
||||||
|
|
@ -992,6 +1044,7 @@ edition process. The file will have the following content:
|
||||||
::
|
::
|
||||||
|
|
||||||
<window id="${arg.id}">
|
<window id="${arg.id}">
|
||||||
|
<caption id="caption" sclass="caption-title" />
|
||||||
<tabbox>
|
<tabbox>
|
||||||
<tabs>
|
<tabs>
|
||||||
<tab label="${i18n:_('Edit')}" />
|
<tab label="${i18n:_('Edit')}" />
|
||||||
|
|
@ -1008,7 +1061,8 @@ edition process. The file will have the following content:
|
||||||
<label value="${i18n:_('Name')}" />
|
<label value="${i18n:_('Name')}" />
|
||||||
<textbox id="tbName"
|
<textbox id="tbName"
|
||||||
value="@{controller.stretchesFunctionTemplate.name}"
|
value="@{controller.stretchesFunctionTemplate.name}"
|
||||||
width="300px" />
|
width="300px"
|
||||||
|
onBlur="controller.updateWindowTitle()" />
|
||||||
</row>
|
</row>
|
||||||
</rows>
|
</rows>
|
||||||
</grid>
|
</grid>
|
||||||
|
|
@ -1051,7 +1105,7 @@ edition process. The file will have the following content:
|
||||||
<button onClick="controller.saveAndContinue()"
|
<button onClick="controller.saveAndContinue()"
|
||||||
label="${i18n:_('Save and Continue')}"
|
label="${i18n:_('Save and Continue')}"
|
||||||
sclass="save-button global-action" />
|
sclass="save-button global-action" />
|
||||||
<button onClick="controller.cancel()"
|
<button onClick="controller.cancelForm()"
|
||||||
label="${i18n:_('Cancel')}"
|
label="${i18n:_('Cancel')}"
|
||||||
sclass="cancel-button global-action" />
|
sclass="cancel-button global-action" />
|
||||||
|
|
||||||
|
|
@ -1064,7 +1118,8 @@ Now, let's take a look to the most important parts of the file.
|
||||||
<label value="${i18n:_('Name')}" />
|
<label value="${i18n:_('Name')}" />
|
||||||
<textbox id="tbName"
|
<textbox id="tbName"
|
||||||
value="@{controller.stretchesFunctionTemplate.name}"
|
value="@{controller.stretchesFunctionTemplate.name}"
|
||||||
width="300px" />
|
width="300px"
|
||||||
|
onBlur="controller.updateWindowTitle()" />
|
||||||
|
|
||||||
This will create a ``Textbox`` field in the form. As you can see, it is using
|
This will create a ``Textbox`` field in the form. As you can see, it is using
|
||||||
data bindings, which means that different methods will be automatically called
|
data bindings, which means that different methods will be automatically called
|
||||||
|
|
@ -1308,23 +1363,56 @@ Conversational steps
|
||||||
Now you are going to implement the form to create a new
|
Now you are going to implement the form to create a new
|
||||||
``StretchesFunctionTemplate``. As you can see in the ``.zul`` page, the method
|
``StretchesFunctionTemplate``. As you can see in the ``.zul`` page, the method
|
||||||
called in order to create a new entity is ``goToCreateForm``. This method will
|
called in order to create a new entity is ``goToCreateForm``. This method will
|
||||||
start the conversation between controller and model::
|
start the conversation between controller and model, and it's already
|
||||||
|
implemented in ``BaseCRUDController``::
|
||||||
|
|
||||||
public void goToCreateForm() {
|
/**
|
||||||
stretchesFunctionTemplateModel.initCreate();
|
* Show edit form with different title depending on controller state and
|
||||||
showEditWindow(_("Create Stretches Function Template"));
|
* reload bindings
|
||||||
|
*/
|
||||||
|
protected void showEditWindow() {
|
||||||
|
getVisibility().showOnly(editWindow);
|
||||||
|
updateWindowTitle();
|
||||||
Util.reloadBindings(editWindow);
|
Util.reloadBindings(editWindow);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show create form. Delegate in {@link #initCreate()} that should be
|
||||||
|
* implemented in subclasses.
|
||||||
|
*/
|
||||||
|
public final void goToCreateForm() {
|
||||||
|
state = CRUDControllerState.CREATE;
|
||||||
|
initCreate();
|
||||||
|
showEditWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs needed operations to initialize the creation of a new entity.
|
||||||
|
*/
|
||||||
|
protected abstract void initCreate();
|
||||||
|
|
||||||
.. NOTE::
|
.. NOTE::
|
||||||
|
|
||||||
Method ``Util::reloadBindings`` forces reload of bindings used in a component.
|
Method ``Util::reloadBindings`` forces reload of bindings used in a component.
|
||||||
For example, this is needed to refresh a list of items when some of them are
|
For example, this is needed to refresh a list of items when some of them are
|
||||||
added or removed.
|
added or removed.
|
||||||
|
|
||||||
This method calls ``initCreate`` in model to start the conversation. Moreover it
|
This method delegates in ``initCreate`` that should be implemented in
|
||||||
opens ``editWindow`` and then reload information in the form. Then you need to
|
``StretchesFunctionTemplateCRUDController``. Moreover it opens ``editWindow``
|
||||||
add the following lines in model (remember to create method in interface too)::
|
and then reload information in the form.
|
||||||
|
|
||||||
|
Implementation of ``initCreate`` in ``StretchesFunctionTemplateConverter``::
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void initCreate() {
|
||||||
|
stretchesFunctionTemplateModel.initCreate();
|
||||||
|
}
|
||||||
|
|
||||||
|
Then this method calls to ``initCreate`` in model to start the conversation.
|
||||||
|
Then you need to add the following lines in model (remember to create method in
|
||||||
|
interface too)::
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Conversation state
|
* Conversation state
|
||||||
|
|
@ -1342,8 +1430,8 @@ add the following lines in model (remember to create method in interface too)::
|
||||||
this.stretchesFunctionTemplate = StretchesFunctionTemplate.create("");
|
this.stretchesFunctionTemplate = StretchesFunctionTemplate.create("");
|
||||||
}
|
}
|
||||||
|
|
||||||
Thanks to first line, model will keep in memory current entity being created or
|
Thanks to the first line, model will keep in memory current entity being created
|
||||||
edited. As you can see in the method, a new instance of the entity is created
|
or edited. As you can see in the method, a new instance of the entity is created
|
||||||
and assigned to state variable.
|
and assigned to state variable.
|
||||||
|
|
||||||
As you are using data bindings for ``StretchesFunctionTemplate`` name, then when
|
As you are using data bindings for ``StretchesFunctionTemplate`` name, then when
|
||||||
|
|
@ -1351,19 +1439,25 @@ user modify this field, attribute ``name`` in entity will be automatically set.
|
||||||
|
|
||||||
In order to allow users add new ``StretchTemplate`` you need to implement method
|
In order to allow users add new ``StretchTemplate`` you need to implement method
|
||||||
``addStretchTemplate`` in controller. As usual this method delegates in model in
|
``addStretchTemplate`` in controller. As usual this method delegates in model in
|
||||||
oder to perform the real operation. You need to modify ``doAfterCompose`` in
|
oder to perform the real operation. You need to override ``doAfterCompose`` at
|
||||||
order to be able to access input elements in the form and create the new
|
``StretchesFunctionTemplateCRUDController`` in order to be able to access input
|
||||||
method::
|
elements in the form and create the new method::
|
||||||
|
|
||||||
|
private Grid stretchTemplates;
|
||||||
|
private Intbox durationPercentage;
|
||||||
|
private Intbox progressPercentage;
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void doAfterCompose(Component comp) throws Exception {
|
public void doAfterCompose(Component comp) throws Exception {
|
||||||
...
|
super.doAfterCompose(comp);
|
||||||
|
|
||||||
stretchTemplates = (Grid) editWindow.getFellow("stretchTemplates");
|
stretchTemplates = (Grid) editWindow.getFellow("stretchTemplates");
|
||||||
durationPercentage = (Intbox) stretchTemplates
|
durationPercentage = (Intbox) stretchTemplates
|
||||||
.getFellow("durationPercentage");
|
.getFellow("durationPercentage");
|
||||||
progressPercentage = (Intbox) stretchTemplates
|
progressPercentage = (Intbox) stretchTemplates
|
||||||
.getFellow("progressPercentage");
|
.getFellow("progressPercentage");
|
||||||
...
|
|
||||||
}
|
}
|
||||||
|
|
||||||
...
|
...
|
||||||
|
|
@ -1414,7 +1508,8 @@ to show ``StrechTemplate`` information in the window::
|
||||||
row.appendChild(Util.createRemoveButton(new EventListener() {
|
row.appendChild(Util.createRemoveButton(new EventListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onEvent(Event event) throws Exception {
|
public void onEvent(Event event) throws Exception {
|
||||||
confirmRemoveStretchTemplate(stretchTemplate);
|
stretchesFunctionTemplateModel
|
||||||
|
.removeStretchTemplate(stretchTemplate);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
@ -1427,44 +1522,111 @@ to show ``StrechTemplate`` information in the window::
|
||||||
|
|
||||||
You will implement ``RowRenderer`` interface, and add the different components
|
You will implement ``RowRenderer`` interface, and add the different components
|
||||||
for each column in ``Row`` element. Moreover, you will create a remove button,
|
for each column in ``Row`` element. Moreover, you will create a remove button,
|
||||||
associated with a new method in the controller. This method will be in charge to
|
associated with a new method in the model which removes the ``StretchTemplate``.
|
||||||
ask user if is sure about removing the element, and then it will remove the
|
|
||||||
``StretchTemplate``.
|
|
||||||
|
|
||||||
The last step is to close the conversation successfully or not. Then you will
|
The last step is to close the conversation successfully or not. For this there
|
||||||
need to implement the following methods in controller::
|
are already several methods in ``BaseCRUDController``::
|
||||||
|
|
||||||
private boolean save() {
|
/**
|
||||||
|
* Save current form and go to list view. Delegate in {@link #save()} that
|
||||||
|
* should be implemented in subclasses.
|
||||||
|
*/
|
||||||
|
public final void saveAndExit() {
|
||||||
try {
|
try {
|
||||||
stretchesFunctionTemplateModel.confirmSave();
|
saveCommonActions();
|
||||||
messagesForUser.showMessage(Level.INFO,
|
goToList();
|
||||||
_("Stretches function template saved"));
|
|
||||||
return true;
|
|
||||||
} catch (ValidationException e) {
|
} catch (ValidationException e) {
|
||||||
messagesForUser.showInvalidValues(e);
|
messagesForUser.showInvalidValues(e);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void saveAndExit() {
|
/**
|
||||||
if (save()) {
|
* Common save actions:<br />
|
||||||
showListWindow();
|
* <ul>
|
||||||
Util.reloadBindings(listWindow);
|
* <li>Delegate in {@link #beforeSaving()} that could be implemented if
|
||||||
|
* needed in subclasses.</li>
|
||||||
|
* <li>Use {@link ConstraintChecker} to validate form.</li>
|
||||||
|
* <li>Delegate in {@link #save()} that should be implemented in subclasses.
|
||||||
|
* </li>
|
||||||
|
* <li>Show message to user.</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @throws ValidationException
|
||||||
|
* If form is not valid or save has any validation problem
|
||||||
|
*/
|
||||||
|
private void saveCommonActions() throws ValidationException {
|
||||||
|
beforeSaving();
|
||||||
|
|
||||||
|
save();
|
||||||
|
|
||||||
|
messagesForUser.showMessage(
|
||||||
|
Level.INFO,
|
||||||
|
_("{0} \"{1}\" saved", getEntityType(), getEntityBeingEdited()
|
||||||
|
.getHumanId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save current form and continue in edition view. Delegate in
|
||||||
|
* {@link #save()} that should be implemented in subclasses.
|
||||||
|
*/
|
||||||
|
public final void saveAndContinue() {
|
||||||
|
try {
|
||||||
|
saveCommonActions();
|
||||||
|
goToEditForm(getEntityBeingEdited());
|
||||||
|
} catch (ValidationException e) {
|
||||||
|
messagesForUser.showInvalidValues(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void saveAndContinue() {
|
/**
|
||||||
if (save()) {
|
* Performs additional operations before saving (usually do some checks or
|
||||||
stretchesFunctionTemplateModel
|
* generate codes of related entities).
|
||||||
.initEdit(stretchesFunctionTemplateModel
|
*
|
||||||
.getStretchesFunctionTemplate());
|
* Default behavior use {@link ConstraintChecker} to see if
|
||||||
}
|
* {@link #editWindow} is valid, however it could be overridden if needed.
|
||||||
|
*/
|
||||||
|
protected void beforeSaving() throws ValidationException {
|
||||||
|
ConstraintChecker.isValid(editWindow);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void cancel() {
|
/**
|
||||||
|
* Performs actions to save current form
|
||||||
|
*
|
||||||
|
* @throws ValidationException
|
||||||
|
* If entity is not valid
|
||||||
|
*/
|
||||||
|
protected abstract void save() throws ValidationException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close form and go to list view. Delegate in {@link #cancel()} that could
|
||||||
|
* be implemented in subclasses if needed.
|
||||||
|
*/
|
||||||
|
public final void cancelForm() {
|
||||||
|
cancel();
|
||||||
|
goToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs needed actions to cancel edition
|
||||||
|
*
|
||||||
|
* Default behavior do nothing, however it could be overridden if needed.
|
||||||
|
*/
|
||||||
|
protected void cancel() {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Then you will need to implement the following methods in controller. ``cancel``
|
||||||
|
is not mandatory but here is used to show an example about it::
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void save() throws ValidationException {
|
||||||
|
stretchesFunctionTemplateModel.confirmSave();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void cancel() {
|
||||||
stretchesFunctionTemplateModel.cancel();
|
stretchesFunctionTemplateModel.cancel();
|
||||||
showListWindow();
|
|
||||||
Util.reloadBindings(listWindow);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
``save`` method will call ``confirmSave`` in model and return true if the
|
``save`` method will call ``confirmSave`` in model and return true if the
|
||||||
|
|
@ -1477,15 +1639,10 @@ model::
|
||||||
@Transactional
|
@Transactional
|
||||||
public void confirmSave() throws ValidationException {
|
public void confirmSave() throws ValidationException {
|
||||||
stretchesFunctionTemplateDAO.save(stretchesFunctionTemplate);
|
stretchesFunctionTemplateDAO.save(stretchesFunctionTemplate);
|
||||||
resetConversationState();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void cancel() {
|
public void cancel() {
|
||||||
resetConversationState();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void resetConversationState() {
|
|
||||||
stretchesFunctionTemplate = null;
|
stretchesFunctionTemplate = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1504,12 +1661,34 @@ Solving issues with detached objects
|
||||||
As it was already stated, you need to be careful managing **detached objects**.
|
As it was already stated, you need to be careful managing **detached objects**.
|
||||||
For example, if you think in edit an already stored
|
For example, if you think in edit an already stored
|
||||||
``StretchesFunctionTemplate``, you will have a very similar method to
|
``StretchesFunctionTemplate``, you will have a very similar method to
|
||||||
``goToCreateForm`` in controller::
|
``goToCreateForm`` in ``BaseCRUDController``::
|
||||||
|
|
||||||
public void goToEditForm(StretchesFunctionTemplate stretchesFunctionTemplate) {
|
/**
|
||||||
|
* Show edit form for entity passed as parameter. Delegate in
|
||||||
|
* {@link #initEdit(entity)} that should be implemented in subclasses.
|
||||||
|
*
|
||||||
|
* @param entity
|
||||||
|
* Entity to be edited
|
||||||
|
*/
|
||||||
|
public final void goToEditForm(T entity) {
|
||||||
|
state = CRUDControllerState.EDIT;
|
||||||
|
initEdit(entity);
|
||||||
|
showEditWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs needed operations to initialize the edition of a new entity.
|
||||||
|
*
|
||||||
|
* @param entity
|
||||||
|
* Entity to be edited
|
||||||
|
*/
|
||||||
|
protected abstract void initEdit(T entity);
|
||||||
|
|
||||||
|
And the specific implementation in ``StretchesFunctionTemplateCRUDController``::
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void initEdit(StretchesFunctionTemplate stretchesFunctionTemplate) {
|
||||||
stretchesFunctionTemplateModel.initEdit(stretchesFunctionTemplate);
|
stretchesFunctionTemplateModel.initEdit(stretchesFunctionTemplate);
|
||||||
showEditWindow(_("Edit Stretches Function Template"));
|
|
||||||
Util.reloadBindings(editWindow);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Then a new method called ``initEdit`` will appear in model as initial
|
Then a new method called ``initEdit`` will appear in model as initial
|
||||||
|
|
@ -1833,18 +2012,26 @@ following validation on ``_editStretchesFunctionTemplate.zul`` file::
|
||||||
<textbox id="tbName"
|
<textbox id="tbName"
|
||||||
value="@{controller.stretchesFunctionTemplate.name}"
|
value="@{controller.stretchesFunctionTemplate.name}"
|
||||||
width="300px"
|
width="300px"
|
||||||
|
onBlur="controller.updateWindowTitle()"
|
||||||
constraint="no empty:${i18n:_('cannot be null or empty')}" />
|
constraint="no empty:${i18n:_('cannot be null or empty')}" />
|
||||||
|
|
||||||
Now, if users set an empty name, they will receive an error in a pop-up. However,
|
Now, if users set an empty name, they will receive an error in a pop-up. However,
|
||||||
if they click *Save* button, the request to sever will be sent and then they
|
if they click *Save* button, the request to sever will be sent and then they
|
||||||
will get validations errors due to Hibernate constraints.
|
will get validations errors due to Hibernate constraints.
|
||||||
|
|
||||||
In order to do not allow users send wrong data to server you should use
|
In order to do not follow with the conversation when user has not filled the
|
||||||
``ConstraintChecker`` utility in ``StretchesFunctionTemplateCRUDController``::
|
right data ``BaseCRUDController`` uses ``ConstraintChecker`` utility in default
|
||||||
|
``beforeSaving`` method::
|
||||||
|
|
||||||
private boolean save() {
|
/**
|
||||||
|
* Performs additional operations before saving (usually do some checks or
|
||||||
|
* generate codes of related entities).
|
||||||
|
*
|
||||||
|
* Default behavior use {@link ConstraintChecker} to see if
|
||||||
|
* {@link #editWindow} is valid, however it could be overridden if needed.
|
||||||
|
*/
|
||||||
|
protected void beforeSaving() throws ValidationException {
|
||||||
ConstraintChecker.isValid(editWindow);
|
ConstraintChecker.isValid(editWindow);
|
||||||
...
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1897,7 +2084,8 @@ know how to develop a web service in the application.
|
||||||
First of all, you need to make that ``StretchesFunctionTemplate`` inherits from
|
First of all, you need to make that ``StretchesFunctionTemplate`` inherits from
|
||||||
``IntegrationEntity``::
|
``IntegrationEntity``::
|
||||||
|
|
||||||
public class StretchesFunctionTemplate extends IntegrationEntity {
|
public class StretchesFunctionTemplate extends IntegrationEntity implements
|
||||||
|
IHumanIdentifiable {
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue