Vaadin 8.4.0 Modal for save confirmation after grid buffer save - vaadin8

We are using a grid to present some data. This grid is not using a data provider but setting its items.
We are working on buffered modd, but we still want to show a modal informing what are we about to save, with the posibility to save or cancel.
SaveEditor method has been removed from grid class in our current version (8.4.0), so cant do it that way.
I have come to a close solution but with some remaining problems.
I have extended grid component to be able to create my own editor:
public class MyGridComponent extends Grid<MyData> {
public MyGridComponent (Class<MyData> beanType) {
super(beanType);
}
#Override
protected Editor<MyData> createEditor() {
return new MyGridEditor(this.getPropertySet());
}
}
On my editor I have overriden following methods:
#Override
protected void doEdit(OutcomeWagerLimit bean) {
copyMyBean = bean;
super.doEdit(bean);
}
#Override
public boolean save() {
String desc = copyMyBean.getDescription();
StringBuilder captionBuilder = new StringBuilder()
.append("Save ")
.append(desc)
.append("?");
StringBuilder messageBuilder = new StringBuilder()
.append("Do you really want to save ")
.append(desc)
.append("?");
openConfirmMsgBox(captionBuilder.toString(), messageBuilder.toString(),() -> super.save(), ()->super.cancel());
return true;
}
With this code clicking on save opens my confirmation modal. If clicking on save, everything works flawlessly, but clicking on my modal cancel which will call to EditorImpl.cancel() method, acts in a weird way. Clicking cancel on my modal will close edition mode, but if I edit again any other row (double clicking on it) grid's save and cancel buttons (not the modal ones) stop working. Not launching any request from client to vaadin's servlet.
Does anyone know any possible solution to this or a better way to reach what I'm trying to achieve?
Thanks in advance

Morning,
Just managed to do it. Since not using dataprovider but normal list, I am the one responsible of saving data in other saveEventListener. That is the moment to present modal and in the "ok" case persist it in database.
So there is no need to override EditorImpl save method and do it in a saveEventListener.
Thanks

Related

what is the best practice to use in filling a form using selenium webdriver maven project?

I have a form to be filled with about 10 or more fields and I want to read the data from excel sheet, so what is the best practice to do this?
In my opinion the best practice is to work step by step, module by module.
If the form has text elements - first click on them, later send keyboards strokes to them. Later change selector and at the end click buttons like submit:
#Test
public void clickElementByID(#Optional String elementID) {
// click element here
}
#Test
public void changeTextOnLastClicked( #Optional String textToType) {
// keyboard action here
}
#Test
void clickButtonByID(#Optional String buttonID) {
// click button by I
}
Using this practice - if the form structure will change in the future - it will be easy to rearrange the test.

JavaFX: Prevent selection of a different tab if the data validation of the selected tab fails

