How to sync view with model changes / rerender - java

I have a form that contains a text field input that is bound to a field in a model object. When a user enters data into that text field it can affect multiple other fields/components within the form based on calculations. My business logic is encapsulated in a service layer.
Theoretically the way I want this to work is that when the user updates the field, an ajax call is made to the server (on keypress) where the logic from the service is applied and all affected fields are updated on the model object. Once the model has been updated on the server, I want to sync the current state of the model object to the screen.
Is the only way to do this to refresh the entire screen? Is there a way to re-render only the components bound to the fields in the model that have changed? Also, if I refresh the screen, the field that is being edited is updated as well, is there a way to exclude this?
I am interested in any best practices for this technique.
Thanks

I assume from your tags that you're using wicket. Wicket uses Behaviors for this purpose. In the following example you will update both DropDownChoices if the input of the TextField changed. You just need to add those controls to the AjaxRequestTarget. Models of both DropDownChoices will be reloaded and rerendered at the frontend. Also make sure to call setOutputMarkupId(true) on controls you want to update.
TextField<String> textField = new TextField<String>("input");
Component dropDownA = new DropDownChoice<>("dropA").setOutputMarkupId(true);
Component dropDownB = new DropDownChoice<>("dropB").setOutputMarkupId(true);
textField.add(new AjaxFormComponentUpdatingBehavior("change") {
private static final long serialVersionUID = 1478280524536023725L;
#Override
protected void onUpdate(AjaxRequestTarget target) {
target.add(dropDownA);
target.add(dropDownB);
}
});

With Ajax you can update any Component you want, just make sure it has markup id (Component.setOutputMarkupId(true)).
You need to use AjaxFormComponentUpdatingBehavior or AjaxFormChoiceComponentUpdatingBehavior and in its onUpdate() callback you have to update the models of the respective FormComponents and then you can use FormComponent.this.getForm().visitChildren(FormComponent.class, IVisit) to visit all FormComponents of the current Form and add to AjaxRequestTarget only the ones you have updated.
Update
Another good solution (even better!) is to use Wicket Event bus. In the Ajax behavior you could broadcast an event:
component.send(getForm(), Broadcast.BREADTH, new UIChangedEvent(target, newValue));
where UIChangedEvent is your own class/POJO that brings the new value of the updated component and the AjaxRequestTarget.
Then the other FormComponents could override #onEvent() and use the value to calculate their new value and use the target to update themselves or not, depending on the earlier calculation.

Related

Wicket form changes removed on ajax update

I have a pretty big and complex form which contains lots of form components.
On one of the drop down fields I have added a AjaxFormComponentUpdatingBehavior to handle changes in the drop down. Based on these changes I am updating some other fields in the form, this way:
masterFooDropDown.add(new AjaxFormComponentUpdatingBehavior("change") {
#Override
protected void onUpdate(AjaxRequestTarget target) {
String value = (String) getFormComponent().getModelObject();
if (value != null) {
for (Foo foo: form.getModelObject().getFoos()) {
foo.setValue(value);
}
target.add(form);
}
}
});
The Foo fields gets updated correctly with the value from the drop down.
The problem is that any changes in the other text fields in the form is removed. I understand that this happens, since they have not been updated in the model.
How can I solve this?
Can I make all the filled in data in the form so far be written to the model in some way (without submitting the form)?
I need to add the whole form to the target in the ajax method since the fields that should be updated is children of the form model object and added to the form dynamically. For example, I cannot do target.add(fooFieldX) since there might be any number of "fooFields".
You can use AjaxFormComponentUpdatingBehavior/AjaxFormChoiceComponentUpdatingBehavior on all other form (choice) components so that they update the form's model object when modified.
You can use Component#visitChildren() and dynamically decide which particular form components to add to the AjaxRequestTarget, if it is easy to decide this. Even AjaxRequestTarget has method #addChildren() but it is not that flexible to filter the children components.

JFace data binding occurs after other events

