In my code I have a something like the following:
The wicket form:
public class MyForm extends Form<MyFormModel> {
public MyForm(String id){
super(id, new CompoundPropertyModel<MyFormModel>(new MyFormModel())
//add some labels from the CompoundPropertyModel
}
#Override
protected void onSubmit() {
setResponsePage(new NewPage(getModelObject()));
}
}
The form model:
public class MyFormModel extends LoadableDetachableModel<List<NotSerializableObject>> {
transient List<NotSerializableObject> list;
//Some labels with getters and setters for CompoundPropertyModel use
protected List<NotSerializableObject> load() {
//list = dbstuff.getstuff()
}
}
When the page is loaded with MyForm on it, the MyFormModel uses the load() function and tries to access the db before the form is submitted. I don't understand why because getModelObject() isn't called until the form is submitted and onSubmit() is called.
I understand that the CompoundPropertyModel and LoadableDetachableModel should be split to solve this but why does this not work? Why and from where is the load() function called?
Thanks for any help,
Martin
When the Form is displayed, each of the FormComponent s ask its Model for the value. The first call to getModelObject() (out of my head, have not done much wicket recently) is chained to the load() in the LoadableDetacheableModel.
It looks there is a component added to MyForm (your code snippet omits that part of code). If any of MyForm component access the model then the getModel().getObject() -> load() is called. Remeber that CompoundPropertyModel use also the getObject() on the inner model.
Related
This feels like a pretty standard question and has probably been asked before, but I found it hard to find because it is hard to define in words. So if this is a duplicate, go ahead and redirect me!
I'm using Vaadin to build this web app, but that shouldn't matter to the problem at hand, unless there is an even better way of solving this through some Vaadin magic.
I have three classes:
Table
FilterGenerator
Container
My "design" looks like this:
The Container adds some properties (column headers) to itself in its constructor
The FilterGenerator #Inject the Container (in order to use the Container's getDistinct() method that gets the distinct items from the container - in order to present them nicely in a ComboBox in the filter)
The Table #Inject the FilterGenerator (in order to table.setFilterGenerator(filterGenerator))
The Table #Inject the Container and calls the containers addItems() method to add items to the container
The Table then adds the container as a datasource
What happens?
What I should have now is a table with a ComboBox in the column header, presenting distinct values to filter.
What I get is a table with a ComboBox in the column header, presenting nothing in the ComboBox, because there are no items in the ComboBox.
This is not surprising, because when the FilterGenerator calls the Containers getDistinct() method, it will get an empty map of <Column, items> back, because at the time of #Inject in the FilterGenerator, the Table hasn't called the Containers addItems() method, so the Container will at this moment be empty.
The question
How should I design this application if I want a component (FilterTable) to get something from a second component (Container), when a third component (Table) is #Inject-ing both forementioned components and it is crucial that the second component (Container) already has been initialized when the first component (FilterGenerator) gets something from it?
I could:
In the Table, simply create a new FilterGenerator. This would work, but it isn't very nice. For example, what happens if some other component wants to use the FilterGenerator?
Go back to xml-configuration in order to "manually" create the instances in the correct order. This would probably work (if I remember correctly), but having instance creation depending on the order of the elements in your xml file doesn't sound very good to me.
Use "programmatic injection" by using the ApplicationContext.getBean() in code. This would probably be even worse than the above alternatives?
Does anyone have any good suggestions on how to solve this triangular drama?
Here is the relevant code:
The Table
#Component
#Scope("session")
public class SampleAppMainTable extends FilteringTable {
#Inject
private SampleAppMainTableContainer sampleAppMainTableContainer;
#Inject
private SampleAppService sampleAppService;
#Inject
private SampleAppMainTableFilterGenerator sampleAppMainTableFilterGenerator;
public SampleAppMainTable() {
//...setting up the table
}
#PostConstruct
public void PostConstruct() throws GeneralSecurityException {
addMainTableItems();
setupMainTable();
}
public void setupMainTable() {
this.setFilterGenerator(sampleAppMainTableFilterGenerator);
sampleAppMainTableFilterGenerator.getCustomFilterComponent("Sample Id");
this.setContainerDataSource(sampleAppMainTableContainer);
}
public void addMainTableItems() {
sampleAppMainTableContainer.addItemsToContainer(sampleAppService.getAllSamples());
}
}
The Container
#Component
#Scope("prototype")
public class SampleAppMainTableContainer extends IndexedContainer {
public void addItemsToContainer(List<Sample> samples) {
// adding items to the container...
}
public Map<String, List<String>> getDistinctProperties() {
// extracting distinct items from the table...
}
}
The FilterGenerator
#Component
#Scope("session")
public class SampleAppMainTableFilterGenerator implements FilterGenerator {
#Inject
SampleAppMainTableContainer sampleAppMainTableContainer;
private List<String> aList = null;
#Override
public AbstractField<?> getCustomFilterComponent(Object propertyId) {
Map<String, List<String>> map = new HashMap<String, List<String>>();
map = sampleAppMainTableContainer.getDistinctProperties();
if (propertyId.equals("Sample Id")) {
ComboBox sampleIdCB = new ComboBox();
BeanItemContainer<String> dataList = new BeanItemContainer<String>(String.class);
List<String> aList = map.get("Sample Id");
dataList.addAll(aList);
sampleIdCB.setContainerDataSource(dataList);
sampleIdCB.setImmediate(true);
return sampleIdCB;
}
return null;
}
// other overridden methods needed...
}
I think you problem is that you do processing logic during the the injection phase. You should wait till everything is set up and then do the processing. You can do something like this by moving the processing logic from the constructor to an initialization method and marking this method with #Inject. By definition injection is done last on methods, i.e., at the time the method gets called by the injector, all the fields are injected.
As i understand spring mvc controllers are thread safe by default (like servlets). But I just want to know any private helper methods inside the controllers are thread safe ?
I have two mapping in the controller class eg: /test and test/success. Every time user invokes this url I want to check the user status and activation time in the database using a service method ( two different calls ). So I have decided to create a one private helper method to check the status.
So could anyone know that my private method is thread safe ?
All request are handled by one instance of your controller (singleton because it's a spring managed bean). So you need to make sure to not store any state (in a field) related to one request.
So:
#Controller
#RequestMapping("/foo")
public class Foo {
#Autowired
private Something something;
#RequestMapping("/list")
public String foo() {
something.someMethod();
bar();
return "view"
}
private void bar() {
// something
}
}
is OK, but:
#Controller
#RequestMapping("/foo")
public class Foo {
private User theUser; // problem is ALL request share this field
#RequestMapping("/foo/{userId}")
public String foo(#PathVariable final Integer userId) {
if (theUser.getId().equals(userId)) {
// something
} else {
theUser = ...
}
return "view"
}
}
is not.
NB: not tested (typed just here so it can even hurts your compiler)
I need to validate something about several Wicket input fields of type TextField<BigDecimal> (namely that the sum of percentages is 100). There are one to many such input fields; thing is, I don't know in advance how many.
(simplified example)
private class PercentageValidator extends AbstractFormValidator {
#Override
public FormComponent<?>[] getDependentFormComponents() {
// ...
}
#Override
public void validate(Form<?> form) {
List<TextField<BigDecimal>> fields = // TODO
// the actual validation where the value of every field is needed
}
}
Java code for the ListView:
ListView<?> listView = new ListView<PropertyShare>("shares", shares) {
#Override
protected void populateItem(ListItem<PropertyShare> item) {
// ...
item.add(new TextField<BigDecimal>("share", ... model ...));
}
};
HTML:
<tr wicket:id="shares">
<td> ... </td>
<td>
<input wicket:id="share" type="text" size="4"> %
</td>
</tr>
I tried keeping every TextField in a collection on the Page, but this approach fails as the populateItem() method of the enclosing ListView gets called not only the the Page is first created, so duplicate fields get added to the collection. (I couldn't figure out an easy way to keep it duplicate-free.)
The fact that ListView is used also seems to somewhat complicate finding the fields from the form object in the validate() method. I suppose I need to get the ListView with form.get("shares") and iterate through its children?
What's the "right way" to access any number of fields enclosed by a repeater such as ListView?
An alternative approach would be to subclass TextField and then use a Visitor to pick out all the descendant components of your subclass.
This way you can avoid unchecked casting and you don't have to rely on the ids, which isn't a very robust approach.
Edit: in practice, it would look something like this:
The subclass:
private static class ShareField extends TextField<BigDecimal> {
// ...
}
Helper method that finds all ShareFields from the form:
private List<ShareField> findShareFields(Form form) {
final List<ShareField> fields = Lists.newArrayList();
form.visitChildren(ShareField.class, new IVisitor<ShareField>() {
#Override
public Object component(ShareField component) {
fields.add(component);
return CONTINUE_TRAVERSAL;
}
});
return fields;
}
Right, while writing the question, it dawned on me that simply looping through the children of form.get("shares") and getting the field with id "share" would probably work.
It indeed does. Here's a helper method that finds the "share" fields:
#SuppressWarnings("unchecked")
private List<TextField<BigDecimal>> findFields(Form form) {
List<TextField<BigDecimal>> fields = Lists.newArrayList();
MarkupContainer container = (MarkupContainer) form.get("shares");
for (Iterator<? extends Component> it = container.iterator(); it.hasNext();) {
MarkupContainer c = (MarkupContainer) it.next();
fields.add((TextField<BigDecimal>) c.get("share"));
}
return fields;
}
However, there are three somewhat ugly casts in the above method, and one of those (Component -> TextField<BigDecimal>) produces an "unchecked cast" warning.
If you can clean up this solution, or know of better approaches, feel free to comment or post other answers!
As far I see you did not set the reuse items property on the list view; from the java doc:
If true re-rendering the list view is more efficient if the windows doesn't get changed at all or if it gets scrolled (compared to paging). But if you modify the listView model object, than you must manually call listView.removeAll() in order to rebuild the ListItems. If you nest a ListView in a Form, ALLWAYS set this property to true, as otherwise validation will not work properly.
However you also can iterate over the children of the listview with a Visitor. Wicket always keeps track of the components you added of the view.
When applying this pattern Delegation Event Model, is it correct to put ALL the code in the fire... methods and pass the parameters from the public method?
Like this
public void addBananas(Banana banana) {
fireBananaAdded(banana);
}
private void fireBananaAdded(Banana banana) {
//Create event etc and add banana to list here
}
Or should I have the add to list part in this example in the addBananas method instead? Because if I do it this way I will not have the opportunity to "attach" the banana object to the event-object which will be passed to the listeners, right?
I would put as much logic in addBanana() that is related to actually adding the Banana as I can.
When I'm done with addBanana(), I would call fireBananaAdded() which would generate the appropriate BananaAddedEvent and send it to the BananaAddedListeners (or just BananaListeners, which ever you have.)
To put the ADD logic in the FIRE method is simply, well, BANANAS!
public void addBananas(Banana banana) {
if(BananaHolder.hasRoom()) {
BananaHolder.add(banana);
fireBananaAdded(banana);
}
}
private void fireBananaAdded(Banana banana) {
BananaAddedEvent event = new BananaAddedEvent(banana);
for(BananaListener listener : listenersByType(Banana.class)) {
listener.bananaAdded(event);
}
}
I'm struggling with a very basic Wicket issue. I'm trying to query a backend database, but can't get the results to display. Below is the code I'm using. currentQuery and currentResult is correctly updated after submission, but my SearchResults class is never rerendered with the new data in currentResults. I suppose that the results class just doesn't notice that the model has in fact been updated. I've been experimenting with modelChanged, but can't get it to work. I'm a bit new to Wicket, so I'm probably doing something fundamental completely wrong. Any help is much appreciated!
public class SearchPage extends WebPage {
Query currentQuery = new Query();
Result currentResult = new Result();
public SearchPage() {
add(new SearchForm("searchForm", new CompoundPropertyModel<Query>(currentQuery)));
add(new SearchResults("searchResults", new PropertyModel<List<Hit>>(currentResult, "hits")));
}
public void doSearch(Query Query) {
currentResult = getResults(query);
}
public class SearchForm extends Form<Query> {
public SearchForm(String id, CompoundPropertyModel<Query> model) {
super(id, model);
add(new TextField<String>("query"));
}
protected void onSubmit() {
super.onSubmit();
doSearch(currentQuery);
}
}
public class SearchResults extends WebMarkupContainer {
public SearchResults(String id, PropertyModel<List<Hit>> model) {
super(id, model);
add(new ListView<Hit>("hit", model) {
protected void populateItem(ListItem<Hit> item) {
item.add(new Label("column", item.getModelObject().getColumnValue("column")));
}
});
}
}
}
PropertyModel uses reflection to look up the named property on a given target object instance. When you constructed the PropertyModel, you passed it a specific instance of Result, i.e. the new Result() from SearchPage's constructor. The PropertyModel will continue to hold a reference to that same Result instance from render to render of this page, serializing the Result at the end and then deserializing the Result at the start of each new request cycle (page view). The fact that you later change the page's currentResult variable to reference a different Result instance does not affect which Result instance the PropertyModel uses to look up its model value. Your PropertyModel does not care what currentResult later refers to.
There are two possible solutions that I can think of off the top of my head.
Have the PropertyModel read hits from the actual current value of the Page's currentResult variable:
new PropertyModel<List<Hit>>(SearchPage.this, "currentResult.hits")
Use a LoadableDetachableModel to load hits once per request cycle/page view:
new LoadableDetachableModel<List<Hit>>()
{
protected Object load()
{
return getResults(currentQuery);
}
}
Note that a LoadableDetachableModel has to be detached at the end of the request cycle or it will never again call getObject() to recalculate the List<Hit>. That said, since your code shows you'd be using it as the default model of the SearchResults component, the SearchResults component would detach the model for you at the end of the request cycle automatically.
I got it working. This seems to be the offending row:
add(new SearchResults("searchResults", new PropertyModel<List<Hit>>(currentResult, "hits")));
The type of the PropertyModel, i.e. List<Hit>, must have been making the model static. So the only data SearchResults ever saw was the initial object, which was empty.
I changed the line to the below, and updated SearchResult accordingly.
add(new SearchResults("searchResults", new Model<Result>(currentResult, "hits")));
If anyone can explain this further, or feel that I'm incorrect, please comment! In any case, I'm marking my own answer as correct as this solved the problem.