I'm attempting to create something like a read-only console window using apache wicket. Essentially the users submits a form to kick off a server-side operation. Then can then track the job output on the page.
I'm currently displaying the output as shown below:
public class ConsoleExample extends WebPage {
protected boolean refreshing;
private String output = "";
public void setOutput(String newOutput) {
synchronized (this) {
output = newOutput;
}
}
public void appendOutput(String added) {
synchronized (this) {
this.output = output+added;
}
}
public ConsoleExample() {
Form<ConsoleExample> form = new Form<ConsoleExample>("mainform");
add(form);
final TextArea<String> outputArea = new TextArea<String>("output",
new PropertyModel<String>(this, "output"));
outputArea.setOutputMarkupId(true);
// A timer event to add the outputArea to the target, triggering the refresh
outputArea.add(new AbstractAjaxTimerBehavior(Duration.ONE_SECOND){
private static final long serialVersionUID = 1L;
@Override
protected void onTimer(AjaxRequestTarget target) {
synchronized (this) {
if(refreshing ){
target.focusComponent(null);
target.addComponent(getComponent());
}
}
}
});
add(outputArea);
form.add(new AjaxSubmitLink("run") {
private static final long serialVersionUID = 1L;
@Override
public void onSubmit(final AjaxRequestTarget target, Form<?> form) {
setOutput("");
new Thread(new Runnable() {
@Override
public void run() {
try {
refreshing = true;
ProcessBuilder pb = new ProcessBuilder(Collections.singletonList("execute"));
pb.redirectErrorStream(true);
String line;
BufferedReader br = new BufferedReader(new InputStreamReader(pb.start().getInputStream()));
while ((line = br.readLine()) != null) {
appendOutput("
" + line);
}
} catch (IOException e) {
//...
} finally {
//...
refreshing = false;
}
}
}).start();
}
});
}
The problem with this solution is that each time the AjaxTimerBehaviorRuns the refresh resets the text area properties, namely, cursor position and scroll position. Therefore as the output increases the user can't track the output as the textarea jumps back to start every second.
Is there a better way to achieve this?
A posible easy to implement approach is to add a hidden TextField
that you update throw AJAX with a AjaxTimerBehavior
and then calls a JavaScript function (using AjaxRequestTarget.appendJavaScript()
) that synchronizes the value of the hidden TextField
with your <textarea>
.
Partial Solution Following on from jordeu's suggestion of using the appendJavaScript() function I am just adding a bit of javascript to append the new text:
protected void onTimer(AjaxRequestTarget target) {
synchronized (this) {
if(refreshing ){
if(update != null){
target.appendJavascript("var obj=document.getElementById(\"output\");var txt=document.createTextNode(\""+update+"\");obj.appendChild(txt)");
update = null;
}
}
}
}
Where the update
field is any new text since the last update.
This solves the scroll issue but any user selection is still reset. Also it doesn't seem like a "nice" solution to me.
Any further suggestions of how to improve are welcome.