I want to do something based on the model change, e.g. if user enters text in the Text SWT widget, I would like to do something based on that. But the problem is, if I use e.g. key listener on the widget, it will get called before the data binding process occurs and therefore the behaviour would'nt be consistent. One way around this is to do the logic inside the binding process, but this is not really a convinient way of handling this problem. Suggestions?
So basically, what I need is an event listener, which is triggered after the data binding occures (on some specified widget of course).
Use an IChangeListener to listen for changes on the model observable value. This will be called after the model has been updated from the target.
Something like:
IObservableValue targetOV = WidgetProperties.text(SWT.Modify).observe(text control);
IObservableValue modelOV = PojoProperties.value("your field").observe(object);
bindContext.bindValue(targetOV, modelOV);
modelOV.addChangeListener(change listener);

How to implement undo/redo in Java for MVC model?

I am having trouble understanding the undo/redo functions using UndoManager, and integrating it with the MVC model.
I am not sure where to put the various methods(in model, view or control)
and I am still not sure how to use the undo manager.
My control class implements UndoableEditListener
It creates:
private UndoManager manager = new UndoManager();
and in:
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equals("Undo")) {
try {
manager.undo();
} catch (CannotUndoException ex) {
ex.printStackTrace();
}
}
}
I understand up to here, but the rest I am not sure what to do. I know I will have to add more in the model and view class, but not sure where.
DO I have to have the following classes?
public class UndoAction extends AbstractAction {}
public void undoableEditHappened(UndoableEditEvent e) {
I am simply placing an integer number in a textfield, and then I want to be able to undo this.I set the number in the textfield in the view class.I want to acheive this the simplest way possible, no fancy coding! This is a minor part of my assg but I just cant get it working!!
==========================================================
Here is a more detailed description of my code, maybe it will help:
I have a model, view and control package.
Contol has:
ButtonGUIControl.java, which implements both
ActionListener and
UndoableEditListener.
final UndoManager manager = new UndoManager();
In the actionPerformed method, it calls
if (e.getActionCommand().equals("Undo")){
try {
manager.undo();
}
and in:
public void undoableEditHappened(UndoableEditEvent evt) {
manager.addEdit(evt.getEdit());
}
In the View:
Grid.java , which extends JTextField will add the following, wherever it needs to display a number on the GUI:(model is simply an instance of my Model class)
getDocument().addUndoableEditListener(new ButtonGUIControl(model));
Could it be because the UndoManager is being created in a different package? I really have no idea how to debug this anymore!!
I could post my entire code if that helps. I guess Im not sure how to integrate this with my mvc model structure.
Take a step back for a second. The whole idea here is that a user will use your app and will make a series of changes to something. A text editor is a good example. You can insert characters and lines, delete them again, replace text with other text, scroll the text, etc. In order to support this with MVC you have a model that holds state and a View that displays it.
Your first instinct might be to have the view directly access the model, and then refresh the view every time the user makes a change, but it's very hard to undo those changes with that implementation. Instead, you encode every kind of change the user can make in classes that are able to perform that change and can later undo that change.
For example, an action that inserts text would be implemented by a class that knows the character offset of the insertion point and the string of characters that is to be inserted. The perform action would insert the string at the offset and the undo action would remove the right number of characters after that insertion point. You'd have a different class that would handle deletion, another to handle scrolling etc.
Every time the user takes some action, the view would construct one of these UndoableEdit classes and would tell the instance to run itself (redo()). Once executed, you put that UndoableEdit at the end of a list of UndoableEdit instances that represent all of the actions the user has taken so far. This list makes it very easy to support any sequence of undo requests, redo requests and actual edit actions (resulting in more UndoableEdit's being put on the list).
So back to your question. If your app needs to support undo and redo, then you'll need to implement an UndoManager which simply manages the list of UndoableEdit's and performs undo and redo as necessary. You also have to implement a whole bunch of UndoableEdits, one for each kind of thing your user will do against the UI. As for a listener, I can't see that you really need to do that one.
If you need only simple undo/redo, you can use UndoManager as it is, you don't need to subclass or customize it in any way.
JTextField (more specifically its model, the Document) has some built-in support for undo, which means you don't need to write UndoableEdit implementations either, the UndoableEdit objects will be automagically created for you (actually AbstractDocument.DefaultDocumentEvent implements UndoableEdit).
Full simple working example is here

How to send signals between java swing forms?

I understand the "How to Use Actions" tutorial, but can't figure out how to make it work between multiple JFrame forms.
I tried making updateComboBox method public static so it could just be accessed from other forms,
but the NetBeans IDE refused to allow it since the auto-generated non-static variable jComboBox cannot be referenced from a static context.
The primary form contains a JComboBox that needs to be modified based on user input (menus, buttons, text fields, etc.).
Some of the widgets are on the primary form, others are on secondary forms.
For example, the primary form makes a secondary form visible; the secondary form makes some changes to the configuration; then the user hides the secondary form by pressing the SAVE button.
How can the secondary form best let the primary form know that the configuration has been updated and changes now need to be applied to the JComboBox?
Is an Action a good fit for this or would some other method be more appropriate?
public class Controller extends javax.swing.JFrame {
...
private javax.swing.JComboBox jComboBox;
private void updateComboBox() {
String[] names = Configuration.getNames();
for (String n : names) {
jComboBox.addItem(n);
}
...
How about a controller class which will hold references to your frames and manage the signaling you need? You can also take a look at the Observer pattern, where your primary form will listen the changes made by the secondary form.
Since the second form does some setting modifications, I would assume that while the user is messing around with the settings, you would like to disable the user from using the main application.
What you can do is to create the first form and then create the settings form from within the first form. You can then use thread synchronization (such as locks) to make the 1st form wait until the user has pressed the "OK Button" on the settings form. Once that the "OK button" is pressed, the lock is removed and the 1st form will continue executing by calling methods pertaining to the 2nd form so that it can access the data that the user has just entered.

How do I have multiple components respond to a single event in JSF?

Here's the sit:
I have a JSF component which is basically a list of 'documents'
I have any number of document viewer components on the same page.
None of these components "know" about each other. In other words, they cannot be configured at design time to link to each other or anything like that.
When the user clicks a document link I wish each one of the document viewer components to be notified.
Basically the idea would be to have the document viewers publish the fact that they listen for a certain type of event ("DocumentSelectedEvent" say) which the doc list component would fire.
I can think of ways of doing this that are not JSF specific, but I'm wondering if the JSF event model can handle that sort of thing.
Anyone have any ideas?
I don't think there's a way of doing that with the standard JSF event model.
Is there any way you can bind the components to a backing bean? That way when an event happens you can just go through your list of components and notify each of them.
You need to just bind the components with a backing bean and use a ValueChangeListener to notify the backing bean. The listener method could change the state of the other components which are tied to respective UI components.
Are you trying to do it in a "Ajax" way without page being explicitly submitted?
ValueChangeEvent
I do not know how you implemented your document list but if it were say a dropdown or any other multi item list component you can do an Value Change Event and force a submit on change for the component. Then in the page code backing bean you can call the methods for your viewers to load whatever you like.
In your jsf you just specify the value change handler you wrote in the backing bean.
/**
* Handle document click value change.
*
* #param valueChangedEvent the value changed event
*/
public void handleDocumentSelect(ValueChangeEvent valueChangedEvent) {
String selectedDocument = valueChangedEvent.getNewValue();
doDocViewer1DisplayMethod(selectedDocument);
doDocViewe2DisplayMethod(selectedDocument);
}
Modify your jsf tag to use your new value change event handler and force the submit.
<f:componentTag
attr=xxx
attr=xxx
valueChangeListener="#{pc_BackingBean.handleDocumentSelect}"
onChange=submit();>

Categories

Resources