VAADIN: Why I can't set a converter to a ComboBox? - combobox

I've made a converter:
public class BooleanToDateConverter implements Converter<Boolean, Date> {
private static final long serialVersionUID = 1L;
#Override
public Date convertToModel(Boolean value, Class<? extends Date> targetType, Locale locale)
throws com.vaadin.data.util.converter.Converter.ConversionException {
if (value == true) {
return new Date();
} else {
return null;
}
}
#Override
public Boolean convertToPresentation(Date value, Class<? extends Boolean> targetType, Locale locale)
throws com.vaadin.data.util.converter.Converter.ConversionException {
if (value == null) {
return false;
} else {
return true;
}
}
#Override
public Class<Date> getModelType() {
return Date.class;
}
#Override
public Class<Boolean> getPresentationType() {
return Boolean.class;
}
}
Then I have a Vaadin ComboBox myComboBox
I try to set my converter to it:
myComboBox.setConverter(new BooleanToDateConverter());
Then I get an error in Eclipse saying:
The method setConverter(Class<?>) in the type AbstractField<Object> is not applicable for the arguments (BooleanToDateConverter)
However, I've seen other converters being used similarly and they don't get errors. Why?

Your code cannot be compiled because there is no setConverter() method available on class ComboBox that fits your custom converter. Let me explain how converters are used on select components and what is the idea behind the specific method signatures you find for setting converters on a ComboBox.
ComboBox provides two overloaded versions of setConverter():
setConverter(Class<?> datamodelType): set a pre-registered converter for the given data model type
setConverter(Converter<Object, ?> converter): set a concrete converter instance
Both of these methods are actually inherited from class AbstractField<T> where T is the data type managed by the field (e.g. Strings for text fields, Date for a DateField, Object for ComboBoxes). A converter is typically used to convert between a presentation type (such as the textual representation of a value on the UI) and its internal model type (such as a date, a monetary value or a custom JavaBean). So, for instance, if you have a Label you can use a StringToDateConverter to correctly display a Date object, which has been set as the value of the Label, in a properly localized way.
How is that with select components such as ComboBox? Here the type T is Object. The data type of a select component actually represents the item ID of the selected item from the underlying container data source. So, if you use a BeanItemContainer as the data source of a ComboBox, the container's item IDs (and hence the selected value of the ComboBox) are the contained JavaBean objects themselves. The concrete type of the item IDs depends on the container implementation used. Therefore, select components are Field components with value type Object. In other words, select components use Object as presentation type.
That is why you can only set a converter instance on a select component whose generic PRESENTATION type is Object. The model type can be chosen freely. And this also explain why you can't set a converter with presentation type Boolean and model type Date on a ComboBox -- ComboBox doesn't use Boolean as presentation type.
I wrote a blog post about Vaadin FieldGroups which also provides a good example for a use case when to use a Converter<Object, ?> on a ComboBox. You can find this article at http://blog.oio.de/2014/04/25/select-nested-javabeans-vaadin-fieldgroup/.
I don't know what you want to achieve with your code, because a converter between a presentation type of Boolean and a model type of Date doesn't make much sense. I can only guess that you want to implementat some sort of decision logic, maybe to decide whether or not a date has been set? In that case you need to take a different approach.
For reference, have a look at the Book of Vaadin on Converters.

Related

Databinding to an ObservableCollection of unknown object type

In my main project I have an ObservableCollection<DataValue>, where DataValue is a type from a 3rd party library, and it looks like this (simplified):
public class DataValue : IFormattable, ICloneable
{
private object m_value;
private TypeInfo m_typeInfo;
public object Value
{
get { return this.m_value; }
}
public TypeInfo TypeInfo
{
get
{
if (this.m_typeInfo == (TypeInfo) null)
return TypeInfo.Unknown;
else
return this.m_typeInfo;
}
}
}
In a different project in my solution I have a view and viewmodel to display the data in this collection, and it works fine when binding to a ListBox (it correctly displays the value, if the DataValue.Value is an integer, a byte[] or whatever), so it somehow figures out of the actual type automatically?
I want the same behavior in a custom control I have created, and if the value is a collection of byte[], it should process the data.
How can I accomplish this? I tried fooling around with a ValueConverter and some reflection and casting, but it seems unnecessary since the native controls seem to figure this out automatically.

Cast Observable Collection from base Class to inherited class

I'm writing a WPF application and I'm currently refactoring some reused code to a base ViewModel Class which my other viewmodels can inherit from.
One Property field on this base class is
public class MessageParentBase
{
MessageParentBase() {}
public string Name;
}
internal ObservableCollection<MessageParentBase> _GridData = new ObservableCollection<MessageParentBase>();
I have a subsequent property declaration
public ObservableCollection<MessageParentBase> GridData
{
get { return _GridData; }
set { _GridData = value; }
}
This works great and everything my issue is that the inerited classes actually use the follow class
Public class ChatMessage : MessageParentBase
{
public string Message;
}
and the view contains a grid of data which is bound to this GridData property but the column which should be bound to the Message field from the ChatMessage class is blank and the fields found in the MessageParentBase class are populated.
So I presume there is an issue with the view not knowing to cast up to the ChatMessage from the MessageParentBase class.
Can I inform the view that the objects will be of the type "ChatMessage".
I did try moving the property declaration up to the inherited viewmodel as
public ObservableCollection<ChatMessage> GridData
{
get { return _GridData; }
set { _GridData = value; }
}
but this gives me the following error:-
Cannot implicitly convert type 'System.Collections.ObjectModel.ObservableCollection' to 'System.Collections.ObjectModel.ObservableCollection'
Do I need to cast at the view level or can I change the viewmodels to implement this better?
Any suggestions would be greatly appreciated.
Emlyn
Change the collection to this:
public ObservableCollection<MessageParentBase> GridData { get; set; }
then add into your constructor
this.GridData = new ObservableCollection<MessageParentBase>();
Since WPF uses reflection to retrieve bound data from the data context it should be able to get the values of the derived classes stored in that collection.
Also when you run your application check the output window with Debug selected, the XAML engine will output any binding errors there.
Your ViewModel should contain a list with the type that your grid will show (in this case, the ChatMessage type). You can still use the inheritance to call common methods, but the binded list must be of the ChatMessage type

