Dirty state for forms considering Collection properties - java

I have the following model:
Person
public class Person {
private String name;
private Set<Phone> phones;
// setters and getters
}
My ViewModel has following filds:
public class PersonViewModel {
private Person selected;
private Phone selectedPhone;
// setters and getters for fields
#Command
public void save() {
// basically persists selected field
}
// additional commands
}
My View will create a form like:
<groupbox form="#id('fx') #load(vm.selected) #save(vm.selected, before='save')">
and will expose the name in a textbox and the phones on a listbox. Everytime I selected a phone I set the property selectedPhone. The phone can be edited by using another textfields on my view.
When I change the name the dirty state of my form is updated, and this is good. When I select a Phone I can see that the property selectedPhone of my viewmodel is set. But when I change the phone using the editBox bounded to selectedPhone the dirty state of my form doesn't changed.
This is expected since I'm changing the ViewModel and not the form. But what would be the way to solve this problem, since when a phone is changed, means the Person is changed too, because there are things to save?
The view implementation is like this:
...
<groupbox form="#id('fx') #load(vm.selected) #save(vm.selected, before='save')" vflex="1">
<textbox width="50px" value="#bind(fx.name)" />
<listbox vflex="true" model="#load(fx.phones)" selectedItem="#bind(vm.selectedPhone)">
<!-- shows the phone record -->
</listbox>
<textbox width="50px" value="#bind(vm.selectedPhone.number)" />
</groupbox>
....
When I change the name it updates the form (fx) and its state is dirty. But changing the selectedPhone.number it doesn't pass the form and due this reason it isn't marked as dirty. Phones is a collection, how shall it be handle to present within the GUI and mark the entire form as dirty if a property from phone that is contained in the collection of fx is changed?

After thinking a bit about the situation, for me there is a simple solution,
just add an additional object, cos your problem is that you don't access fx,
so we just make a container so you have a new fx which is accessed when you change something in your collection.
public class MyPersonContainer {
private Person selected;
private Phone selectedPhone;
//getter/setter
}
and change your VM
public class PersonViewModel {
//private Person selected;
//private Phone selectedPhone;
private MyPersonContainer container;
...
}
as well as your view
<groupbox form="#id('fx') #load(vm.container) #save(vm.container, before='save')">
<listbox vflex="true" model="#load(fx.selected.phones)" selectedItem="#bind(fx.selectedPhone)">
...
<textbox width="50px" value="#bind(fx.selectedPhone.number)" />

Related

Values of Java fields are not shown in aem component

I have aem component and Sling model with three injected fields corresponding to dialog fields in component. Only value of headline field is shown on the component. I tried to change the type of other fields to textfield, but that didn't make any effect. I don't see any other difference among my fields.
Here is Java class:
#Model(
adaptables = {SlingHttpServletRequest.class},
defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL
)
public class ArticlePreviewImpl implements ArticlePreview {
#Inject
private String contentpath;
#Inject
private String headline;
#Inject
private String elements;
#Override
public String getContentpath() {
return contentpath;
}
#Override
public String getHeadline() {
return headline;
}
#Override
public String getElements() {
return elements;
}
}
Here is HTL:
<!--/* articlepreview.html */-->
<div class="cmp-apreview"
data-sly-use.apreview="com.training.core.models.ArticlePreview">
path: ${apreview.contentPath}<br/>
headline: ${apreview.headline}<br/>
elements: ${apreview.elements}
</div>
<sly data-sly-use.clientlib="/libs/granite/sightly/templates/clientlib.html"></sly>
<sly data-sly-call="${clientlib.js # categories='cq.authoring.dialog, training.components'}"></sly>
Here are definitions of dialog fields from .content.xml:
<contentPath
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/pathfield"
fieldLabel="Path to directory with content"
name="./contentpath"/>
...
<headline
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
emptyText="Enter the headline to display."
fieldLabel="Headline"
name="./headline"/>
...
<elements
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/numberfield"
defaultValue="3"
fieldDescription="Number of elements in one portion"
fieldLabel="Number of elements in one portion"
max="{Double}15.0"
min="{Double}1.0"
name="./elements"
value="3"/>
Only value of apreview.headline is shown on the component, the two others are not.
For ${apreview.contentPath} you need to change your getter to getContentPath (note the camelCase). For ${apreview.elements} the getter looks ok, you'll need to check if the property is populated correctly in JCR for your resource (as the DefaultInjectionStrategy.OPTIONAL allows null fields during injection).
It was very easy and stupid - I pushed HTL into repository instead of building entire project

