Wicket - Set Model from another panel - java

I am quite new to Wicket. I am adding a model to a sub-panel(ChartPanel) from a main panel (MainPanel) on a button click.
MainPanel.java
On button click, I am re-adding the chartPanel after I change its model. Following is the code I am using in the buttonClick of the MainPanel. Here the onRenderAnnotations event is generated on some click in the UI.
#OnEvent
public void onRenderAnnotations(RenderAnnotationsEvent aEvent)
{
LOG.trace("clicked on the annotation");
renderChart( aEvent.getRequestHandler());
}
private void renderChart(IPartialPageRequestHandler aRequestHandler)
{
MultiValuedMap<String, Double> recommenderScoreMap = getLatestScores(aRequestHandler);
Map<String,String> curveData = new HashMap<String,String>();
LearningCurve learningCurve = new LearningCurve();
for (String recommenderName : recommenderScoreMap.keySet()) {
String data = recommenderScoreMap.get(recommenderName).stream().map(Object::toString)
.collect(Collectors.joining(", "));
curveData.put(recommenderName,data);
learningCurve.setCurveData(curveData);
learningCurve.setMaximumPointsToPlot(MAX_POINTS_TO_PLOT);
}
chartPanel.setDefaultModel(Model.of(learningCurve));
// to avoid the error, A partial update of the page is being rendered
try {
aRequestHandler.add(chartPanel);
}
catch (IllegalStateException e) {
LOG.warn("Not updating the chart. " + e.toString());
setResponsePage(getPage());
}
}
ChartPanel.java
After this in the chartPanel, I want to use the updated model to add component inside the chartpanel. What would be the best way to do that?
I want to do something like this in the class ChartPanel:
#Override
protected void onRender()
{
super.onModelChanged();
LearningCurve newLearningCurve = getModel().getObject();
requestTarget = ???
String js = createJavascript(newLearningCurve);
requestTarget.prependJavascript(js);
}
My question is, in the above code how to get the request target since it is not an ajax request neither do I get it in the arguments. Should I use some other function where I also get a requestTarget. But I want it to be called every time the model of ChartPanel is updated from anywhere.
Pardon my ignorance. I have been trying for a few days but I am still stuck. I tried to explain it enough but if any information is missing, please comment and I will add it right away.
Thanks.

You should override renderHead() instead:
#Override
public void renderHead(IHeaderResponse response)
{
super.renderHead(response);
response.render(OnLoadHeaderItem.forScript(
createJavascript(newLearningCurve)));
}
This way your chart will be shown correctly regardless whether it was added due to an AjaxRequest or simply when the page is rerendered.

Related

call javascript in wicket and set value from Wicket as a parameter

