问题
Let me start with I have a solution, but I don't really think it is elegant. So, I am looking for a cleaner way to do this.
I have an EntityProxy displayed in a view panel. The view panel is a RequestFactoryEditorDriver only using display mode. The user clicks on a data element and opens a popup editor to edit a data element of the EntityProxy with a few more bits of data than is displayed in the view panel. When the user saves the element I need the view panel to update the display.
I ran into a problem because the RequestFactoryEditorDriver of the popup editor flow doesn't let you get to the edited data. The driver uses the same passed in context that you use to send data the server, Yet the context returned out of flush only allows a Receiver<Void>
even if you cast it to the type of context you stored in the editor driver in the edit() call. [It doesn't appear to send and EntityProxyChanged event either, so I couldn't listen for that and update the display view. - scratch this - I see now this event is not for this use case]
The solution I found was to change my domain object persist to return the newly saved entity. Then create the popup editor like this
editor.getSaveButtonClickHandler().addClickHandler(createSaveHandler(driver, editor));
// initialize the Driver and edit the given text.
driver.initialize(rf, editor);
PlayerProfileCtx ctx = rf.playerProfile();
ctx.persist().using(playerProfile).with(driver.getPaths())
.to(new Receiver<PlayerProfileProxy>(){
@Override
public void onSuccess(PlayerProfileProxy profile) {
editor.hide();
playerProfile = profile;
viewDriver.display(playerProfile);
}
});
driver.edit(playerProfile, ctx);
editor.centerAndShow();
Then in the save handler I just fire() the context I get from the flush(). While this approach works, it doesn't seem right. [It would seem I should subscribe to the entitychanged event in the display view and update the entity and the view from there. - once again scratch, same reason as before ] Also this approach saves the complete entity, not just the changed bits, which will increase bandwidth usage.
What I would think should happen, is when you flush the entity it should 'optimistically' update the rf managed version of the entity and fire the entity proxy changed event. Only reverting the entity if something went wrong in the save. The actual save should only send the changed bits. In this way there isn't a need to refetch the whole entity and send that complete data over the wire twice.
Is there a better solution?
回答1:
You don't seem to really understand the details of what's going on with RF; plus, your terminology doesn't really help in understanding (flush vs. fire).
A proxy in RF is a snapshot of the server's state at the time you retrieved it. You can do whatever you want with the entity elsewhere in your app (through other proxies), your proxy won't ever change to reflect those modifications.
An EntityProxyChange
event is dispatched on the client-side (for an entity that is already known by the server and has been sent from the client) when the server has detected that it has changed: either its version (as returned by getVersion
on the Locator
) has changed, or it has been deleted (as told by the isLive
method of the Locator
). If you don't use a Locator
, it'll use the getVersion
of the entity and isLive
will be replaced by find
ing the entity by its ID (as returned by its getId
method) and checking for null
(this is also the default implementation of isLive
in the Locator
).
In your case, if you don't see an EntityProxyChange
being dispatched, then check that you correctly update the entity's version.
Finally, RF always sends a diff of your changes to the server (except for a ValueProxy
, in which case a diff would have no meaning). As for retrieving data, it doesn't retrieve linked proxies by default, unless you explicitly ask for them using with
; and this is independent of what you possibly sent about the entity.
In your case, to update the view panel, you have 3 possibilities:
- retrieve the proxy back from the server (listening to
EntityProxyChange
events or after an explicit signal from your popup; you could use thefind
method ofRequestContext
with the proxy'sstableId
as argument, and the appropriatewith
for the properties you need).
It's a bit inefficient as you're doing a second HTTP request, but on the other hand it can handle changes from elsewhere in your app (they'll fireEntityProxyChange
events too) - retrieve the updated proxy in the same HTTP request as the one you use to save it: have the
save
method of your request context return the saved entity, or call thefind
method in the same request context to batch thesave
andfind
together in the same HTTP request.
That's what you did. It sends a diff of the changes and retrieves the properties needed by your view panel. One could say it has the drawback of tightly coupling your popup and your view panel, it's up to you to decide whether it's an acceptable trade-off. - use the entity you edited and sent to the server right in your view panel, with no extra data over the wire.
While this seems simpler, you'll miss any change that could have been done on the entity by another user (changes only the server know).
All in all, I think I'd go with the current solution. Regarding your code though, I'd launch the popup with the proxy and a callback, and leave the request context and editing editor driver as implementation details of the popup: you only need that it calls the view panel back when it's done, passing the updated proxy as an argument to the callback.
A last word regarding terminology: you flush an editor driver to copy the field's value back to the object/proxy, and (independently, but in your case sequentially) you fire a request context to send a batch of service methods and proxy changes to the server. Flushing an editor driver doesn't send anything to the server, these are distinct actions.
回答2:
I found a better solution. I make the proxy editable before calling the RequestFactoryEditorDriver edit() and save the editable proxy as my view proxy.
PlayerProfileCtx ctx = rf.playerProfile();
playerProfile = ctx.edit(playerProfile);
driver.edit(playerProfile, ctx);
Also, (and I thought I had tried this before and it didn't work but I must have done something wrong then) I can cast the context that comes back from the flush. It is the same context I sent to the driver with the edit()
call so it is safe.
PlayerProfileCtx ctx = (PlayerProfileCtx) driver.flush();
Doing this fixed the problem with rf sending the whole object with fire() and not just the diffs. I'm not sure why though. That might be a bug in the RF Editor Driver.
So now I have the data from the driver already in the view and don't have to depend on sending it back from the server. But I did register for EntityProxyChange events so I could detect and refetch if there was a conflict on the server.
来源:https://stackoverflow.com/questions/9789870/requestfactoryeditordriver-getting-edited-data-after-flush