How should I pass property values from View to Model in MVVM?

I have three classes,
first:
public class Setting
which has properties:
string ID, string Value; (implements INotifyPropertyChanged on Value)
Second:
public class SettingCollection
which is a:
ObservableCollection<Setting>
Third:
public class SimObject
which has properties:
string ID, SettingsCollection Settings;
I have a View:
SettingsDisplay.xaml
which has dependency property:
SettingsCollection SimObjSettings;
and displays the setting's ID and with its Value in a TextBox inside an ItemsContainer.
My MainWindow ViewModel has a SimObject Jeff; and in the View I have
<local:SettingsDisplay SimObjSettings="{Binding Jeff.Settings}"/>
This all works fine.
I need to know when a Setting Value changes and when that happens I need to know the SimObject ID the Setting belongs to so that I can call a method that sends an event with the Setting ID, Value and the SimObject ID. (e.g. SendEvent(settingID, settingValue, targetObject))
Obviously Setting's and SettingCollection's have no knowledge of what SimObject they belong to.
The way I've tried to do this is in the Setting class call SendEvent in the Value property's Setter.
I'm struggling to find a suitable way to pass the SimObject's ID down the chain to the Setting's class, I also don't think this is a good solution.
What is the best way to acheive what I'm trying to do?
There are lots of ways to do this, but obviously all of them involve someone observing the SettingsCollection and dynamically attaching/detaching PropertyChanged handlers to all items inside it (at least that's what your requirements sound to me).
I would argue that if all you want is unconditional live updetes you should not involve the Views and ViewModels at all in this. Simply write an implementation of something like this:
interface ISimObjLiveUpdateService
{
void StartObserving(SimObject o);
bool IsObserving(SimObject o);
bool StopObserving(SimObject o);
}
The actual implementation would hook up to observe o.SettingsCollection and all items in it. Before you display a SimObject you StartObserving it, and when the view is closed you StopObserving it.
I ended up solving this by giving all Setting's a SimObject property, which was the setting's SimObject owner, and creating an event handler delegate void SettingHandler(string settingID, string settingValue, string targetObj);
in SimObject : public event SettingHandler SettingChanged;
public void RaiseSettingChangedEvent(string settingId, string settingValue, string targetObj)
{
if (SettingChanged != null)
{
SettingChanged(settingId, settingValue, targetObj);
}
}
In Setting on the string Value Setter:
set
{
_value = value;
RaisePropertyChanged("Value");
SimObject.RaiseSettingChangedEvent(ID, Value, SimObject.Settings["UID"].Value);
}
Then in SettingsDisplay I created a SelectedPropertyChangedCallback which adds the ParameterChanged event to the SimObject and also contains the SettingChanged method:
public void SettingChanged(string settingID, string settingValue, string targetObj)
{
Framework.GetBusinessDelegate().SendEvent(settingID, settingValue, targetObj);
}
Don't know how to add syntax highlighting to make this answer clearer.

How to populate options of h:selectOneMenu from database?

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.

Value Object and View Model Property

I am working on a solution that used DDD for architecture. I have a property in my ViewModel which points to a ValueObject, the view model also implements INotifyPropertyChanged interface. The value of the ValueObject will change as a user enters data on the front end. The problem I am running into is the value object is suppose to be immutable. How can I work around this issue? Thank you in advance.
If you can edit something, then there must be a mutable container for the immutable value. Therefore, your viewmodel should act on the mutable container rather than on the immutable value directly.
An integer is an example of such an immutable value object: the Int32 type does not have any members that allow you to change the state of the object. You can only replace an integer, not change it. So a view model for an integer would look like this:
public MutableIntegerViewModel
{
private readonly mutableInteger;
public MutableIntegerViewModel(MutableInteger mutableInteger)
{
this.mutableInteger = mutableInteger;
}
public string DisplayText
{
get
{
return this.mutableInteger.Value.ToString(
CultureInfo.CurrentCulture);
}
set
{
this.mutableInteger.Value =
Int32.Parse(value, CultureInfo.CurrentCulture);
}
}
}
Where MutableInteger is just this:
public class MutableInteger
{
public int Value { get; set; }
}
I've omitted error handling and change notification here, but hopefully you get the idea.
Also note this example is not really different from the typical example of a Customer class with a FirstName and a LastName. Strings are also immutable, so again we have a mutable container for immutable values.

Resources