Values of Java fields are not shown in aem component - java

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

Related

getting the rest of data in database row with JSF [duplicate]

I am creating a web application, where you have to read a list of objects / entities from a DB and populate it in a JSF <h:selectOneMenu>. I am unable to code this. Can someone show me how to do it?
I know how to get a List<User> from the DB. What I need to know is, how to populate this list in a <h:selectOneMenu>.
<h:selectOneMenu value="#{bean.name}">
...?
</h:selectOneMenu>
Based on your question history, you're using JSF 2.x. So, here's a JSF 2.x targeted answer. In JSF 1.x you would be forced to wrap item values/labels in ugly SelectItem instances. This is fortunately not needed anymore in JSF 2.x.
Basic example
To answer your question directly, just use <f:selectItems> whose value points to a List<T> property which you preserve from the DB during bean's (post)construction. Here's a basic kickoff example assuming that T actually represents a String.
<h:selectOneMenu value="#{bean.name}">
<f:selectItems value="#{bean.names}" />
</h:selectOneMenu>
with
#ManagedBean
#RequestScoped
public class Bean {
private String name;
private List<String> names;
#EJB
private NameService nameService;
#PostConstruct
public void init() {
names = nameService.list();
}
// ... (getters, setters, etc)
}
Simple as that. Actually, the T's toString() will be used to represent both the dropdown item label and value. So, when you're instead of List<String> using a list of complex objects like List<SomeEntity> and you haven't overridden the class' toString() method, then you would see com.example.SomeEntity#hashcode as item values. See next section how to solve it properly.
Also note that the bean for <f:selectItems> value does not necessarily need to be the same bean as the bean for <h:selectOneMenu> value. This is useful whenever the values are actually applicationwide constants which you just have to load only once during application's startup. You could then just make it a property of an application scoped bean.
<h:selectOneMenu value="#{bean.name}">
<f:selectItems value="#{data.names}" />
</h:selectOneMenu>
Complex objects as available items
Whenever T concerns a complex object (a javabean), such as User which has a String property of name, then you could use the var attribute to get hold of the iteration variable which you in turn can use in itemValue and/or itemLabel attribtues (if you omit the itemLabel, then the label becomes the same as the value).
Example #1:
<h:selectOneMenu value="#{bean.userName}">
<f:selectItems value="#{bean.users}" var="user" itemValue="#{user.name}" />
</h:selectOneMenu>
with
private String userName;
private List<User> users;
#EJB
private UserService userService;
#PostConstruct
public void init() {
users = userService.list();
}
// ... (getters, setters, etc)
Or when it has a Long property id which you would rather like to set as item value:
Example #2:
<h:selectOneMenu value="#{bean.userId}">
<f:selectItems value="#{bean.users}" var="user" itemValue="#{user.id}" itemLabel="#{user.name}" />
</h:selectOneMenu>
with
private Long userId;
private List<User> users;
// ... (the same as in previous bean example)
Complex object as selected item
Whenever you would like to set it to a T property in the bean as well and T represents an User, then you would need to bake a custom Converter which converts between User and an unique string representation (which can be the id property). Do note that the itemValue must represent the complex object itself, exactly the type which needs to be set as selection component's value.
<h:selectOneMenu value="#{bean.user}" converter="#{userConverter}">
<f:selectItems value="#{bean.users}" var="user" itemValue="#{user}" itemLabel="#{user.name}" />
</h:selectOneMenu>
with
private User user;
private List<User> users;
// ... (the same as in previous bean example)
and
#ManagedBean
#RequestScoped
public class UserConverter implements Converter {
#EJB
private UserService userService;
#Override
public Object getAsObject(FacesContext context, UIComponent component, String submittedValue) {
if (submittedValue == null || submittedValue.isEmpty()) {
return null;
}
try {
return userService.find(Long.valueOf(submittedValue));
} catch (NumberFormatException e) {
throw new ConverterException(new FacesMessage(String.format("%s is not a valid User ID", submittedValue)), e);
}
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object modelValue) {
if (modelValue == null) {
return "";
}
if (modelValue instanceof User) {
return String.valueOf(((User) modelValue).getId());
} else {
throw new ConverterException(new FacesMessage(String.format("%s is not a valid User", modelValue)), e);
}
}
}
(please note that the Converter is a bit hacky in order to be able to inject an #EJB in a JSF converter; normally one would have annotated it as #FacesConverter(forClass=User.class), but that unfortunately doesn't allow #EJB injections)
Don't forget to make sure that the complex object class has equals() and hashCode() properly implemented, otherwise JSF will during render fail to show preselected item(s), and you'll on submit face Validation Error: Value is not valid.
public class User {
private Long id;
#Override
public boolean equals(Object other) {
return (other != null && getClass() == other.getClass() && id != null)
? id.equals(((User) other).id)
: (other == this);
}
#Override
public int hashCode() {
return (id != null)
? (getClass().hashCode() + id.hashCode())
: super.hashCode();
}
}
Complex objects with a generic converter
Head to this answer: Implement converters for entities with Java Generics.
Complex objects without a custom converter
The JSF utility library OmniFaces offers a special converter out the box which allows you to use complex objects in <h:selectOneMenu> without the need to create a custom converter. The SelectItemsConverter will simply do the conversion based on readily available items in <f:selectItem(s)>.
<h:selectOneMenu value="#{bean.user}" converter="omnifaces.SelectItemsConverter">
<f:selectItems value="#{bean.users}" var="user" itemValue="#{user}" itemLabel="#{user.name}" />
</h:selectOneMenu>
See also:
Our <h:selectOneMenu> wiki page
View-Page
<h:selectOneMenu id="selectOneCB" value="#{page.selectedName}">
<f:selectItems value="#{page.names}"/>
</h:selectOneMenu>
Backing-Bean
List<SelectItem> names = new ArrayList<SelectItem>();
//-- Populate list from database
names.add(new SelectItem(valueObject,"label"));
//-- setter/getter accessor methods for list
To display particular selected record, it must be one of the values in the list.
Roll-your-own generic converter for complex objects as selected item
The Balusc gives a very useful overview answer on this subject. But there is one alternative he does not present: The Roll-your-own generic converter that handles complex objects as the selected item. This is very complex to do if you want to handle all cases, but pretty simple for simple cases.
The code below contains an example of such a converter. It works in the same spirit as the OmniFaces SelectItemsConverter as it looks through the children of a component for UISelectItem(s) containing objects. The difference is that it only handles bindings to either simple collections of entity objects, or to strings. It does not handle item groups, collections of SelectItems, arrays and probably a lot of other things.
The entities that the component binds to must implement the IdObject interface. (This could be solved in other way, such as using toString.)
Note that the entities must implement equals in such a way that two entities with the same ID compares equal.
The only thing that you need to do to use it is to specify it as converter on the select component, bind to an entity property and a list of possible entities:
<h:selectOneMenu value="#{bean.user}" converter="selectListConverter">
<f:selectItem itemValue="unselected" itemLabel="Select user..."/>
<f:selectItem itemValue="empty" itemLabel="No user"/>
<f:selectItems value="#{bean.users}" var="user" itemValue="#{user}" itemLabel="#{user.name}" />
</h:selectOneMenu>
Converter:
/**
* A converter for select components (those that have select items as children).
*
* It convertes the selected value string into one of its element entities, thus allowing
* binding to complex objects.
*
* It only handles simple uses of select components, in which the value is a simple list of
* entities. No ItemGroups, arrays or other kinds of values.
*
* Items it binds to can be strings or implementations of the {#link IdObject} interface.
*/
#FacesConverter("selectListConverter")
public class SelectListConverter implements Converter {
public static interface IdObject {
public String getDisplayId();
}
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if (value == null || value.isEmpty()) {
return null;
}
return component.getChildren().stream()
.flatMap(child -> getEntriesOfItem(child))
.filter(o -> value.equals(o instanceof IdObject ? ((IdObject) o).getDisplayId() : o))
.findAny().orElse(null);
}
/**
* Gets the values stored in a {#link UISelectItem} or a {#link UISelectItems}.
* For other components returns an empty stream.
*/
private Stream<?> getEntriesOfItem(UIComponent child) {
if (child instanceof UISelectItem) {
UISelectItem item = (UISelectItem) child;
if (!item.isNoSelectionOption()) {
return Stream.of(item.getValue());
}
} else if (child instanceof UISelectItems) {
Object value = ((UISelectItems) child).getValue();
if (value instanceof Collection) {
return ((Collection<?>) value).stream();
} else {
throw new IllegalStateException("Unsupported value of UISelectItems: " + value);
}
}
return Stream.empty();
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
if (value == null) return null;
if (value instanceof String) return (String) value;
if (value instanceof IdObject) return ((IdObject) value).getDisplayId();
throw new IllegalArgumentException("Unexpected value type");
}
}
I'm doing it like this:
Models are ViewScoped
converter:
#Named
#ViewScoped
public class ViewScopedFacesConverter implements Converter, Serializable
{
private static final long serialVersionUID = 1L;
private Map<String, Object> converterMap;
#PostConstruct
void postConstruct(){
converterMap = new HashMap<>();
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object object) {
String selectItemValue = String.valueOf( object.hashCode() );
converterMap.put( selectItemValue, object );
return selectItemValue;
}
#Override
public Object getAsObject(FacesContext context, UIComponent component, String selectItemValue){
return converterMap.get(selectItemValue);
}
}
and bind to component with:
<f:converter binding="#{viewScopedFacesConverter}" />
If you will use entity id rather than hashCode you can hit a collision- if you have few lists on one page for different entities (classes) with the same id
Call me lazy but coding a Converter seems like a lot of unnecessary work. I'm using Primefaces and, not having used a plain vanilla JSF2 listbox or dropdown menu before, I just assumed (being lazy) that the widget could handle complex objects, i.e. pass the selected object as is to its corresponding getter/setter like so many other widgets do. I was disappointed to find (after hours of head scratching) that this capability does not exist for this widget type without a Converter. In fact if you supply a setter for the complex object rather than for a String, it fails silently (simply doesn't call the setter, no Exception, no JS error), and I spent a ton of time going through BalusC's excellent troubleshooting tool to find the cause, to no avail since none of those suggestions applied. My conclusion: listbox/menu widget needs adapting that other JSF2 widgets do not. This seems misleading and prone to leading the uninformed developer like myself down a rabbit hole.
In the end I resisted coding a Converter and found through trial and error that if you set the widget value to a complex object, e.g.:
<p:selectOneListbox id="adminEvents" value="#{testBean.selectedEvent}">
... when the user selects an item, the widget can call a String setter for that object, e.g. setSelectedThing(String thingString) {...}, and the String passed is a JSON String representing the Thing object. I can parse it to determine which object was selected. This feels a little like a hack, but less of a hack than a Converter.