HowTo update Backing Bean before partial refresh

I want to refresh a XPages' control, that displays data based on input of a ListBox. I use a Backing Bean that holds the data. The Backing Bean is connected by EL Value Binding.
For a Computed Text Control, the Backing Bean returns the first selected value of the listData property.
public class BackingBean implements Serializable {
private static final long serialVersionUID = 1L;
private List listData;
private String displayData;
public BackingBean() {}
public List getListData() {
System.out.println("getListData()");
return listData;
}
public void setListData(List listData) {
System.out.println("setListData()");
this.listData = listData;
}
public String getDisplayData() {
System.out.println("getDisplayData()");
if(listData != null && listData.size() > 0) {
return (String) listData.get(0);
}
return "NO LIST DATA";
}
}
Here is my XPage code:
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core"
xmlns:xe="http://www.ibm.com/xsp/coreex">
<xp:div id="refreshMe">
<xp:listBox id="listBox1">
<xp:this.value><![CDATA[#{backingBean.listData}]]></xp:this.value>
<xp:selectItem itemLabel="Value 1"></xp:selectItem>
<xp:selectItem itemLabel="Value 2"></xp:selectItem>
<xp:eventHandler event="onchange" submit="true" refreshMode="partial" refreshId="refreshMe">
</xp:eventHandler>
</xp:listBox>
<xp:text escape="true" id="computedField1" value="#{backingBean.displayData}"></xp:text>
</xp:div>
</xp:view>
The problem is, on partial refresh, the data will be sent to the server, but the setListData setter is never called.
The computed text only shows "NO LIST DATA".
view:_id1:listBox1:Value 1 // <-- selected option is sent to the server
$$viewid:!e9x9cl6xse!
$$xspsubmitid:view:_id1:_id298
$$xspexecid:
$$xspsubmitvalue:
$$xspsubmitscroll:0|0
view:_id1:view:_id1
How can I do a partial refresh off a control, whose value relies on value bound data of another control?
Found cause and solution!
When I set disableValidators to true the setListData setter will be called and the Backing Bean gets updated.
<xp:listBox id="listBox1">
<xp:this.value><![CDATA[#{controller.listData}]]></xp:this.value>
<xp:selectItem itemLabel="Value 1"></xp:selectItem>
<xp:selectItem itemLabel="Value 2"></xp:selectItem>
<xp:eventHandler event="onchange" submit="true" refreshMode="partial" refreshId="refreshMe" disableValidators="true"></xp:eventHandler>
</xp:listBox>
To be honest, I also want to update data by using XSP.partialRefreshPost. When I try this, I'm not able to disable validation out of the box.
But Sven Hasselbach posted a solution for this:
https://stackoverflow.com/a/21931796/4735030
(In short: Implement a PhaseListener that disables Validation on demand.)
The event handler, that uses XSP.partialRefreshPost looks like this:
<xp:eventHandler event="onchange" submit="false">
<xp:this.script><![CDATA[
XSP.partialRefreshPost(#{id:refreshMe}', {'params': {'disableValidation':true}});
]]></xp:this.script>
</xp:eventHandler>
Try adding a setListData(String listData) method as well. "1" is not a List, so I wouldn't expect it to call your current method. I'm also not sure whether an xp:listBox maps to a List or a Vector. I would expect it to map to a Vector.

Spring: How to register converter/editor for nested list of objects

I have quite complicated html form and I'm trying to map it to models. Form contains information about Board (name, date, owner, etc...) and list of Items. Each item contains some data too. Items are added/deleted dynamically so I use JS to add indexes before form is sent. Here is part of my form:
<div>
<label>Link:</label>
<input type="text" name="items[<index>].link">
<label>JScript:</label>
<select name="items[<index>].javaScript">
<option>disable</option>
<option>enable</option>
</select>
</div>
I use form's onsubmit function to change to appropriate value. It maps fine for my model which is Board with List of items:
public class Board
{
private long id;
private User owner;
private String title;
private List<Item> items;
...
}
what I would like to do is register custom editor or converter for List because I need to do some manual changes.
I've tried
binder.registerCustomEditor(Item.class, "items", new PropertyEditorSupport()
and
binder.registerCustomEditor(List.class, "board.items", new CustomCollectionEditor(List.class)
but it doesn't work at all.

Conditional object population

Hy guys, I've turning around a problem using Struts2.
Basically I've a form that the user can submit filling different fields. I would like to populate conditionally the right object with the properties insert by the user.
So for example, if the user fill the form in a intermediary way I would like to use the properties for to create an Intermediary object, instead if the user fill the request in entity way, i'll use the properties for to create the Entity object, ecc...
These object share the same interface Request.
Is there any way to use polimorphically properties for to create Intermediary or Entity object?
Suppose this is my two POJO:
public class Intermediary implements Request{
private String name;
private String surname;
private String code;
private String FiscalCode;
private String address;
...
/*Getters and setters */
....
}
public class Entity implements Request{
private String name;
private String surname;
private String code;
....
/*Getters and setters */
....
}
This could be my form into the jsp page:
<s:form action="fillRequest" id="formRichiesta" name="formRichiesta" method="post">
<s:radio id="tipoRichiedente" name="tipoRichiedente" list="richiedenti">
<label>Name</label><s:textfield name="name"/>
<label>Surname</label><s:textfield name="surname"/>
<label>code</label><s:textfield name="code"/>
<label>Adress</label><s:textfield name="adress"/>
<label>Fiscal Code</label><s:textfield name="fiscalCode"/>
</s:form>
<s:submit>
Basically my problem is that I can discover the kind of request the the user trying to fill only ofter submission and watching at the radiobutton state.
Is there any way to mapping the properties into the right object directly instead of creating one big object with all the form properties and then create the right implementation? for to map properties could I use the interface reference?
For example image my action:
public class RequestAction {
private Request request;
....
}
So in my jsp could I use:
<label>Name</label><s:textfield name="request.name"/>
Thanks in advance.

Partially submitting a form for the purpose of "sequentially" asking for input

I have a JSF page where users can enter their car into my database. The form has three input fields:
Manufacturer
Model
Registration
I am using PrimeFaces 3.0.M2 and both the Manufacturer and Model field are autocomplete input fields:
<p:autoComplete id="manufacturer"
minQueryLength="3"
completeMethod="#{carController.completeManufacturer}"
forceSelection="true"
value="#{carController.manufacturer}" />
The field for the model looks the same, with slightly different values obviously.
The managed bean looks as follows (slightly abbreviated):
private String manufacturer;
private String model;
private String registration;
public List<String> completeManufacturer(String query) {
List<String> ms = new ArrayList<String>();
for (Manufacturer m : manufacturerFacade.findAllByName(query)) {
ms.add(m.getLongName());
}
return ms;
}
public List<String> completeModel(String query) {
List<String> ms = new ArrayList<String>();
for (Model m :
modelFacade.findAllByManufacturer(manufacturerFacade.findByName(manufacturer))) {
ms.add(m.getShortName());
}
return ms;
}
The problem lies in completing the model field. I need this field to only display autocompletion results based on the selected manufacturer, but the manufacturer String in the managed bean does not get populated until the entire form is submitted, so I cannot look the models up that are associated with the selected manufacturer.
How would I go about submitting only the manufacturer field, without submitting the entire form, so I can look the models of the selected manufacturer up?
Thanks!
Just add a selectListener, like so:
<p:autoComplete id="manufacturer"
minQueryLength="3"
completeMethod="#{carController.completeManufacturer}"
forceSelection="true"
selectListener="#{carController.manufacturerSelected}"
value="#{carController.manufacturer}" />
and then in the controller:
public void manufacturerSelected(SelectEvent vce) {
String nameOfSelected = vce.getObject().toString();
// whatever logic comes to your mind
}
You could add an extra ajax handler to the manafucturer input field and then handle the onchange event. In the server-side handler, simply remember the new value in your backing bean.
If you then put your backing bean in the view scope, the ajax requests originating from the model input field will get the same instance and you have direct access to the manufacturer field that you previously remembered.

Categories

Resources