I'm creating a CRUD application that store data in a local h2 DB. I'm pretty new to JavaFX. I've created a TabPane to with 3 Tab using an jfxml created with Scene Builder 2.0. Each Tab contains an AncorPane that wrap all the controls: Label, EditText, and more. Both the TabPane and the Tabs are managed using one controller. This function is used to create and to update the data. It's called from a grid that display all the data. A pretty basic CRUD app.
I'm stuck in the validation phase: when the user change the tab, by selecting another tab, it's called a validation method of the corresponding tab. If the validation of the Tab fails, I want that the selection remains on this tab.
To achieve this I've implemented the following ChangeListener on the SelectionModel of my TabPane:
boolean processingTabValidationOnChange = false;
tabPane.getSelectionModel().selectedIndexProperty().addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> ov, Number t, Number t1) {
if (processingTabValidationOnChange == false) {
boolean success;
switch (t.intValue()) {
case 0: success = validationTab1Passed();
break;
case 1: success = validationTab2Passed();
break;
case 1: success = validationTab3Passed();
break;
default: success = false;
}
if (success == false) {
processingTabValidationOnChange = true;
// select the previous tab
tabPane.getSelectionModel().select(t.intValue());
processingTabValidationOnChange = false;
}
}
}
});
I'm not sure that this is the right approach because:
The event changed is fired two times, one for the user selection and one for the .select(t.intValue()). To avoid this I've used a global field boolean processingTabValidationOnChange... pretty dirty I know.
After the .select(t.intValue()) the TabPane displays the correctly Tab as selected but the content of the tab is empty as if the AnchorPane was hidden. I cannot select again the tab that contains the errors because it's already selected.
Any help would be appreciated.
Elvis
I would approach this very differently. Instead of waiting for the user to select a different tab, and reverting if the contents of the current tab are invalid, prevent the user from changing tabs in the first place.
The Tab class has a disableProperty. If it is set to true, the tab cannot be selected.
Define a BooleanProperty or BooleanBinding representing whether or not the data in the first tab is invalid. You can create such bindings based on the state of the controls in the tab. Then bind the second tab's disableProperty to it. That way the second tab automatically becomes disabled or enabled as the data in the first tab becomes valid or invalid.
You can extend this to as many tabs as you need, binding their properties as the logic dictates.
Here's a simple example.
Update: The example linked above is a bit less simple now. It will dynamically change the colors of the text fields depending on whether the field is valid or not, with validation rules defined by bindings in the controller. Additionally, there are titled panes at the top of each page, with a title showing the number of validation errors on the page, and a list of messages when the titled pane is expanded. All this is dynamically bound to the values in the controls, so it gives constant, clear, yet unobtrusive feedback to the user.
As I commented to the James's answer, I was looking for a clean solution to the approach that I've asked. In short, to prevent the user to change to a different tab when the validation of the current tab fails. I proposed a solution implementing the ChangeListener but, as I explained: it's not very "clean" and (small detail) it doesn't work!
Ok, the problem was that the code used to switch back the previous tab:
tabPane.getSelectionModel().select(t.intValue());
is called before the process of switching of the tab itself it's completed, so it ends up selected... but hidden.
To prevent this I've used Platform.runLater(). The code .select() is executed after the change of tab. The full code becomes:
//global field, to prevent validation on .select(t.intValue());
boolean skipValidationOnTabChange = false;
tabPane.getSelectionModel().selectedIndexProperty().addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> ov, Number t, Number t1) {
if (skipValidationOnTabChange == false) {
boolean success;
switch (t.intValue()) {
case 0:
success = validationTab1Passed();
break;
case 1:
success = validationTab2Passed();
break;
case 1:
success = validationTab3Passed();
break;
default:
success = false;
}
if (success == false) {
Platform.runLater(new Runnable() {
#Override
public void run() {
skipValidationOnTabChange = true;
tabPane.getSelectionModel().select(t.intValue());
skipValidationOnTabChange = false;
}
});
}
}
}
});
Anyway, if anyone has a better solution to accomplish this, you're welcome. In example using a method like consume() to prevent the tab to be selected two times. This way I can eliminated the global field skipValidationOnTabChange.
Elvis
I needed to achieve the similar thing. I've done this by changing the com.sun.javafx.scene.control.behavior.TabPaneBehaviour class by overriding selectTab method:
class ValidatingTabPaneBehavior extends TabPaneBehavior {
//constructors etc...
#Override
public void selectTab(Tab tab) {
try {
Tab current = getControl().getSelectionModel().getSelectedItem();
if (current instanceof ValidatingTab) {
((ValidatingTab) current).validate();
}
//this is the method we want to prevent from running in case of error in validation
super.selectTab(tab);
}catch (ValidationException ex) {
//show alert or do nothing tab won't be changed
}
}
});
The ValidatingTab is my own extension to Tab:
public class ValidatingTab extends Tab {
public void validate() throws ValidationException {
//validation
}
}
This is the "clean part" of the trick. Now we need to place ValidatingTabPaneBehavior into TabPane.
First you need to copy (!) the whole com.sun.javafx.scene.control.skin.TabPaneSkin to the new class in order to change its constructor. It is quite long class, so here is only the part when I switch the Behavior class:
public class ValidationTabPaneSkin extends BehaviorSkinBase<TabPane, TabPaneBehavior> {
//copied private fields
public ValidationTabPaneSkin(TabPane tabPane) {
super(tabPane, new ValidationTabPaneBehavior(tabPane));
//the rest of the copied constructor
}
The last thing is to change the skin in your tabPane instance:
tabPane.setSkin(new ValidationTabPaneSkin(tabPane));

How do I handle double click on a GWT web application running on a touch device?

I have built a GWT (2.5) web application that, among other things, uses a DataGrid. I have used addDomHandler to add a DoubleClickEvent to select a row and perform an action, and it works great on the desktop. However, when I run the web application on a touch device, the double click zooms the screen instead. Is there are proper way to handle that? I would prefer to override the default behavior of zooming, but I have no idea where to begin. I suppose a long press might be more appropriate, but I have no idea where to begin with that either.
The code:
_dataGrid.addDomHandler(new DoubleClickHandler() {
#Override
public void onDoubleClick(DoubleClickEvent event) {
// Do something exciting here!
}
}, DoubleClickEvent.getType());
The only idea I have it that stopping the propagation of the DOM event may prevent the default zoom behavior.
Although I'd be curious is it registers as a double-click event at all on a touchscreen device. Would be worth trying just putting the handler on Root and seeing if you can even catch the event.
Also try this: just have your application catch -any- DOM event and simply write the name out somehow. That way you, should find out what event is triggering (might be one for long touch!) and can write a handler for that.
OK, I found a solution, but it's probably pretty unique to my situation. What I did was keep the double click handler and I also implemented a slow double click. In other words, if you select the same row of the DataGrid twice in sequence, no matter how fast you do it, then the application interprets that as a double click. Here is the code:
result.addDomHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
Scheduler.get().scheduleDeferred(new Command() {
public void execute () {
if (result.isDoubleTap()) {
// Do the same thing a a double click.
}
}
});
}});
I had a problem with the click handler being fired before the selection change handler, so I had to defer the processing of the click event with the Scheduler. The "result" is a Composite that contains the DataGrid and some other stuff, and in each "result" I store the last selected item in a private variable. Then in IsDoubleTap() all I do is see if the current selection is the same as the last one:
public boolean isDoubleTap() {
boolean result = false;
String selected = getSelected();
if (_lastSelect != null && selected != null && selected.equals(_lastSelect))
result = true;
_lastSelect = selected;
return result;
}
So effectively if you do a normal double click or a slow double click you get the same action. I'm just glad that while I use this result object many places, it is the only place that I use a double click. And I would REALLY like to have a conversion with the committee that decided overriding standard double click behavior with a touch device was a GOOD thing.

