I have this code:
searchButton.addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
statusLabel.setText("Searching...");
final String query = searchField.getText();
RootPanel.get("flickr").clear();
AsyncCallback<Flickr> ac=new AsyncCallback<Flickr>(){
#Override
public void onFailure(Throwable caught) {
}
#Override
public void onSuccess(Flickr result) {
for(Photo p:result.getPhotos().getPhoto())
{
flck.add(p);
}
statusLabel.setText("");
}
};
mashupService.getFlickrPhotos(query, ac);
if(!flck.isEmpty())
{
for(int i=0;i<flck.size();i++)
{
RootPanel.get("flickr").add(new HTML("<img src='http://farm"+flck.get(i).getFarm()+".staticflickr.com/"+flck.get(i).getServer()+"/"+flck.get(i).getId()+"_"+flck.get(i).getSecret()+".jpg'/><br/>"));
}
}
}
});
I want execute first onSuccess (have flick.add)... but it executes after of if(!flck.isEmpty)... and I need have flck with data but I can't...
When I press secont time the same button, flck have data of first onClick...
Thanks in advance
Move the code inside the onSuccess() method that is depended on the result of AsyncCallback.
It's clear from the name that AsyncCallback is just like a AJAX request that talks to server asynchronously means the execution of code is not sequential.
Just move if(!flck.isEmpty)... inside onSuccess() method.
Related
I am making a media player which allows users to drag and drop media thumbnails onto a media player (using JavaFX MediaPlayer).
When I run it and try the drag/drop functionality, some of the time it works just as expected. However sometimes it produces a weird error.
The error in its fullness is:
Error:80de0001 in CallbackToJava(javaIDs.View.notifyDragDrop, grfKeyState, pt, pdwEffect)
COM Error:80de0001 Unknown Error 0x80DE0001
Error:80de0001 in :: DoDragDrop( pcd, IDropSourcePtr(new GlassDropSource(obj), getDROPEFFECT(supportedActions, &performedDropEffect)
COM Error:80de0001 Unknown error 0x80DE0001
The print-out in Eclipse looks like: (all double-spaced)
I really have no idea why this is happening - I'm clearly doing something wrong. The error doesn't point me to any line of code. I would suggest that the error is referring to a memory address? Are you able to help me, please?
The listeners/handlers which perform the Drag/Drop functionality are:
root.setOnMouseEntered(new EventHandler<MouseEvent>(){
#Override
public void handle(MouseEvent arg0) {
root.setStyle(highlightBackgroundStyle);
parentScene.setCursor(Cursor.OPEN_HAND);
}
});
root.setOnMouseExited(new EventHandler<MouseEvent>(){
#Override
public void handle(MouseEvent arg0) {
root.setStyle(backgroundStyle);
//parentScene.setCursor(Cursor.DEFAULT);
}
});
root.setOnDragDetected(new EventHandler<MouseEvent>(){
#Override
public void handle(MouseEvent arg0) {
parentScene.setCursor(Cursor.CLOSED_HAND);
Dragboard db = parentScene.startDragAndDrop(TransferMode.COPY);
ClipboardContent content = new ClipboardContent();
content.putString(mediaFile.toURI().toString());
db.setContent(content);
arg0.consume();
}
});
mediaPanel.getDropZoneA().setOnDragDropped(new EventHandler<DragEvent>(){
#Override
public void handle(DragEvent arg0) {
mediaPanel.getMixerA().dragGraphicsOff();
Dragboard db = arg0.getDragboard();
if (db.hasString())
{
//System.out.println(db.getString());
String mediaURI = db.getString();
System.out.println(mediaURI);
mediaPanel.getMixerA().getJfxMediaPlayer().loadAndPlayMediaA(new Media(mediaURI));
mediaPanel.getMixerA().prepareMedia();
arg0.setDropCompleted(true);
}else{System.out.println("Drag error");arg0.setDropCompleted(false);}
arg0.consume();
}
});
mediaPanel.getDropZoneA().setOnDragExited(new EventHandler<DragEvent>(){
#Override
public void handle(DragEvent arg0) {
mediaPanel.getMixerA().dragGraphicsOff();
arg0.consume();
}
});
mediaPanel.getDropZoneA().setOnDragOver(new EventHandler <DragEvent>() {
public void handle(DragEvent arg0) {
mediaPanel.getMixerA().dragGraphicsOn();
if (arg0.getGestureSource() != mediaPanel.getDropZoneB() && arg0.getDragboard().hasString())
{
arg0.acceptTransferModes(TransferMode.COPY);
}
arg0.consume();
}
});
Update 1
I am wondering whether this is caused by the way I 'change' the media for the MediaPlayer. I do the following:
public void loadAndPlayMediaA(Media playableMedia)
{
if((player != null))
{
player.stop();
}
media = playableMedia;
player = new MediaPlayer(media);
player.play();
}
I then immediately call:
public void prepareMedia()
{
view.setMediaPlayer(jfxMediaPlayer.getMediaPlayer());
view.setPreserveRatio(preserveRatio);
view.setFitWidth(miniaturePlayerX);
view.setFitHeight(miniaturePlayerY);
playerSlider.setValue(0.0);
}
...The key line being player = new MediaPlayer(media);. I wonder whether occasionally I am hitting the split second where the MediaView is trying to get the next frame, but failing because the media has been re instantiated. (?)
What's the best way to load up a new video?
I don't know if you haven't found a solution yet...
I had the same problem and solved it by following code:
setOnDragDropped(new EventHandler<DragEvent>()
{
#Override
public void handle(DragEvent event)
{
System.out.println("onDragDropped");
event.setDropCompleted(true);
}
});
setOnDragExited(new EventHandler<DragEvent>()
{
#Override
public void handle(DragEvent event)
{
System.out.println("onDragExited");
Dragboard db = event.getDragboard();
ArrayList<Object> f = (ArrayList<Object>) db.getContent(DataFormat.FILES);
for(Object o:f)
System.out.println(o.getClass().getName() + ":" + o);
File file = (File) f.get(0);
newMedia(file.toURI());
}
});
setOnDragOver(new EventHandler<DragEvent>()
{
#Override
public void handle(DragEvent event)
{
System.out.println("onDragOver");
event.acceptTransferModes(TransferMode.ANY);
}
});
public void newMedia(URI uri)
{
m = new Media(uri.toASCIIString());
mp = new MediaPlayer(m);
mV = new MediaView(mp);
mp.setOnReady(new Runnable()
{
#Override
public void run()
{
mp.play();
}
});
}
In the DragDropped handler I call setDropCompleted to call the DragExited handler. Then I get the File out of the Dragboard and create a new MediaPlayerInstance. And then, you aren't allowed to call play immideately, else nothing will happen. You need to call play in the OnReady Handler, or at least after the MediaPlayer's status has been changed to Ready.
The Error will still appear, but it should work now ;)
I have an webapp with a large collection. So my user get a text box to filter the collection.
But every time the user put in one letter the filter action starts. This is very slow some times.
So I want only get the value change event when the user stop typing for one second.
I tried it this way:
#Override
public void onValueChange( ValueChangeEvent<String> event )
{
Timer t = new Timer()
{
#Override
public void run()
{
addChangeHandler( new ChangeHandler()
{
#Override
public void onChange( ChangeEvent event1 )
{
ValueChangeEvent.fire( TextBoxPSG.this, getValue() );
}
} );
}
};
t.schedule( 15000 );
}
But this doesn't work.
Maybe someone has an Idea or the same problem.
Thanks in advance.
Do not use ValueChangeEvent. Use KeyUpEvent.
private static Timer timer = new Timer() {
#Override
public void run() {
// do you filter work;
}
};
...
myTextBox.addKeyUpHandler(new KeyUpHandler() {
#Override
public void onKeyUp(KeyUpEvent event) {
if (timer.isRunning()) {
timer.cancel();
}
timer.schedule(1000);
}
});
This question is very useful. There're some questions about calling multiple AsyncCallback but they didn't tell how to call them in a loop.
Here is my problem. I am doing a project using Gwt-platform. I got a presenter TestPresenter.java which has these codes:
#Inject
DispatchAsync dispatchAsync;
private AsyncCallback<GetDataResult> getDataCallback = new AsyncCallback<GetDataResult>() {
#Override
public void onFailure(Throwable caught) {
// TODO Auto-generated method stub
}
#Override
public void onSuccess(GetDataResult result) {
// do Something o show the gui here
}
};
public void load_All_Gui_At_Once() {
for(int i=0; i<5; i++) {
GetData getDataAction=new GetData(i);
dispatchAsync.execute(getDataAction, getDataCallback);
}
}
The problem is that the program show the data but it showed in the wrong order. This is cos the next Async method started to run while the previous one has not finish yet.
Some suggested me to put the 2nd call inside onSuccess, but that is only for simple 2 sync calls. But in my case, I have to loop many unknown number of Async calls, then how can i do it?
This is a question similar to this one. All your calls are executed in the same instant, but the response time is unknown and it is not guaranteed the order. So the solution is almost the same, call the loop inside the callback. Your code should look like:
#Inject
DispatchAsync dispatchAsync;
private AsyncCallback<GetDataResult> getDataCallback = new AsyncCallback<GetDataResult>() {
int idx = 0;
#Override
public void onFailure(Throwable caught) {
// TODO Auto-generated method stub
}
#Override
public void onSuccess(GetDataResult result) {
if (result != null) {
// do Something or show the gui here
}
if (idx < 5) {
GetData getDataAction = new GetData(idx);
dispatchAsync.execute(getDataAction, getDataCallback);
}
idx ++;
}
};
public void load_All_Gui_At_Once(){
// Start the loop, calling onSuccess the first time
getDataCallback.onSuccess(null);
}
I have the following problem:
I am trying to model a process using GWT, where i have a couple of views with a couple of submit buttons. And pressing button1 will create a server interaction and if everything was ok, the next view will be loaded. My problem is now that I get really nasty spaghetti code (just very highlevel to show you what i mean):
onClick {
AsyncCallback {
onSuccess {
load new view with another clickhandler and an asynccallback
}
}
}
Is there some way to create some kind of abstraction or something? Maybe a state pattern? How? Thanks a lot!
This is actually a very good question - and probably one without a definitive answer. It's a problem that applies to many frameworks, not just GWT, so I like your idea to look at this with some simplified code. I'll make this a little bit longer, to show what even just 4 very simple callbacks look like:
Nested callbacks
alice.call("a", new Callback() {
#Override
public void onSuccess() {
bob.call("b", new Callback() {
#Override
public void onSuccess() {
charlie.call("c", new Callback() {
#Override
public void onSuccess() {
daisy.call("d", new Callback() {
#Override
public void onSuccess() {
// finished
}
});
}
});
}
});
}
});
Named callbacks
You can use your IDE to refactor this easily into named callbacks (hint: Please read the callbacks from bottom to top!):
final Callback daisyCallback = new Callback() {
#Override
public void onSuccess() {
// finished
}
};
final Callback charlieCallback = new Callback() {
#Override
public void onSuccess() {
daisy.call("d", daisyCallback);
}
};
final Callback bobCallback = new Callback() {
#Override
public void onSuccess() {
charlie.call("c", charlieCallback);
}
};
final Callback aliceCallback = new Callback() {
#Override
public void onSuccess() {
bob.call("b", bobCallback);
}
};
alice.call("a", aliceCallback);
Problem: The control flow is not so immediately obvious anymore.
Still, an IDE can help by using "Search References" (Ctrl-G in Eclipse) or something similar.
Event Bus (or Observer/Publish-Subscribe pattern)
This is how the same calls look like with an event bus:
alice.call("a", new Callback() {
#Override
public void onSuccess() {
bus.fireEvent(BusEvent.ALICE_SUCCESSFUL_EVENT);
}
});
bus.addEventListener(BusEvent.ALICE_SUCCESSFUL_EVENT, new BusEventListener() {
#Override
public void onEvent(final BusEvent busEvent) {
bob.call("b", new Callback() {
#Override
public void onSuccess() {
bus.fireEvent(BusEvent.BOB_SUCCESSFUL_EVENT);
}
});
}
});
bus.addEventListener(BusEvent.BOB_SUCCESSFUL_EVENT, new BusEventListener() {
#Override
public void onEvent(final BusEvent busEvent) {
charlie.call("c", new Callback() {
#Override
public void onSuccess() {
bus.fireEvent(BusEvent.CHARLIE_SUCCESSFUL_EVENT);
}
});
}
});
bus.addEventListener(BusEvent.CHARLIE_SUCCESSFUL_EVENT, new BusEventListener() {
#Override
public void onEvent(final BusEvent busEvent) {
daisy.call("d", new Callback() {
#Override
public void onSuccess() {
bus.fireEvent(BusEvent.DAISY_SUCCESSFUL_EVENT);
}
});
}
});
bus.addEventListener(BusEvent.DAISY_SUCCESSFUL_EVENT, new BusEventListener() {
#Override
public void onEvent(final BusEvent busEvent) {
// finished
}
});
Under the right circumstances (when it's very clear what each event means, and
if you don't have too many), this pattern can make things very nice and clear.
But in other cases, it can make the control flow more confusing (and you easily get twice the lines of code).
It's harder to use your IDE to find out about the control flow.
The GWT History mechanism is a very positive example for where to use this technique reasonably.
Divide and Conquer
In my experience, it's often a good idea to "divide and conquer" by mixing nesting and named callbacks:
final Callback charlieCallback = new Callback() {
#Override
public void onSuccess() {
daisy.call("d", new Callback() {
#Override
public void onSuccess() {
// finished
}
});
}
};
alice.call("a", new Callback() {
#Override
public void onSuccess() {
bob.call("b", new Callback() {
#Override
public void onSuccess() {
charlie.call("c", charlieCallback);
}
});
}
});
Depending on the situation, two nested callbacks are often still readable, and they reduce jumping around between methods when reading the code by 50%.
(I created a pastebin of my examples here, if you like to play around with them: http://pastebin.com/yNc9Cqtb)
Spaghetti code is a tricky problem in GWT as it is in Javascript, where much of your code is structured around asynchronous callbacks.
Some of the techniques to deal with it that are described in the answers to this question could apply.
The suggested approach to avoid coupling between widgets is to use EventBus. Read more details here https://developers.google.com/web-toolkit/articles/mvp-architecture#events
Hope it helps.
changeview(boolean first){
if(first)
{
firstView.setVisible(true);
secondView.setVisible(false);
}else{
firstView.setVisible(false);
secondView.setVisible(true);
}
}
onClick {
AsyncCallback {
onSuccess {
changeView(false);
}
}
}
Switch between views by above.
Use MVP from very beginning. Use Activities and Places. Your code will be clean.
To clarify what double submit is: When the user clicks on a submit button twice, the server will process the same POST data twice. To avoid this (apart from disabling the button after a single submit), most web frameworks like Struts provide a token mechanism. I am searching for the equivalent of this in GWT.
If you want to avoid submitting twice, how about:
boolean processing = false;
button.addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
if (!processing) {
processing = true;
button.setEnabled(false);
// makes an RPC call, does something you only want to do once.
processRequest(new AsyncCallback<String>() {
#Override
public void onSuccess(String result) {
// do stuff
processing = false;
button.setEnabled(true);
});
});
}
}
});
That's the gist of it.
This will be helpfull for you -
final Button btn = new Button("Open");
btn.addSelectionListener(new SelectionListener<ButtonEvent>() {
#Override
public void componentSelected(ButtonEvent ce) {
btn.setEnabled(false);
openMethod(name, new AsyncCallback<Void>() {
public void onFailure(Throwable caught) {
btn.setEnabled(true);
}
public void onSuccess(Void result) {
MessageBox.alert(info, "Opened Window", null);
btn.setEnabled(true);
window.hide();
}
});
}
});