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...
}