Spring boot Thymeleaf same instance passed between endpoints as #ModelAttribute

I have a question regarding Thymeleaf and Spring Boot. I'm creating a form wizard and I would like to have the same object passed between multiple controllers, so that the object (SimpleCharacter) stores each time the value from each page.
What I have right now is, that with each endpoint, I get a new object created that "forgets" what I wanted to store from the previous page. How can I achieve that to have the same instance of the object passed between endpoints and in the end fully completed object with fields from each previous endpoint?
private static final String CHARACTER = "character";
#GetMapping(value = "/new-char/01_race")
public String showCharWizRace(Model model) {
CharacterDto character = new SimpleCharacter();
model.addAttribute(CHARACTER, character);
return "new-char/01_race";
}
#PostMapping(value = "/new-char/02_class")
public String showCharWizClass(Model model, #ModelAttribute CharacterDto character) {
model.addAttribute(CHARACTER, character);
model.addAttribute("classes", charClassService.findAll());
return "new-char/02_class";
}
#PostMapping(value = "/new-char/03_attributes")
public String showCharWizAttributes(Model model, #ModelAttribute CharacterDto character) {
model.addAttribute(CHARACTER, character);
return "new-char/03_attributes";
}
Thank you very much for all valuable hints and help. I've searched the Web, but couldn't find anything useful to point me in the right direction.
EDIT: But if you make CharacterDto have more fields for example race, class, attributes and use each time only one page (one form) to provide one field, spring "forgets" the other property when opening the next form. For example: 1st page: race is set, 2nd page (no race field existing here) class is set but in this place the previously set race had been already forgotten.
CharacterDto fields, that should be filled step by step on each page:
private String race;
private String charClass;
private int strength;
private int endurance;
private int dexterity;
private int charisma;
private int intelligence;
private int perception;
private String name;
private String surname;
private String description;
private String title;
private String avatar;
First, your character field are inside a spring form?
If yes, you also could to store your variable in a hidden field and pass this by #RequestParam.
Follow a example:
<input th:field="*{character}" name="character"/>
And in your controller method add a request parameter variable
showCharWizClass(#RequestParam(value = "character", required = false) String character, otherVariables){}
If it doesn't work, you also try to use something like a template strategy with session.
Putting your variable in a session scope, changing the variable with each request and removing it on last access.
Here a good link about access data from templates:
https://www.thymeleaf.org/doc/articles/springmvcaccessdata.html
UPDATE
You need to combine Model and Session Attributes in your workflow pages.
In your controller add a SessionAttribute pointing to the DTO that is using, like this:
#Controller
#SessionAttributes("character")
public class WizardController { ..
And when you have finished your flow, you can end session attributes this way.
#GetMapping(value = "/new-char/04_clear")
public String clearSession(SessionStatus sessionStatus) {
sessionStatus.setComplete();
return "new-char/04_clear";
}
If you look at my example code I add a new page to clean session and restart a form with a default DTO values.

JSR-303 / Spring MVC - validate conditionally using groups

I worked out a concept to conditionally validate using JSR 303 groups. "Conditionally" means that I have some fields which are only relevant if another field has a specific value.
Example: There is an option to select whether to register as a person or as a company. When selecting company, the user has to fill a field containing the name of the company.
Now I thought I use groups for that:
class RegisterForm
{
public interface BasicCheck {}
public interface UserCheck {}
public interface CompanyCheck {}
#NotNull(groups = BasicCheck.class)
private Boolean isCompany
#NotNull(groups = UserCheck.class)
private String firstName;
#NotNull(groups = UserCheck.class)
private String lastName;
#NotNull(groups = CompanyCheck.class)
private String companyName;
// getters / setters ...
}
In my controller, I validate step by step depending on the respective selection:
#Autowired
SmartValidator validator;
public void onRequest(#ModelAttribute("registerForm") RegisterForm registerForm, BindingResult result)
{
validator.validate(registerForm, result, RegisterForm.BasicCheck.class);
if (result.hasErrors()
return;
// basic check successful => we can process fields which are covered by this check
if (registerForm.getIsCompany())
{
validator.validate(registerForm, result, RegisterForm.CompanyCheck.class)
}
else
{
validator.validate(registerForm, result, RegisterForm.UserCheck.class);
}
if (!result.hasErrors())
{
// process registration
}
}
I only want to validate what must be validated. If the user selects "company" fills a field with invalid content and then switches back to "user", the invalid company related content must be ignored by the validator. A solution would be to clear those fields using Javascript, but I also want my forms to work with javascript disabled. This is why I totally like the approach shown above.
But Spring breaks this idea due to data binding. Before validation starts, Spring binds the data to registerForm. It adds error to result if, for instance, types are incompatible (expected int-value, but user filled the form with letters). This is a problem as these errors are shown in the JSP-view by <form:errors /> tags
Now I found a way to prevent Spring from adding those errors to the binding result by implementing a custom BindingErrorProcessor. If a field contains null I know that there was a validation error. In my concept null is not allowed - every field gets annotated with #NotNull plus the respective validation group.
As I am new to Spring and JSR-303 I wonder, whether I am totally on the wrong path. The fact that I have to implement a couple of things on my own makes me uncertain. Is this a clean solution? Is there a better solution for the same problem, as I think this is a common problem?
EDIT
Please see my answer here if you are interested in my solution in detail: https://stackoverflow.com/a/30500985/395879
You are correct that Spring MVC is a bit picky in this regard,and it is a common problem. But there are work-arounds:
Make all your backing fields strings, and do number/date etc conversions and null checks manually.
Use JavaScript to set fields to null when they become irrelevant.
Use JavaScript to validate fields when they are entered. This will fix almost all of your problems.
Good luck!
I know this question is old, but I came upon it looking for an answer for a different situation.
I think for your situation you could use inheritance for the forms and then use two controller methods:
The forms would look like this:
public class RegistrationForm
{
// Common fields go here.
}
public class UserRegistrationForm
extends RegistrationForm
{
#NotNull
private String firstName;
#NotNull
private String lastName;
// getters / setters ...
}
public class CompanyRegistrationForm
extends RegistrationForm
{
#NotNull
private String companyName;
// getters / setters ...
}
The controller methods would look like this:
#RequestMapping(method = RequestMethod.POST, params = "isCompany=false")
public void onRequest(
#ModelAttribute("registerForm") #Valid UserRegistrationForm form,
BindingResult result)
{
if (!result.hasErrors())
{
// process registration
}
}
#RequestMapping(method = RequestMethod.POST, params = "isCompany=true")
public void onRequest(
#ModelAttribute("registerForm") #Valid CompanyRegistrationForm form,
BindingResult result)
{
if (!result.hasErrors())
{
// process registration
}
}
Notice that the #RequestMapping annotations include a params attribute so the value of the isCompany parameter determines which method is called.
Also notice that the #Valid annotation is place on the form parameter.
Finally, no groups are needed in this case.

JAXB binding nested elements

I am using JAXB-impl. I need to be able to map nested elements to class fields as simple types. For example:
<mapping>
<search>
<channel>main-channel</channel>
<url>my-channel-url</url>
</search>
<items>
<item>first</item>
<item>second</item>
<item>third</item>
</items>
</mapping>
Assuming I need to bind "url" tag to a field in the class, this will not work (of course):
class Mapping{
#XmlElement
private String url;
}
#XmlElementWrapper, is for collections only. I have seen some post about using eclipse MOXy, and utilize #XmlPath, but this is not an option. It has to be JAXB-impl.
for reference:
http://wiki.eclipse.org/EclipseLink/Examples/MOXy/XPath#Marshal_to_XML_-_MOXy_Customization_.231_-_Grouping_Element
Is there a way to get this mapping without having to create these extra nested classes ?
With additional class Search, but this class is private nested class, and not used outside of Mapping class.
public API of class Mapping returns url as expected
#XmlAccessorType(XmlAccessType.FIELD)
class Mapping
{
#XmlAccessorType(XmlAccessType.FIELD)
private static class Search
{
private String channel;
private String url;
}
private Search search;
public String getUrl()
{
return search == null ? null : search.url;
}
}

Dirty state for forms considering Collection properties

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)" />

Categories

Resources