I have wicket component with onClick event where I'd like to run javascript code which:
reloads the page
after page has been reloaded, scroll down to the markupId which was clicked
I have to pass as parameter the "markupId" value from wicket to javascript to find out to which position should I scroll down
WicketComponent.java
MyPanel div = new MyPanel("div");
div.add(new AjaxEventBehavior("click") {
#Override
protected void onEvent(AjaxRequestTarget target) {
// some requests...
String markupId = div.getMarkupId();
target.appendJavaScript("window.location.reload();");
target.appendJavaScript(jsReload(markupId));
}
div.add(AttributeModifier.replace("onclick", "clicked('" + div.getMarkupId() + "');"));
#Override
public void renderHead(IHeaderResponse response) {
super.renderHead(response);
response.render(JavaScriptReferenceHeaderItem.forReference(new JavaScriptResourceReference(this.getClass(), "script.js")));
}
WicketComponent.html
<div wicket:id="div" onclick="clicked('markupId');">Text</div>
script.js
function clicked(markupId) {
window.location.reload();
}
document.addEventListener("DOMContentLoaded", function (event) {
let elementOffset = $("#{markupId}").offset().top; // how to pass here markupId parameter from wicket ?
let windowOffset = $(window).scrollTop();
window.scrollTo(0, elementOffset- windowOffset);
});
how to pass parameter "markupId" in javascript file which was attached in renderHead() or may be there is another solution for this ? I'll appreciate any help. Thanks!
you should solve your problem using location hash as described here:
Can we have code after location.reload(true)?
for the hash value use a fixed markup id for your component, something like:
div.setMarkupId("myMarkupId");
div.add(new AjaxEventBehavior("click") {
#Override
protected void onEvent(AjaxRequestTarget target) {
// some requests...
String markupId = div.getMarkupId();
target.appendJavaScript("window.location.hash = 'myMarkupId'");
target.appendJavaScript("window.location.reload();");
//that's it! no other js is needed
}
}
I haven't tried it but after page reloading it should scroll down to your component.

Update Wicket Text Field on onUpdate event

I have a component that extends TextField where a user can type an web address. I want that after the user type something (for example www.example.org) to change that value to something else (for exemple http://www.example.org)
I have tried this:
urlField = new TextFieldIndicatingError<String>("url", new PropertyModel<String>(this, "url"));
urlField.add(new AjaxFormComponentUpdatingBehavior("onblur") {
#Override
protected void onUpdate(AjaxRequestTarget target)
{
//url = "ABCDDEE";
urlField.getModel().setObject("AAAA");
}
});
but anything inside the onUpdate() doesn't seems to have an effect in the TextField's value.
What I'm doing wrong here?
You need to use target.add(urlField) to update it on the client side after setting its new model.

GWT - Methods in onModuleLoad() called multiple times

I'm trying to write a simple application to display chart data. I want to display some data as soon as the user loads the page, so I'm getting data & drawing tables inside of the Runnable as described in the gwt-visualization Getting Started.
Things seem to work alright, except charts tend to get loaded more than once. Below is my onModuleLoad().
private final StatisticsServiceAsync statisticsService = GWT.create(StatisticsService.class);
GWTBeanFactory factory = GWT.create(GWTBeanFactory.class);
DataTable locationData;
AnnotatedTimeLine atl;
GeoMap usMap;
TextBox storeField;
Button log10Button;
DateRange durationChartRange;
String eusrJson = null;
Button b;
HTML last1000Html;
public void onModuleLoad() {
storeField = new TextBox();
storeField.setText("Enter a store");
storeField.addKeyDownHandler(new MyKeyHandler());
b = new Button("Get Stats!");
log10Button = new Button("Show Log10 Scale");
log10Button.addClickHandler(new Log10ClickHandler());
b.addClickHandler(new MyClickHandler());
last1000Html = new HTML();
getLast1000Avg();
Runnable onLoadCallback = new Runnable() {
public void run() {
storeDurationData = DataTable.create();
storeDurationDataLog10 = DataTable.create();
RootPanel.get("storeDurationDiv").add(storeField);
RootPanel.get("storeDurationDiv").add(b);
RootPanel.get("storeDurationDiv").add(log10Button);
RootPanel.get("storeDurationDiv").add(last1000Html);
log10Button.setVisible(false);
// Get initial Data
getAvgByRegion();
getLast1000Avg();
Scheduler.get().scheduleFixedDelay(new RepeatingCommand() {
#Override
public boolean execute() {
getLast1000Avg();
return true;
}
}, 5000);
}
};
// Load the visualization api, passing the onLoadCallback to be called
// when loading is done.
VisualizationUtils.loadVisualizationApi(onLoadCallback, AnnotatedTimeLine.PACKAGE);
VisualizationUtils.loadVisualizationApi(onLoadCallback, GeoMap.PACKAGE);
}
All of the "simple" elements seem to get populated correctly, as the Button, HTML, and TextBox all get placed appropriately (which used to be inside of run, they're where they are now as a result of debugging previous errors). However, the GeoMap gets placed twice, and looking at the logging you can tell that the Runnable's run is being executed at least twice, which seems reasonable, but I don't know how to keep it from adding twice.
I'm probably screwing up something with the Async stuff, but I'm new and confused. Below is my getAvgByRegion() method:
private void getAvgByRegion() {
statisticsService.getEusrForRegion(new AsyncCallback<String>() {
#Override
public void onFailure(Throwable caught) {
System.out.println(":(");
}
#Override
public void onSuccess(String result) {
createLocTable();
DataTable dt = parseEusrLocations(result);
usMap = new GeoMap(dt, createGeoMapOptions());
usMap.setSize("800px", "600px");
RootPanel.get("storeDurationDiv").add(usMap);
}
});
}
Any advice on how best to work with GWT is welcome.
So, you call VisualizationUtils.loadVisualizationApi twice, so the onLoadCallback will be run twice (I don't know GWT Google Apis, this is a supposition).
onLoadCallback calls getAvgByRegion, so that one will get called twice too; and it gets data and in the callback creates a new GeoMap and adds it to the RootPanel.get("storeDurationDiv"), so you get two GeoMaps on the screen.
The other widgets(storeField, etc.) are created only once, so adding them repeatedly is not a problem (except performance-wise), as they'll first be removed from their current parent before being added to the new one (which in this case is the same)

Can't display GWT DataGrid

So I'm having a heck of a time creating a Datagrid with GWT. I've created my table according the the docs for GWT, and I've added my data, but I can't get it or the datagrid itself to show up at all. What am I missing? I feel I've been tearing my hair out over this. I feel like making an aysnc call might be an issue, but I get no errors. When i compile and execute this portion of my code nothing shows up on the screen and the area where the datagrid is supposed to be on the dock is empty. Am I forgetting something trivial?
static int orderID = 1001;
private static List<OrderLine> orderLineList = new ArrayList<OrderLine>();
final DataGrid<OrderLine> dgOrder = new DataGrid<OrderLine>();
dgOrder.setWidth("100%");
//set columns
TextColumn<OrderLine> orderLineIdColumn = new TextColumn<OrderLine>(){
#Override
public String getValue(OrderLine object) {
return Integer.toString(object.getOrderLineID());
}
};
dgOrder.addColumn(orderLineIdColumn, "OrderLine ID");
TextColumn<OrderLine> productColumn = new TextColumn<OrderLine>(){
#Override
public String getValue(OrderLine object) {
return getProductName(object.getProductNumber());
}
};
dgOrder.addColumn(productColumn, "Product");
TextColumn<OrderLine> quantityColumn = new TextColumn<OrderLine>(){
#Override
public String getValue(OrderLine object) {
return Integer.toString(object.getQuantity());
}
};
dgOrder.addColumn(quantityColumn, "Quantity");
// add data to datagrid
Ioma.dataservice.getOrderLines(orderID, new AsyncCallback<ArrayList<OrderLine>>(){
#Override
public void onFailure(Throwable caught) {// TODO Auto-generated method stub
System.out.println("error in retrieving GP.getOrderLines" + caught.toString());
}
#Override
public void onSuccess(ArrayList<OrderLine> result) {
// TODO Auto-generated method stub
//yes i realize I could also set it to "result" but I use the result in other places as well, I have also tried setting it to result with no success
orderLineList = result;
dgOrder.setRowData(0, orderLineList);
}
});
//add datagrid to the dock
dock.add(dgOrder, DockPanel.EAST);
//add dock to mainPanel
return dock;
When you use the DataGrid you need to give it an explicit size. For CellTable you don't need to so that's why it worked.
When you were using the DataGrid is was there but had both height and width of 0. So you either need to explicitly set the size:
DataGrid<OrderLine> grid = new DataGrid<OrderLine>;
grid.setWidth("500px");
grid.setHeight("600px");
dock.add(dgOrder, DockPanel.EAST);
or you can put the gird in a ProvidesResize widget. So in your case I believe you can make it work with a DockLayoutPanel as opposed to a DockPanel which is what you seem to be using.
I had a smilliar problem . Try to put the datagrid in a Panel that implements the ProvidesResize interface, like SimpleLayoutPanel. Also in my case it helped to set the size of the SimpleLayoutPanel.
Turns out my problem was my declaration.
final DataGrid<OrderLine> dgOrder = new DataGrid<OrderLine>();
should be
final CellTable<OrderLine> dgOrder = new CellTable<OrderLine>();
I can't find out why this works, but it does. I can see my table now. are you not supposed to explicitly call an instance of datagrid I wonder?

Is it possible to nest forms in Wicket that are independent of each other?

Is it possible to nest forms in Wicket that are independent of each other? I want to have a form with a submit button and a cancel button. Both buttons should direct the user to the same page (let's call it Foo). The submit button should send some info to the server first; the cancel button should do nothing.
Here's a really simplified version of my existing code:
Form form = new Form() {
public void onSubmit()
{
PageParameters params = new PageParameters();
params.put("DocumentID", docID);
setResponsePage(Foo.class, params);
}
};
DropDownChoice<String> ddc = new DropDownChoice<String>("name", new PropertyModel<String>(this, "nameSelection"), names);
ddc.setRequired(true);
final Button submitButton = new Button("Submit") {
public void onSubmit() { doSubmitStuff(true); }
};
final Button cancelButton = new Button("Cancel") {
public void onSubmit() { doSubmitStuff(false); }
};
form.add(ddc);
form.add(submitButton);
form.add(cancelButton);
form.add(new FeedbackPanel("validationMessages"));
The problem is, I just added a validator, and it fires even if I press the cancel button, since the cancel button is attached to the same form as everything else. This could be avoided if the cancel button were in a separate form. As far as I know, I can't create a separate form because — due to the structure of the HTML — the separate form would be under the existing form in the component hierarchy.
Can I make the forms separate somehow in spite of the hierarchy? Or is there some other solution I can use?
EDIT:
In response to Don Roby's comment, this is a bit closer to what my code looked like back when I was trying setDefaultFormProcessing():
Form<Object> theForm = new Form<Object>("theForm") {
public void onSubmit()
{
PageParameters params = new PageParameters();
params.put("DocumentID", docID);
setResponsePage(Foo.class, params);
}
};
final CheckBox checkbox = new CheckBox("checkbox", new PropertyModel<Boolean>(this, "something"));
checkbox.add(new PermissionsValidator());
theForm.add(checkbox);
final Button saveButton = new Button("Save") {
public void onSubmit()
{ someMethod(true); }
};
final Button cancelButton = new Button("Cancel") {
public void onSubmit()
{ someMethod(false); }
};
cancelButton.setDefaultFormProcessing(false);
theForm.add(saveButton);
theForm.add(cancelButton);
theForm.add(new FeedbackPanel("validationMessages"));
There is an even simpler solution: call the setDefaultFormProcessing method on the cancel button with false as a parameter:
cancelButton.setDefaultFormProcessing(false);
This way, clicking the cancel button will bypass the form validation (and model updating), directly calling the onSubmit function.
It is possible to "nest" forms in wicket.
See this wiki entry
for some notes on how it works and this wiki entry for how it interacts with validation.
But for what you're after, the answer from Jawher should have worked and is much simpler.
Look at this example code for hints on getting that working.
I'm wondering if you've simplified your code too far in this posting. Can you produce a sample small enough to post that definitely has the problem?

Categories

Resources