Register commandHandler with EventList in a Nattable with Filters - nattable

As a suggestion from question Nattable add row command I tried to register a command handler with an EventList.
But since the Eventlist is wrapped by a FilterList which acts as BodyDataProvider I could not create a DataLayer based in EventList from which I could register my CommmandHandler.
The starting point is the previous question to define a BodyLayerStack with the following:
class BodyLayerStack extends AbstractLayerTransform {
//AncolabMaterial is the model to show at the natTable
private final EventList<AncolabMaterial> filterList;
private final IDataProvider bodyDataProvider;
private final SelectionLayer selectionLayer;
public BodyLayerStack(List<AncolabMaterial> input_values_list, IColumnPropertyAccessor<AncolabMaterial> columnPropertyAccessor) {
EventList<AncolabMaterial> eventList = GlazedLists.eventList(input_values_list);
TransformedList<?, ?> rowObjectsGlazedList = GlazedLists.threadSafeList(eventList);
SortedList<?> sortedList = new SortedList<>(rowObjectsGlazedList, null);
this.filterList = new FilterList<AncolabMaterial>((EventList<AncolabMaterial>) sortedList);
this.bodyDataProvider = new ListDataProvider<AncolabMaterial>(filterList, columnPropertyAccessor);
DataLayer bodyDataLayer = new DataLayer(getBodyDataProvider());
//Other layers stacked
GlazedListsEventLayer<?> glazedListsEventLayer = new GlazedListsEventLayer<AncolabMaterial>(bodyDataLayer, this.filterList);
this.selectionLayer = new SelectionLayer(glazedListsEventLayer, false);
//...
}
}
I have tried the following:
DataLayer dataLayer = new DataLayer(
new ListDataProvider<AncolabMaterial>(eventList, columnPropertyAccessor));
But since DataLayer wraps the IDataProvider, and serves as the data source for all other layers, If I sets the EvenList as the IDataProvider of the DataLayer then filterlList is not working properly.
i.e. this.filterList is not the base of the bodyDataProvider.
I have not find at nattable_examples -> tutorial examples -> GlazedLists -> Filter any other BodyLayerStack configuration different than the above.

There seems to be a big misunderstanding with regards to the list instances. If you want to use the filter functionality the shown BodyLayerStack is correct. You have to use the FilterList in the IDataProvider. There is absolutely now reason to change that!
For the command handler you need to use the base EventList instance. Of course that does not work if you use the list that you get from the IDataProvider. You need to provide the access to the EventList in another way. From the snippets you show in this and in the other related post, I don't see a reason why you access the underlying list via IDataProvider, but as you already noticed, that does not work. You need to change your code structure.

Related

How to Set View Model Class Programmatically when Creating Components

