How do i get a panel to use the some modelObject as the parent page?
I have a form that uses an OwnedAccount as its model, and in the form i have a custom panel which has a refreshingview containing a list of financeAccount. The problem is that changes to the financeaccounts are not being changed in the modelobject of the form.
Some code, i removed a lot of code where there is 3 dots "..."
#Entity
#Table(name = "ownedaccount")
public class OwnedAccount implements Serializable {
...
//used for multiple currencies
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "ownedAccount")
private List<FinanceAccount> financeAccounts = new ArrayList<FinanceAccount>();
...
}
public class AssetsPage extends LoggedInPage {
...
// bookmarkable constructor
public AssetsPage(PageParameters parameters) {
super(parameters);
init();
}
private void init() {
final OwnedAccount ownedAccount = getCurrentSelections().getSelectedOwnedAccount();
add(new FeedbackPanel("feedback"));
entityEdit = new OwnedAccountForm("entityEdit", ownedAccount);
add(entityEdit);
}
#Override
protected void selectionsChanged() {
OwnedAccount selectedOwnedAccount = getCurrentSelections().getSelectedOwnedAccount();
CompoundPropertyModel<OwnedAccount> model = new CompoundPropertyModel<OwnedAccount>(selectedOwnedAccount);
entityEdit.setModel(model);
}
...
class OwnedAccountForm extends BaseCreateEditForm<OwnedAccount, Void> {
...
public OwnedAccountForm(String s, OwnedAccount entity) {
super(s, entity, null);
assetType = entity.getAssetType();
}
#Override
protected void initComponents(Void initParams) {
...
multipleCurrenciesPanel = new MultipleCurrenciesPanel("multipleCurrenciesPanel", ownedAccountService, getCurrentSelections());
add(multipleCurrenciesPanel);
...
}
...
}
public class MultipleCurrenciesPanel extends Panel {
OwnedAccountService ownedAccountService;
CurrentSelections currentSelections;
public MultipleCurrenciesPanel(String id, OwnedAccountService ownedAccountService, CurrentSelections currentSelections) {
super(id);
this.ownedAccountService = ownedAccountService;
this.currentSelections = currentSelections;
init();
}
private void init() {
DepositoryLabel currencyLabel = new DepositoryLabel("currency", new ResourceModel("currency"));
add(currencyLabel);
DepositoryLabel accountForSecuritasLabel = new DepositoryLabel("account.for.securitas", new ResourceModel("account.for.securitas"));
add(accountForSecuritasLabel);
DepositoryLabel accountForCashLabel = new DepositoryLabel("account.for.cash", new ResourceModel("account.for.cash"));
add(accountForCashLabel);
DepositoryLabel buttonDeleteLabel = new DepositoryLabel("button.delete", new ResourceModel("button.delete"));
add(buttonDeleteLabel);
CurrenciesView currenciesView = new CurrenciesView("financeAccounts", ownedAccountService, currentSelections, this);
add(currenciesView);
setOutputMarkupId(true);
}
}
Updated 25/9 - 15:15
public class CurrenciesView extends RefreshingView<FinanceAccount> {
private OwnedAccountService ownedAccountService;
private CurrentSelections currentSelections;
private WebMarkupContainer multipleCurrenciesForDepot;
public CurrenciesView(String id, OwnedAccountService ownedAccountService, CurrentSelections currentSelections, WebMarkupContainer multipleCurrenciesForDepot) {
super(id);
this.ownedAccountService = ownedAccountService;
this.currentSelections = currentSelections;
this.multipleCurrenciesForDepot = multipleCurrenciesForDepot;
}
#Override
protected Iterator getItemModels() {
List<FinanceAccount> financeAccounts = ownedAccountService.getFinanceAccounts(currentSelections.getSelectedOwnedAccount());
return new ModelIteratorAdapter<FinanceAccount>(financeAccounts.iterator()) {
#Override
protected IModel<FinanceAccount> model(FinanceAccount object) {
return new CompoundPropertyModel<FinanceAccount>((object));
}
};
}
#Override
protected void populateItem(Item<FinanceAccount> item) {
final FinanceAccount financeAccount = item.getModelObject();
item.add(new EnumDropDownChoice<MoneyCurrency>("forCurrency"));
item.add(new TextField("accountNumber"));
item.add(new OwnedAccountDropDownChoice("accountForCash", currentSelections.getSelectedLegalEntity().getOwnedBankAccounts()));
item.add(new AjaxButton("deleteFinanceAccount") {
#Override
protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
//TODO Delete finance account
ownedAccountService.deleteFinanceAccount(currentSelections.getSelectedOwnedAccount(), financeAccount);
target.add(multipleCurrenciesForDepot);
}
#Override
protected void onError(AjaxRequestTarget target, Form<?> form) {
//TODO create error message
}
});
}
}
CurrentSelections needs to model if its going to be used and mutiplated by two different wicket page/components.
For example theres a parent page, which has a constructor, a new String object and a Panel that uses the String object as a parameter,
public class ParentPage extends WebPage {
public ParentPage() {
String string = new String("Dave");
add(new Panel("childPanel", string));
string = new String("Brian");
}
}
If the string object is updated after the panel has been added, then that updated string isn't what the panel has. What the panel has is "Dave" when thought the ParentPage now has the string as "Brian".
But if we made a model and made it use the string object then when we change the string the childPanel will get the update.
public class ParentPage extends WebPage {
public ParentPage() {
String string = new String("Dave");
IModel model = new Model(string);
add(new Panel("childPanel", model));
model.setObject(new String("Brian"));
}
}
Its a very simple example but i hope it helps.
I saved the model reference passed in the constructor to the panel, so far its working ok.
BankAccountPanel bankAccountPanel = new BankAccountPanel("bankAccountPanel", getModel());
add(bankAccountPanel);
...
public class BankAccountPanel extends Panel {
IModel<OwnedAccount> iModel;
public BankAccountPanel(String id, IModel<OwnedAccount> model) {
super(id, model);
this.iModel = model;
init();
}
Related
Wicket use of models can be cumbersome. For a stateful page to properly render an object, you need to use lots of boiler-plate code, overriding classes to properly get the visibility status, etc... A simple example:
private IModel<FooBar> fooBarModel;
public MyPage() {
Label lbl1 = new Label("field1",
new PropertyModel<>(fooBarModel, "field1")) {
#Override public boolean isVisible() {
return fooBarModel.getObject().someCondition();
} }
add(lbl1);
/// Etc... same for dozen of other fields
}
I'm often using a trick using a ListView to help. Same example:
public MyPage() {
add(new ListView<FooBar>("content",
new SingleListModel<FooBar>(fooBarModel)) {
#Override protected void populateItem(...) {
FooBar fooBar = item.getModelObject();
// Code here gets simpler:
Label lbl1 = new Label("field1", fooBar.getField1());
lbl1.setVisible(fooBar.someCondition());
item.add(lbl1);
// Etc...
}
});
}
With a simple utility class SingleListModel, that transform a IModel<T> to a ListModel<T>, having 1 or 0 elements, depending whether T is null or not:
public class SingleListModel<T>
extends LoadableDetachableModel<List<T>> {
private IModel<T> tModel;
public SingleListModel(IModel<T> tModel) {
this.tModel = tModel;
}
#Override
protected List<T> load() {
List<T> ret = new ArrayList<>(1);
T t = tModel.getObject();
if (t != null)
ret.add(tModel.getObject());
return ret;
}
}
The nice side-effect of this is that the whole "content" element in the markup is hidden if fooBarModel returns null; no special treatment needed.
But all this smells like a hack to me, as I use ListView in a somehow "unnatural" fashion.
Is there a cleaner way to get the same result? A standard wicket framework?
You should use Behavior instead to avoid such duplications.
public class MyBehavior extends Behavior {
private final MyModel model;
public MyBehavior(MyModel model) {this.model = model;}
#Override public void onConfigure(Component c) {
if (model.someCondition()) {
component.setVisible(false);
}
}
}
Usage:
MyBehavior b = new MyBehavior(modelInstance);
component1.add(b);
component2.add(b);
// dozen more
Label lbl1 = new Label("field1",
new PropertyModel<>(fooBarModel, "field1")) {
#Override public boolean isVisible() {
return fooBarModel.getObject().someCondition();
} }
add(lbl1);
with little refactoring it can be converted into
add(new FLabel("id","text")
.setVisibilityFunction(()->model.getObject().isVisible()))
);
the FLabel class:
public class FLabel extends Label implements IComponentWithVisibilityFunction<FLabel> {
private SerializableBooleanSupplier visibilityFunction;
public FLabel(String id) {
super(id);
}
public FLabel(String id, Serializable label) {
super(id, label);
}
public FLabel(String id, IModel<?> model) {
super(id, model);
}
#Override
public FLabel setVisibilityFunction(SerializableBooleanSupplier visibilityFunction) {
this.visibilityFunction = visibilityFunction;
return this;
}
#Override
protected void onConfigure() {
if (visibilityFunction != null) {
setVisible(visibilityFunction.getAsBoolean());
}
}
}
public interface IComponentWithVisibilityFunction<T> {
T setVisibilityFunction(SerializableBooleanSupplier visibilityFunction);
}
Moreover you can put supplier into constructor:
add(new FLabel("id","text", ()->model.getObject().isVisible()));
I have an object with bound members in JavaFX andI want to reset the members to initial state at a certain moment.
So my first option is to reset them one by one, which is to much code, but its works fine.
The other option is to re-instantiate i.e. myModel = new Model(), but in the case the object reference changes and I lose my binding.
Is there any alternative way to accomplish this?
EDIT
Model
public class Service extends BaseModel {
private StringProperty serviceType = new SimpleStringProperty();
private ObjectProperty<BigDecimal> buyingPrice = new SimpleObjectProperty<>(new BigDecimal(0));
private ObjectProperty<BigDecimal> sellingPrice = new SimpleObjectProperty<>(new BigDecimal(0));
public Service(){
}
public String getServiceType() {
return bundle.getString("service");
}
public StringProperty serviceTypeProperty() {
return serviceType;
}
public void setServiceType(String serviceType) {
this.serviceType.set(serviceType);
}
public BigDecimal getBuyingPrice() {
return buyingPrice.get();
}
public ObjectProperty<BigDecimal> buyingPriceProperty() {
return buyingPrice;
}
public void setBuyingPrice(BigDecimal buyingPrice) {
this.buyingPrice.set(buyingPrice);
}
public BigDecimal getSellingPrice() {
return sellingPrice.get();
}
public ObjectProperty<BigDecimal> sellingPriceProperty() {
return sellingPrice;
}
public void setSellingPrice(BigDecimal sellingPrice) {
this.sellingPrice.set(sellingPrice);
}
}
The bindings:
public class ServiceForm extends HBox implements Initializable {
private Service service = new Service();
#FXML
private TextField serviceDescriptionField;
#FXML
private TextField servicePriceField;
#FXML
private Button addButton;
#Override
public void initialize(URL url, ResourceBundle resourceBundle) {
serviceDescriptionField.textProperty().bindBidirectional(service.descriptionProperty());
servicePriceField.textProperty().bindBidirectional(service.sellingPriceProperty(), new BigDecimalStringConverter());
}
public Service getService() {
return service;
}
public void setService(Service service) {
this.service = service;
}
public Button getAddButton() {
return addButton;
}
public void setAddButton(Button addButton) {
this.addButton = addButton;
}
}
Doing this does not clear my fields and I believe it breaks my binding:
serviceForm.setService(new Service());
You can try to have the Service as a property:
private ObjectProperty<Service> service = new SimpleObjectProperty<>(new Service());
Extract the bindings into a private method which is able to unbind old bindings and create new ones:
private void createBindings(Service oldService) {
// Unbind if there is an older service
if (oldService != null) {
servicePriceField.textProperty().unbindBidirectional(oldService.sellingPriceProperty());
serviceDescriptionField.textProperty().unbindBidirectional(oldService.descriptionProperty());
}
servicePriceField.textProperty().bindBidirectional(service.get().sellingPriceProperty(), new BigDecimalStringConverter());
serviceDescriptionField.textProperty().bindBidirectional(service.descriptionProperty());
}
and then in the initialize method:
createBindings(null);
service.addListener((obs, oldval, newval) -> createBindings(oldval));
This way if you call service.set(new Service()), the bindings will be created for the new Service and will be removed for the previous one.
Now when you add new bindings, you can bundle them in the createBindings method while adding the unbinging logic to the same place. Note: this could be further generalized.
I am looking for a design pattern / solution for the following problem, that is related to the Observer pattern, I have already studied.
In my code I have a MyModel class. It has many properties.
public class MyModel {
private List<Flower> flowers = new ArrayList<Flower>();
private List<Toys> toys = new ArrayList<Toys>();
private List<Coffee> coffees = new ArrayList<Coffee>();
private List<IBusinessEntityListener> listener =
new ArrayList<IBusinessEntityListener>();
public void addChangeListener(IBusinessEntityListener newListener) {
listener.add(newListener);
}
}
So classes that implement IBusinessEntityListener can register to MyModel class.
Then I have 10+ listeners that are interested only in some properties of MyModel. They all implement IBusinessEntityListener. But how can I specify (for example with Java Generics?) that some listener are only interested in Flowers, some only about Toys, etc.?
So How to design such class structure that would support listening to certain properties?
All listeners would anyway implement 3 methods for the operations add, update and delete.
How about an application of the Extrinsic Visitor pattern?
Define an interface for properties:
public interface ListenableProperty {
// Degenerate interface for listeners
public interface Listener {}
public void acceptUpdate(Listener listener);
}
Then implement a class for each property, and a Listener interface for each property, and use like so from your model:
public class MyModel {
public static class FlowersProperty implements ListenableProperty {
public interface Listener extends ListenableProperty.Listener {
public void update(FlowersProperty p);
}
#Override
public void acceptUpdate(ListenableProperty.Listener listener) {
if (listener instanceof FlowersProperty.Listener) {
Listener myListenerType = (Listener)listener;
myListenerType.update(this);
}
}
// some property accessors here
}
public static class ToysProperty implements ListenableProperty {
public interface Listener extends ListenableProperty.Listener {
public void update(ToysProperty p);
}
#Override
public void acceptUpdate(ListenableProperty.Listener listener) {
if (listener instanceof ToysProperty.Listener) {
Listener myListenerType = (Listener)listener;
myListenerType.update(this);
}
}
// some property accessors here
}
private FlowersProperty flowers = new FlowersProperty();
private ToysProperty toys = new ToysProperty();
private List<ListenableProperty> properties = new ArrayList();
// CopyOnWrite so that listeners can remove themselves during update if desired
private List<ListenableProperty.Listener> listeners =
new CopyOnWriteArrayList<>();
// Convenience interface for implementors that want all properties
public interface AllPropertiesListener extends
FlowersProperty.Listener,
ToysProperty.Listener
{}
public MyModel() {
properties.add(flowers);
properties.add(toys);
}
public void addListener(ListenableProperty.Listener l) {
if (!listeners.contains(l)) {
listeners.add(l);
}
}
private void updateAll() {
for (ListenableProperty p : properties) {
for (ListenableProperty.Listener l : listeners) {
p.acceptUpdate(l);
}
}
}
private void updateToys() {
for (ListenableProperty.Listener l : listeners) {
toys.acceptUpdate(l);
}
}
private void updateFlowers() {
for (ListenableProperty.Listener l : listeners) {
flowers.acceptUpdate(l);
}
}
}
Listeners can then implement as many or as few of the listener interfaces as they please, or all of them via the convenience interface MyModel.AllPropertiesListener
You could also move the update routines for individual properties to the properties themselves.
for any type of Listeners have a class :
FlowerListerner implemts IBusinessEntityListener;
ToyListerner implemts IBusinessEntityListener;
and a listener list:
public class MyModel {
private List<Flower> flowers = new ArrayList<Flower>();
private List<Toys> toys = new ArrayList<Toys>();
private List<IBusinessEntityListener> flowerListeners =
new ArrayList<IBusinessEntityListener>();
private List<IBusinessEntityListener> toyListeners =
new ArrayList<IBusinessEntityListener>();
public void addListener(IBusinessEntityListener newListener) {
if(newListener instance of FlowerListener)
flowerListeners.add(newListener);
else if (newListener instance of ToyListener)
} toyListeners.add(newListener);
updateFlowerListeners() { ....}
updateToyListeners() { ....}
}
and any changes to each property reflect to related listeners.
UPDATE
another solution is that u have a list of interest in Listener Object:
Class Listener {
private List<Class> interests;
public Listener(List<Class> interests) {
this.interests = interests;
}
public boolean isInterested(Class clazz) {
return list.contains(clazz);
}
public void update() { ... }
}
an in model :
public class MyModel {
private List<Flower> flowers = new ArrayList<Flower>();
private List<Toys> toys = new ArrayList<Toys>();
private List<Listener> listeners =
new ArrayList<Listener>();
public void addListener(Listener newListener) {
listeners.add(newListener);
}
updateFlowerListeners() {
for(Listener l : listerners) {
if(l.isInterested(Flower.class)
l.update();
}
updateToyListeners() { ... }
}
I have a bean, which I attached to the form using Model and it works fine. Also I have a field in bean like Map<String, javax.mail.Address>. How can I bind this field with form by model considering that every map entry should be like Label: TextField?
Thanks in advance.
Maybe something like:
ListView<String> textAreasListView = new ListView<String>("someid", bean.map.keySet()) {
#Override
protected void populateItem(final ListItem<String> itemLang) {
itemLang.add(new Label("label", itemLang.getModelObject()));
Model<String> textModel = new Model<String>() {
#Override
public String getObject() {
return bean.map.get(itemLang.getModelObject()).toString;
}
#Override
public void setObject(String object) {
bean.map.put(itemLang.getModelObject(), new Address(object));
}
};
itemLang.add(new TextField<String>("email", textModel));
}
};
and add this to your form.
A custom converter for Address & String would be an additional improvement.
I think, you could bind it in such a way:
TextField<Address> textField = new TextField<Address>("address",
beanModel.<Address> bind("addressMap[addressKey]"), Address.class) {
#SuppressWarnings("unchecked")
#Override
public <C> IConverter<C> getConverter(Class<C> type) {
if (Address.class.isAssignableFrom(type)) {
return (IConverter<C>) new AddressConverter();
} else {
return super.getConverter(type);
}
}
};
form.add(textField);
Here "addressMap" is name of the map field, "addressKey" is the key of the address value in the map.
The listing for AddressConverter class:
public class AddressConverter implements IConverter<Address> {
public Address convertToObject(String string, Locale locale) {
try {
return new InternetAddress(string);
} catch (AddressException e) {
return null;
}
}
public String convertToString(Address address, Locale locale) {
return address.toString();
}
}
Of course, converter for Address class may be attached globally with the ConverterLocator. Just add the following method to your Application class:
#Override
protected IConverterLocator newConverterLocator() {
ConverterLocator locator = new ConverterLocator();
locator.set(Address.class, new AddressConverter());
return locator;
}
And then you don't need to override getConverter() method on the all text fields.
Main.java
package com.example.decorator;
public class Main {
public static void main(String[] args) {
Response response = new Response();
View head = new View("<title>Hello, world!</title>");
View body = new View("<h1>Hello, world!</h1>");
response.setContent(new HtmlLayout(head, body));
response.render();
}
}
Response.java
package com.example.decorator;
public class Response {
private Response content;
public Response () {}
public <T extends Response> void setContent(T content) {
this.content = content;
}
public void render() {
this.content.render();
};
}
View.java
package com.example.decorator;
public class View extends Response {
private String content;
public View(String content) {
this.content = content;
}
public void setContent(String content) {
this.content = content;
}
public String getContent() {
return this.content;
}
public void render() {
System.out.println(this.content);
}
}
Layout.java
package com.example.decorator;
public class Layout extends Response {
private Response view;
public <T extends Response> Layout(T view) {
this.view = view;
}
public void render() {
this.view.render();
}
}
HtmlLayout.java
package com.example.decorator;
public class HtmlLayout extends Response {
private Response head;
private Response body;
public <T extends Response> HtmlLayout(T head, T body) {
this.head = head;
this.body = body;
}
public void render() {
System.out.println("<!doctype html>");
System.out.println("<html>");
System.out.println("<head>");
this.head.render();
System.out.println("</head>");
System.out.println("<body>");
this.body.render();
System.out.println("</body>");
System.out.println("</html>");
}
}
Decorator pattern is used when you want an object of type(interface) A to do more than it does currently. An example would be: Web page(logical screen) that does fit your physical screen does not need scroll bar. however, if the page(logical screen) does not fit the physical screen, you have to decorate it with scroll bar.
In GOF words: The intent of Decorator is to attach additional responsibilities to an object dynamically.
In code that would look like:
interface LogicalScreen {
void render(String physical );
}
An implementation:
class SimpleScreen implements LogicalScreen {
public void render(String physical) {
// render itself
}
}
Implementation of decorator:
class ScreenWithScrollbar implements LogicalScreen {
private final LogicalScreen decoratd;
public ScreenWithScrollbar(LogicalScreen decorated) {
this.decoratd = decorated;
}
public void render(String physical) {
// render scroll bar
// ...
// render the decorated
decoratd.render(physical);
// eventually do some more stuff
}
public doScroll() {}
}
How is wired:
public class WhatIsDecorator {
public static void main(String[] args) {
LogicalScreen l1 = new SimpleScreen();
LogicalScreen ds = new ScreenWithScrollbar(l1);
ds.render("MyMonitor");
}
}
You can chain like this as many as you need. Decorator2(Decorator1(Simple)) ...
As far as I've looked over for short:
Change your Response to an interface
Your View.java does not match a decorator pattern really, so remove the extends of Response e.g.
Best regards