Double-click seems to disrupt Wicket-Spring injection

Our application uses a Wicket front-end, with Spring injection to load our DOAs and manage transactions.
We have discovered that several of our users are double clicking on links/buttons and this is
somehow disrupting the Spring injection, so that subsequent calls to whateverDao.doStuff(obj) throw N.P.E. This
application runs on our client's internal network, and for now, we have politely requested that the
client spread the word amongst their team that single clicks work on all features. But it is apparent
that this is becoming a problem for them.
A common use case involves a "Search" screen showing a list of all Foo objects currently in the system,
which can be filtered via search parameters if desired, and when an item is clicked the user is taken to
a detail page for that specific Foo, initially in a read-only mode. Next, the user may click an
"Edit" button in the corner to switch to edit mode. Then, the user might make some changes and
click "Save" (or possibly click "Delete" to remove the item.)
This scenario involves DAO calls at up to three steps:
1. On the search page, when the item is clicked, to load basic details of that item.
2. On the detail page in read-only mode, when edit is clicked, to load complete details of that item.
3a. On the detail page in edit mode, when save is clicked, to persist the changes.
3b. On the detail page in edit mode, when delete is clicked, to delelte.
In any of these cases, if the user double clicked on the previous step, the next step produces
the error. The reproducablitiy is about 33% with some variations between browsers and OSs.
Any insight on preventing this?
In the samples below, BasePage is our custom extension of Wicket's WebPage containing our menus
and other common page elements, and PageType is an enumeration of CREATE, EDIT, and READ_ONLY details.
Sample code for search page (Java shown, HTML is what you expect):
import org.apache.wicket.spring.injection.annot.SpringBean;
// and other imports
public class FooManagerPage extends BasePage {
#SpringBean
private transient FooDao fooDao;
public FooManagerPage() {
SortableDataProvider<Foo> provider = new SortableDataProvider<Foo>(fooDao);
add(new FeedbackPanel("feedback");
final Form<Foo> searchFooForm = new Form<Foo>("searchFooForm",
new CompoundPropertyModel<Foo>(new Foo()));
// the form's search parameter's go here
// with a 'search' button that filters table below
add(searchFooForm)
List<IColumn<Foo>> columns = new ArrayList<IColumn<Foo>>();
columns.add(new PropertyColumn<Foo>(Model.of("Name"), "name", "name"));
// a couple other columns here
DataTable fooTable = new AjaxFallbackDefaultDataTable<Foo>("fooTable", columns, provider, 10){
#Override
protected Item<Foo> newRowItem(String id, int index, final IModel<Foo> model){
Item<Foo> item = super.newRowItem(id, index, model);
item.add(new AjaxEventBehavior ("onclick") {
#Override
protected void onEvent(AjaxRequestTarget target) {
Foo foo = fooDao.load(model.getObject().getId());
setResponsePage(new FooViewPage(foo, PageType.READ_ONLY));
}
});
return item;
}
};
add(fooTable);
}
}
Sample code for view page (Java shown, HTML is what you expect)::
// several imports, including Spring Bean
public class FooFormPage extends BasePage {
#SpringBean
private transient fooDao fooDao;
public FooFormPage(final Foo foo, PageType type) {
Form<Foo> fooForm = new Form<Foo>("fooForm",
new CompoundPropertyModel<Foo>(foo));
// all of Foo's input elements go here
// are enabled or disabled and sometimes invisible based on PageType
add(fooForm);
SubmitLink submitButton = new SubmitLink("save", fooForm){
#Override
public void onSubmit() {
super.onSubmit();
//***** A double click on the Edit button can cause fooDao to be N.P.E. here *****
fooDao.save(createInitiativeForm.getModelObject().getId());
changePageType(PageType.VIEW, createFooForm.getModelObject());
}
};
add(submitButton);
AjaxLink<Void> editButton = new AjaxLink<Void>("edit"){
#Override
public void onClick(AjaxRequestTarget target) {
// reload the item from DB
//***** A double click on Search Page item will cause fooDao to be N.P.E. here *****
Foo foo = fooDao.load(fooForm.getModelObject().getId());
setResponsePage(new FooPage(foo, PageType.EDIT));
}
};
add(editButton);
// some stuff here that makes save button invisible in READ_ONLY, and Edit visible only in READ_ONLY
// delete button is similar (visible only in CREATE)
}
}
The dependency fields should not be marked as transient, they should be serialized along the page. The wicket-spring module injects serializable proxies into #SpringBean-annotated fields at component/page creation time, so that they can be safely serialized, without worry about serializing the dependencies themselves.

Serializing data by GameStateManagment on Windows phone

I am trying to serialize my game data. In case the user presses the Windows button, everything should be saved. I know that we should override the OnExiting event in the game class. but am using the Game State Management , I want to serialize game data in my GamePlayScreen class. I did override the Serialize and DeSerialize methods, but they didnt work.
hers my code:
public override void Serialize(Stream stream)
{
gameState.HumanPlayer = HumanPlayer;
gameState.Player1 = AIPlayer1;
gameState.Player2 = AIPlayer2;
gameState.Player3 = AIPlayer3;
XmlSerializer serializer = new XmlSerializer(typeof(GameState));
serializer.Serialize(stream, gameState);
base.Serialize(stream);
}
public override void Deserialize(Stream stream)
{
XmlSerializer serializer = new XmlSerializer(typeof(GameState));
gameState = (GameState)serializer.Deserialize(stream);
if (gameState.HumanPlayer != null)
HumanPlayer = gameState.HumanPlayer;
if (gameState.Player1 != null)
AIPlayer1 = gameState.Player1;
if (gameState.Player2 != null)
AIPlayer2 = gameState.Player2;
if (gameState.Player3 != null)
AIPlayer3 = gameState.Player3;
base.Deserialize(stream);
}
I tried to create my own IsolatedStorageFile instead of the provided stream object, but it didnt work.
I tried to write the same code in the Load and Unload event. it works fine there, but in case of pressing the back button. i need to serialize if the user pressed the windows button or the search button.
It looks like you need to handle the OnDeactivated and OnActivated events. Just do the same thing as is done in the OnExiting event and the Constructor. I would have thought the sample would do this as proper handling of tombstone/rehydrate is such a big thing for WP7, however it seems it has been neglected. Note that OnActivated is NOT called when the app is launched and OnDeactivated is NOT called when the app is closed manually or exited using the Back button.
Note that Activated and Deactivated area also available as events on PhonApplicationServices.Current, along with Launching and Closing, which are ONLY called on actual open and exit situations.
EDIT
Ok, I take it back. OnDeactivated and OnActivated are not required. It seems that OnExiting is fired for both Deactivate and Exit scenarios. I downloaded the sample you linked (XNA4 WP7, not Mango version) and put this code into the GameplayScreen:
public override void Serialize(System.IO.Stream stream)
{
System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(string));
serializer.Serialize(stream, "Blah de blah blah");
base.Serialize(stream);
}
public override void Deserialize(System.IO.Stream stream)
{
System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(string));
string testStr = (string)serializer.Deserialize(stream);
base.Deserialize(stream);
}
A break point shows that the Deserialize method is being hit functioning correctly, so your problem must be in how you apply your loaded data, or perhaps you've edited other code that has broken it.

Resources