I am experimenting with ZK framework and look for ways to load a zul-defined view programmatically via a utility class that uses Executions to load a view definition programmatically.
I do however fail to find a way to set the view model class programmatically, which would be very convenient and would allow some very simple way of reusing a view with different view model classes.
I.e. the code to load the view looks like this:
public static Component loadComponent(Class<?> modelClz, String zulFile, Component parent, Map<String,Object> params) {
Execution exec = Executions.getCurrent();
PageDefinition page = exec.getPageDefinitionDirectly(
new InputStreamReader(modelClz.getResourceAsStream(zulFile)),
null
);
return exec.createComponents(
page,
// no (previous parent)
parent,
params
);
}
I thought about "forcing" the view model by setting annotations programmatically on the top component info from the page definition, like so:
public static Component loadComponent(Class<?> modelClz, String zulFile, Component parent, Map<String,Object> params) {
Execution exec = Executions.getCurrent();
PageDefinition page = exec.getPageDefinitionDirectly(
new InputStreamReader(modelClz.getResourceAsStream(zulFile)),
null
);
if (!page.getChildren().isEmpty()) {
ComponentInfo top = (ComponentInfo) page.getChildren().get(0);
AnnotationMap annotationMap = top.getAnnotationMap();
String viewModel = "viewModel";
if (annotationMap==null || !annotationMap.getAnnotatedProperties().contains(viewModel)) {
// no view model set on top declaration,
// force ours
Map<String,String[]> id = new HashMap<>();
id.put(null, new String[]{"vm"});
top.addAnnotation("viewModel","id",id, null);
Map<String,String[]> init = new HashMap<>();
init.put(null, new String[]{String.format("%s", modelClz.getName())});
top.addAnnotation("viewModel","init",init, null);
top.enableBindingAnnotation();
}
}
return exec.createComponents(
page,
// no (previous parent)
parent,
params
);
}
This did not work however. Maybe it was too late in the process. Or there is some really simple way of doing this but I missed it. Or maybe I should "apply" some BindComposer, but I am not sure how to do that.
Any helpful idea would be great!
Just to make sure I've understood:
You have some zul fragment (such as a reusable UI structure)
This fragment uses the MVVM pattern, but you want to choose a viewModel object when you are instantiating that fragment, instead of declaring a class name in the zul viewModel="#id()#init()" attribute
You want to initialize that fragment from a java class that has access to the UI (using Execution#createComponents to load the fragment and attach them to the page)
Does that sound correct?
If that's the case, you can make this way simpler
The attribute can be written as viewModel="#id('yourVmId')#init(aReferenceToAnAlreadyInstantiatedObject)".
Important note here: Notice that I have NOT put quotes around the object in the #init declaration. I'm passing an actual object, not a string containing a reference to a class to be instantiated.
When you invoke execution.createComponents() you may pass a map<String, Object> of arguments to the created page. You can then use the name of the relevant passed object when you create bind the VM.
have a look at this fiddle (bit rough, but it should make sense): https://zkfiddle.org/sample/2jij246/4-Passing-an-object-through-createComponents-as-VM#source-2
HashMap<String, Object> args = new HashMap<String, Object>();
args.put("passedViewModel", new GenericVmClass("some value in the passed VM here"));
Executions.createComponents("./fragment.zul", e.getTarget().getPage(),null, args);
FYI if you are using ZK shadow-elements, you can also pass that object to the fragment from an apply with a source in pure MVVM pattern.
The <apply> shadow element for example can pass objects to the created content with a variable name, and you can use that variable name when initializing the VM.
Regarding BindComposer:
You need to instantiate BindComposer up to ZK 7.X
In ZK 8.X and above, BindComposer will be instantiated automatically when you use the viewModel="..." attribute on a ZK component.

JBehave doesn't recognize steps, but does load the steps file

With a different test runner (the annotation based one) the steps get picked up and run. The annotation based approach doesn't seem to support a steps factory though, so I swapped models. Now, it will load the steps class (some visible things happen when the constructor is called) but it won't recognize any of the steps inside it. Any ideas? Here's my embedder class:
public class LoginAcceptanceFull extends JUnitStories {
private final CrossReference xref = new CrossReference();
public LoginAcceptanceFull() {
configuredEmbedder().embedderControls().doGenerateViewAfterStories(true)
.doIgnoreFailureInStories(true).doIgnoreFailureInView(true).useThreads(2)
.useStoryTimeoutInSecs(60);
}
#Override
public Configuration configuration() {
Class<? extends Embeddable> embeddableClass = this.getClass();
Properties viewResources = new Properties();
viewResources.put("decorateNonHtml", "true");
// Start from default ParameterConverters instance
ParameterConverters parameterConverters = new ParameterConverters();
// factory to allow parameter conversion and loading from external
// resources (used by StoryParser too)
ExamplesTableFactory examplesTableFactory = new ExamplesTableFactory(new LocalizedKeywords(),
new LoadFromClasspath(embeddableClass), parameterConverters);
// add custom converters
parameterConverters.addConverters(new DateConverter(new SimpleDateFormat("yyyy-MM-dd")),
new ExamplesTableConverter(examplesTableFactory));
return new MostUsefulConfiguration()
.useStoryControls(new StoryControls().doDryRun(false).doSkipScenariosAfterFailure(false))
.useStoryLoader(new LoadFromURL())
.useStoryParser(new RegexStoryParser(examplesTableFactory))
.useStoryPathResolver(new UnderscoredCamelCaseResolver())
.useStoryReporterBuilder(
new StoryReporterBuilder()
.withCodeLocation(CodeLocations.codeLocationFromClass(embeddableClass))
.withDefaultFormats().withPathResolver(new ResolveToPackagedName())
.withViewResources(viewResources).withFormats(org.jbehave.core.reporters.Format.HTML,
org.jbehave.core.reporters.Format.TXT, org.jbehave.core.reporters.Format.XML)
.withFailureTrace(true).withFailureTraceCompression(true).withCrossReference(xref))
.useParameterConverters(parameterConverters)
.useStepPatternParser(new RegexPrefixCapturingPatternParser("%"))
.useStepMonitor(xref.getStepMonitor());
}
#Override
public InjectableStepsFactory stepsFactory(){
return new InstanceStepsFactory(configuration(), new LoginSteps());
}
#Override
protected List<String> storyPaths(){
String codeLocation = org.jbehave.core.io.CodeLocations.codeLocationFromClass(this.getClass()).getFile();
return new StoryFinder().findPaths(codeLocation, asList("**/login_trial.story"),
asList(""), "file:" + codeLocation);
}
}
I found it. Right here was the culprit:
.useStepPatternParser(new RegexPrefixCapturingPatternParser("%"))
It was causing JBehave to not recognize the #Given annotations and so JBehave assumed everything needed a step and listed them all as pending (or skipped them because they were missing the #Given step). Once I pulled that part of the configuration everything was cool.

Wicket : Submitting data with a listview inside a form

I have the following issues:
Imagine a wicket list view that looks something like this:
ListView<Question> lvQuestion = new ListView<Question>("lvQuestion",
questions) {
private static final long serialVersionUID = 1L;
#Override
protected void populateItem(ListItem<Question> item) {
Question q = item.getModelObject();
item.add(new Label("question", q.getQuestion()));
List<String> possibleAnswers = getFromDb();
q.setPossibleAnswers(possibleAnswers);
if(q.getComponent().equals("dropDown")){
item.add(new DropDownPanel("questionComponent", q));
}
}
};
This listview is created within a form component; what i need to do is take every question answered using this listview and record them in the database.
Problem being though is that I don't know how many drop down panels there will be so there is always going to be a dynamic set of questions returned.
Is there a way of ensuring that when the parent form is submitted every model in the listview is submitted to the database.
What I do for these situations is maintain a hashmap with the selected option for each question. I update it with AjaxFormComponentUpdatingBehavior such as the following:
First, declare & initialize the map (There may be a more efficient way of doing this)
Map<Question, String> qMap = new HashMap<>();
for(Question q : questions) {
qMap.put(q, "");
}
Then, update the map within your ListView (you probably want to set q as final when you initialize it)
if(q.getComponent().equals("dropDown")) {
DropDownPanel ddp = new DropDownPanel("questionComponent", q);
ddp.add(new AjaxFormComponentUpdatingBehavior("onchange") {
#Override
protected void onUpdate(AjaxRequestTarget art) {
String answer = (String) getFormComponent().getConvertedInput();
qMap.put(q, answer);
}
}
item.add(ddp);
}
This allows you to keep the qMap up to date and process it when the form is submitted.
Alternatively, if you have a String attribute to hold the answer on the Question object and you make sure your ListView.list and your List questions stay consistent, you can do the following in the onUpdate() without needing the map:
q.setAnswer(answer);
questions.set(item.getIndex(), q);
In this case you would just process the questions list in the form's onSubmit().

AutoComplete with GlazedLists

I'm using glazedlists for auto-completion but i want to ask something in this point. I have an arraylist for friendlist. People can be added to friendlist or can be removed from friendlist by clicking add or remove button. Data of friendlist is written to friend.txt and is read from friend.txt ,by using Gson and Json.
When user add a person to the list or remove a person from the list the selected person is removed from or added to friend.txt.
...
Object[] elements = new Object[holdSizeValue];
for( int i = 0 ; i < holdSizeValue ; i++ ){
elements[i] = sendFriendNameFromList(i);
}
searchBox = new JComboBox<Object>();
final EventList<Object> eventList = GlazedLists.eventList(Arrays.asList(elements));
SwingUtilities.invokeAndWait(new Runnable() {
#Override
public void run() {
AutoCompleteSupport.install(searchBox, eventList);
}
});
...
As you might understand from the code, i used glazedlist for search operation.I want to update elements[]. How can i update ? When i call the method ,which includes the code above , from controller of add or remove button i am getting an exception which is about invokeAndWait.
The major advantage of the EventList is that once it's initialised you simply add objects to it and everything that is observing that data, e.g., the ComboBox will magically update thanks to all the plumbing GlazedLists provides.
So, move the EventList to be an instance variable:
private EventList<Object> eventList = new BasicList<Object>();
Perform the AutoCompleteSupport once, after the searchBox has been created and set up.
Then, when you need to add items call eventList.addAll(...). Don't reinstantiate that list, nor reinstanstiate the searchBox each time you want to do an update. Add/remove with the event list and the rest will follow automatically.

Wicket not updating page after form submit

I have this code in my page constructor:
private String selectedAwsId;
private String selectedIsReal;
//these two are actually outside the constructor, and getters and setters for these two strings not shown
List<AwsCredentials> awsCredentials = (List<AwsCredentials>)getAwsCredentials();
List<String> awsIds = new ArrayList<String>();
for (AwsCredentials cred : awsCredentials){
awsIds.add(cred.getAwsId());
}
selectedAwsId = awsIds.get(0);
List<String> yesOrNo = Arrays.asList(new String[] { "sandbox", "real"});
selectedIsReal = "sandbox";
Form selectAwsCredentialsForm = new Form("selectAwsCredentialsForm"){
#Override
public void onSubmit() {
super.onSubmit();
//TODO: why isn't this updating the form?
}
};
add(selectAwsCredentialsForm);
selectAwsCredentialsForm.add(new DropDownChoice("selectAwsCredentialsDropdown", new PropertyModel(this, "selectedAwsId"), awsIds));
selectAwsCredentialsForm.add(new DropDownChoice("selectRealOrSandboxHitsDropdown", new PropertyModel(this, "selectedIsReal"), yesOrNo));
The first time I render the page, this works fine. But when I change the selection in either of the DropDownChoices and submit the form, the page doesn't change (values in the selectedAwsId and selectedIsReal aren't altered accordingly). Is there something I'm missing in my understanding of how forms work? Does the entire page get refreshed when a form gets submitted (does the constructor get run again?)
You might want to construct a model and set this as your form's model. (This is what I do.) Submitting the form (and if all is coded appropriately) will then result in the form's model being updated.