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.
Related
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
I have three fragments in a view pager:
A -> B -> C
I would like to get the strings of my two edittexts in Fragment A on swipe to Fragment B to show them in Fragment B. The edittext data may be changed up until the swipe.
Someone has suggested listening for typing and sending data after each one, but the callbacks I know for that change state EVERY key click (which can be expensive). How do I this without using buttons, since their right next to each other, for a more delightful experience?
You can check the data of the EditText on swipe ; if it's not null, then you can send it to any other fragment using Bundle since you are dealing with fragments
With help from #I. Leonard I found a solution here.
It was deprecated so I used the newer version. I put the below code in my fragment class because I needed access to the data without complicating things. It works like a charm!
On the page listener callback, I suggest, calling an interface for inter-fragment communication to do your actions on your home activity or to call the appropriate fragment that can do the work, from the activity.
// set content to next page on scroll start
vPager = (ViewPager) getActivity().findViewById(R.id.viewpager);
vPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
}
#Override
public void onPageScrollStateChanged(int state) {
if (state == ViewPager.SCROLL_STATE_SETTLING) {
// ViewPager is slowing down to settle on a page
if (vPager.getCurrentItem() == 1) {
// The page to be settled on is the second (Preview) page
if (!areFieldsNull(boxOne.getText().toString(), boxTwo.getText().toString()))
// call interface method in view pager activity using interface reference
communicator.preview(boxOne.getText().toString(), boxTwo.getText().toString());
}
}
}
});
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));
I am developing a wizard using eclipse plugin development.
Requirement:
I have to create a context menu that needs to get populated as soon as the user right clicks on the source folder in java project. once the User performs the first step my handler needs to get the selected src folder in my wizard. My wizard contains a treeviewer where i need to get the selected src folder packaged.
My analysis:
i have my handler class that gets the selected packages
SampleHandler.java
public Object execute(ExecutionEvent event) throws ExecutionException {
shell = HandlerUtil.getActiveShell(event);
// Initializing workbench window object
IWorkbenchWindow window = (IWorkbenchWindow) HandlerUtil.getActiveWorkbenchWindow(event);
ISelection sel = HandlerUtil.getActiveMenuSelection(event);
final IStructuredSelection selection = (IStructuredSelection) sel;
Object firstElement = selection.getFirstElement();
if (firstElement instanceof IPackageFragment) {
// Get the selected fragment
IPackageFragment packageFragment = (IPackageFragment) firstElement;
modelPackage = packageFragment.getElementName();
boolean a =!ProjectResourceHelper.isEntityBasePackage(modelPackage);
if(a == true){
MessageDialog.openInformation(shell, "Warning", "Please click from entity base package");
Shell shell = HandlerUtil.getActiveShell(event);
GreenWizard wizard = new GreenWizard();
WizardDialog dialog = new WizardDialog( part.getSite().getShell(), wizard);
dialog.create();
dialog.open();
return null;
}
try{
window.run(true, true, new IRunnableWithProgress(){
#Override
public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
monitor.beginTask("Layer codes are being generated...", 1);
// Invocation of generate layers method
monitor.worked(1);
// Done with operation completion.
monitor.done();
}
});
}
catch(InvocationTargetException ite){
MessageDialog.openError(shell, "Greenfield Code Generation Exception", ite.getMessage());
}
catch (InterruptedException ie) {
MessageDialog.openError(shell, "Greenfield Code Generation Exception", ie.getMessage());
}
}
I have my main wizard class that is called within this method.
GreenWizard wizard = new GreenWizard();
and my main wizard in return calls my wizard page where i need to get the selection performed on right click by the user.
My Wizardpageclass
public GenerateGreenfieldLayer(IWorkbench workbench,
IStructuredSelection selection) {
super("Greenfield");
setImageDescriptor(ResourceManager
.getImageDescriptor("\\icons\\greenfield-new-wiz.png"));
setTitle("GreenField Generate layer");
setDescription("Select specfic class to grenerate Layers");
}
/**
* Create contents of the wizard.
*
* #param parent
*/
#Override
public void createControl(Composite parent) {
Composite container = new Composite(parent, SWT.NULL);
setControl(container);
container.setLayout(new GridLayout(2, false));
final CheckboxTreeViewer treeViewer = new CheckboxTreeViewer(container,
SWT.BORDER);
tree = treeViewer.getTree();
tree.setToolTipText("Choose package");
GridData gd_tree = new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1);
gd_tree.widthHint = 280;
gd_tree.heightHint = 140;
tree.setLayoutData(gd_tree);
treeViewer.setContentProvider(new GreenfieldTreeContentProvider());
treeViewer.setLabelProvider(new WorkbenchLabelProvider());
treeViewer.addSelectionChangedListener(new ISelectionChangedListener () {
public void selectionChanged(SelectionChangedEvent event) {
}
});
}
Can anyone please guide me how to get the selection from object method and pass as treeviewer initial input in my wizard page.
Please correct me if i am missing any steps as i am very new to this.
Thanks in advance
You should separate the code into the following pieces and dataflows:
Handler: get the selection and create the wizard and wizard dialog (as you do already)
Handler->Wizard: use the Wizard's constructor or a custom init(foo) method (which you call from the handler) to set the selected object (or whatever you want to pass as initial data) from the handler
Wizard->WizardPage: When creating the Wizard, instantiate the WizardPage(s) and pass the selection to the wizard pages. (If you need a more complex model which is shared between the Wizard and its pages consider creating an instantiating a simple value-holder class as your wizard model; i.e., a simple java class with your data and getters/setters. That object can then be shared across the pages if you pass it to every page's constructor)
WizardPage: create UI for wizard page, let user modify the model
WizardPage->Wizard: if you do not use the shared wizard model via a value-holder class, have a getXxx() method to let the wizard access the user's input from the page
Wizard: implement Wizard.performFinish() to do the work at the end of the wizard using getContainer().run() instead of having the window.run() call in your handler.
I'd like to pair a Model with it's View through an interface. I want to control when and how often the view is updated. So something like PropertyChangeListener wouldn't work well (where an event is fired after each property is set).
I'm not developing for a specific GUI framework. The goal here is the ability to swap out different GUI front ends (right now for testing, but might be useful later for different versions of the app). These might be Swing, or it might be a web browser (via GWT, for example).
Below is my approach. The view implements an interface to provide a method to update. This is triggered by the controller when it determines it's done updating the model. This still feels ok to me, since the Controller is only interacting with the view through the model, the controller is not dependent on a particular implementation of the View.
So, I guess my question(s) are
does this work well?
Is this standard practice?
Does this pattern have a name?
Rough code sample (Java):
// Controller, manages Items (the model)
class ItemList {
void addItem(Item item) {
}
void doStuffWithItems() {
// perform some set of operations, such as sorting or layout
for (Item item : items) {
// ....
}
// now with everything in it's final position:
for (Item item : items) {
item.updateView();
}
}
}
// Model
class Item {
private int top;
private int left;
private int width;
private int height;
// Can remember it's previous position/size:
public void savePostion() {
}
// And recall it for the Controller to use:
public public Position getSavedPosition() {
}
// Plus some other useful functions:
public boolean intersectsWith(Item other) {
}
public void updateView() {
this.view.update();
}
void setView(ItemView view) {
this.view = view;
}
}
// Interface used by View implementations
public interface ItemView {
// Trigger the view to reflect the current state of the model
void update();
}
// Example, as a Swing component
class ItemComponent extends JComponent implements ItemView {
private Item item;
public ItemComponent(Item item) {
this.item = item;
item.setView(this);
}
// ItemView#update
public void update() {
// update the component's size/position
setBounds(new Rectangle(item.getLeft(), item.getTop(), item.getWidth(), item.getHeight()));
}
#Override
public void paint(Graphics g) {
// ...
}
}
I would avoid forcing the View to implement an interface only for change notification. Create a separate "update now" event on the model instead.
The model should not be controlling or know about the view directly. The view should register a callback with the controller so the controller can tell the view when to update, that's why its the controller. You could have the model allow external listeners for a modelChangedEvent. Then the view could register with the model in that respect without the model knowing there was a view. See the J2EE blueprint for MVC and how there is an indirect event notification of state change in the model.
For traditional applications that run on the desktop of a computer I recommend variants of the Passive View. The class responsible for creating and managing the form is a thin shell that passes events to the UI Object. The UI_Object interact with the form via a interface. In term the UI Object implements a UI_View Interface and registers itself with a View Controller that is situated lower in the object hierarchy.
The UI_Object then execute object implementing the Command Pattern which modifies the model. The command object can interacts with the various views via the interfaces exposed by the View Control.
What this does is allow you to rip off the form classes and replace them with stub classes that implement the form interfaces. The stub classes are used for automated testing especially for integration tests.
The interfaces precisely define the interaction between the Form, the UI_Object, Commands and the views. They can be designed to be relatively language agnostic so they can make porting between platform easier.
What you are missing in your example are command objects. You need this structure
ItemViewForms
ItemViewImplementation
ItemViewFormInterface
ItemViewCommands
ItemViewInterface
MyModel
Incorporate ItemList in the ItemViewImplementation
ItemComponent would register with the ItemViewImplementation using the ItemViewInterface.
The sequence of events would look something like this
User wants to update the Item
Clicks on the UI (assuming that UI
involves clicking with a mouse)
The Form tells the
ItemViewImplementation through the
ItemViewInterface that X has been
done with Y parameters.
The ItemViewImplementation then
creates a command object with the
parameters it needs from Y.
The Command Object take the Y
Parameters modifies the model and
then tells the
ItemViewImplementation through the
ItemViewInterface to update the UI.
The ItemViewImplementation tells the
ItemViewForms to update the UI
through the ItemViewFormInterface.
The ItemViewForms updates.
The advantage of this approach is that the interaction of each layer is precisely defined through interfaces. The Software ACTIONS are localized into the command objects. The Form layers is focused on display the result. The View layer is responsible for routing actions and response between the Commands and Forms. The Commands are the only things modifying the model. Also you have the advantage of ripping off the Forms Implementation to substitute any UI you want including mock object for unit testing.