Getting the record being edited - gwt

I am using a custom validator like as:
CustomValidator duplicateValidator;
duplicateValidator = new CustomValidator()
{
#Override
protected boolean condition(Object value)
{
getRecord();
//* .. code to validate this record here *//
}
};
But my page gets stuck in a loop, and by using Firebug, it stucks on getRecord(); part, also the getRecord() == null. Is there another way to get the record that I am editing ?

It could not stuck on getRecord method of CustomValidator because it's code is trivial - nothing could go wrong there:
/**
* #return field values for record being validated
*/
public Record getRecord() {
return record;
}
But depending on what you editing you can try to get edited record from that widget by using their specific methods.
For example for DynamicForm:
form.getValuesAsRecord()
For ListGrid:
listGrid.getEditedRecord(listGrid.getEditRow())

Related

How to refresh a FilteredItemsSelectionDialog

I have a FilteredItemsSelectionDialog shose underlying data model may change while the dialog is open. If there are structural changes, i.e. elements are added or removed, I am unable to tell the dialog to update its filtered list.
I tried calling refresh() and reloadCache() and also scheduleRefresh() (which essentially runs reloadCache() in a Job), but all methods re-use the elements that were initially added to the content provider.
What I think would solve the problem is a way to reset the content provider and have it call fillContentProvider() again. Then I could re-populate the content provider with the current state of the model. While preserving the current filter and selection, of course.
[BEGIN EDIT]
Based on Greg's answer here is what I also tried:
ItemsFilter overrideFilter;
#Override
protected ItemsFilter createFilter() {
if( overrideFilter != null ) {
return overrideFilter;
}
return new LaunchConfigItemsFilter();
}
public void forceRefresh() {
overrideFilter = new ItemsFilter() {
#Override
public boolean matchItem( Object item ) {
return false;
}
#Override
public boolean isConsistentItem( Object item ) {
return false;
}
#Override
public boolean equalsFilter( ItemsFilter filter ) {
return false;
}
#Override
public boolean isSubFilter( ItemsFilter filter ) {
return false;
}
};
applyFilter();
overrideFilter = null;
applyFilter();
}
forceRefresh() is called whenever the model structure changes. The intention is to first call applyFilter() with a filter that will never match the existing one and thus triggers fillContentProider() and then again call applyFilter() with the current filter to restore the elements that match.
But when an element is added and another is removed, the outcome is that the deleted element is still visible.
What I also tried is to let createFilter() return null for the first call to applyFilter(). This again leaves the deleted element visible.
Irrespective of whether a fitler was set or not before the elments were added and removed, at its best, the list is in a meaningful state only after I cleared and (re-)entered a filter.
[END EDIT]
To give some context, the dialog in question lists launch configurations, the code can be found here:
https://github.com/rherrmann/eclipse-extras/blob/master/com.codeaffine.extras.launch/src/com/codeaffine/extras/launch/internal/dialog/LaunchSelectionDialog.java
Does anyone know a way to force the FilteredItemsSelectionDialog to reset and refill its content provider?
If I read the code correctly calling applyFilter will rerun all the code to fill the table including calling fillContentProvider.
When checking if it needs to do anything applyFilter calls createFilter, this must return a filter and the filter's ItemsFilter.equalsFilter method must return false when compared with the previous filter.

How to validate inputs and prevent save actions using databinding in eclipse?

I want to create input forms which validate user input and prevent the model from being saved with invalid data. I have been using databinding which works up to a point but my implementation is not as intuitive as I would like.
Imagine an input which contains '123' and the value must not be empty. The user deletes the characters one by one until empty. The databinding validator shows an error decoration.
However, if the user saves the form and reloads it, then a '1' is displayed in the field - i.e. the last valid input. The databinding does not transmit the invalid value into the model.
I have a ChangeListener but this is called before the databinding so at that point the invalid state has not been detected.
I would like the error to be displayed in the UI but the model remains valid (this is already so). Also, for as long as the UI contains errors, it should not be possible to save the model.
/**
* Bind a text control to a property in the view model
**/
protected Binding bindText(DataBindingContext ctx, Control control,
Object viewModel, String property, IValidator validator)
{
IObservableValue value = WidgetProperties.text(SWT.Modify).observe(
control);
IObservableValue modelValue = BeanProperties.value(
viewModel.getClass(), property).observe(viewModel);
Binding binding = ctx.bindValue(value, modelValue, getStrategy(validator), null);
binding.getTarget().addChangeListener(listener);
ControlDecorationSupport.create(binding, SWT.TOP | SWT.LEFT);
return binding;
}
private UpdateValueStrategy getStrategy(IValidator validator)
{
if (validator == null)
return null;
UpdateValueStrategy strategy = new UpdateValueStrategy();
strategy.setBeforeSetValidator(validator);
return strategy;
}
private IChangeListener listener = new IChangeListener()
{
#Override
public void handleChange(ChangeEvent event)
{
// notify all form listeners that something has changed
}
};
/**
* Called by form owner to check if the form contains valid data e.g. before saving
**/
public boolean isValid()
{
System.out.println("isValid");
for (Object o : getDataContext().getValidationStatusProviders())
{
ValidationStatusProvider vsp = (ValidationStatusProvider) o;
IStatus status = (IStatus)vsp.getValidationStatus()
.getValue();
if (status.matches(IStatus.ERROR))
return false;
}
return true;
}
Your best bet is to steer clear of ChangeListeners - as you've discovered, their order of execution is either undefined or just not helpful in this case.
Instead, you want to stick with the 'observable' as opposed to 'listener' model for as long as possible. As already mentioned, create an AggregateValidationStatus to listen to the overall state of the DataBindingContext, which has a similar effect to your existing code.
Then you can either listen directly to that (as below) to affect the save ability, or you could even bind it to another bean.
IObservableValue statusValue = new AggregateValidationStatus(dbc, AggregateValidationStatus. MAX_SEVERITY);
statusValue.addListener(new IValueChangeListener() {
handleValueChange(ValueChangeEvent event) {
// change ability to save here...
}
});
You can use AggregateValidationStatus to observe the aggregate validation status:
IObservableValue value = new AggregateValidationStatus(bindContext.getBindings(),
AggregateValidationStatus.MAX_SEVERITY);
You can bind this to something which accepts an IStatus parameter and it will be called each time the validation status changes.

Form fields are reset on validation error

I have a rather complex form in the way that the number of form fields is flexibel. In short, the model object is a TLabel (TranslationLabel) that contains a Map of values (translations). Language here is an enum so the idea is that the number of fields (text areas) for which a translation is given depends on the values in this enum.
This is my form (simplified):
public class TranslationEditForm extends Form {
private final static List<Language> LANGUAGES = newArrayList(Language.values());
public TranslationEditForm(String id, final TranslationLabelView label) {
super(id, new CompoundPropertyModel<TranslationLabelView>(label));
ListView<Language> textAreas = new ListView<Language>("translationRepeater", LANGUAGES) {
#Override
protected void populateItem(final ListItem<Language> itemLang) {
//loop through the languages and create 1 textarea per language
itemLang.add(new Label("language", itemLang.getModelObject().toString()));
Model<String> textModel = new Model<String>() {
#Override
public String getObject() {
//return the value for current language
return label.getValue(itemLang.getModelObject());
}
#Override
public void setObject(String object) {
//set the value for current language
label.getTranslations().put(itemLang.getModelObject(), object);
}
};
itemLang.add(new TextArea<String>("value", textModel).setRequired(true));
}
};
//add the repeater containing a textarea per language to the form
this.add(textAreas);
}
}
Now, it works fine, 1 text area is created per language and its value is also set nicely; even more when changed the model gets updated as intended.
If you submit the form after emptying a text area (so originally there was a value) then of course there is a validation error (required). Normal (wicket) behaviour would be that the invalid field is still empty but for some reason the original value is reset and I don't understand why.
If I override onError like this:
#Override
protected void onError() {
this.updateFormComponentModels();
}
then it is fine, the value of the field is set to the submitted value (empty) instead of the original value.
Any idea what is causing this? What is wicket failing to do because the way I've set up the form (because with a simple form/model this is working fine as are the wicket examples)?
Posted as answer, so the question can be marked as solved:
ListView does recreate all its items at render time. This means that the validation will be broken. Have a look at API doc of the ListView
Calling setReuseItems() on the ListView solves this.
Regards,
Bert

GWT: Trouble getting value from a text box

I'm using GWT 2.4. I'm trying to submit an AJAX request with the only input being the value of a text field on the page. Here is how I attach the handler to the page's button ...
public void onModuleLoad() {
...
final com.google.gwt.dom.client.Element submitElement = Document.get().getElementById(SUBMIT_BUTTON_ID);
final Button submitButton = Button.wrap(submitElement);
...
// Add a handler to send the name to the server
GetHtmlHandler handler = new GetHtmlHandler();
submitButton.addClickHandler(handler);
}
But here's the problem. In my handler, whenever I try and get the value of the text field, it always returns the value entered in the text field when the page was first loaded, as opposed to what the most current value is ...
class GetHtmlHandler implements ClickHandler {
/**
* Fired when the user clicks on the submitButton.
*/
public void onClick(ClickEvent event) {
submitRequest();
}
/**
* Send the name from the nameField to the server and wait for a
* response.
*/
private void submitRequest() {
...
final Element nameFieldElement = DOM.getElementById(Productplus_gwt.NAME_FIELD_ID);
// This always returns an old value.
String docId = nameFieldElement.getAttribute("value");
Anyone know how I can write GWT code inside my handler to return the most current value of a text field given its page id?
Thanks, - Dave
Try using DOM.getPropertyString / DOM.getElementProperty
Following is the javadoc from GWT source for getAttribute function. It clearly says that the support for javascript's "getAttribute" function could be inconsistent for a few browsers and thus Element and subclasses should be used.
Alternatively you can use DOM.getPropertyString to fetch a value which uses object notation of javascript to get te current value
/**
* Retrieves an attribute value by name. Attribute support can be
* inconsistent across various browsers. Consider using the accessors in
* {#link Element} and its specific subclasses to retrieve attributes and
* properties.
*
* #param name The name of the attribute to retrieve
* #return The Attr value as a string, or the empty string if that attribute
* does not have a specified or default value
*/
public final String getAttribute(String name) {
return DOMImpl.impl.getAttribute(this, name);
}
I tried using javascript's "getAttribute" function to get value of a text field in IE8 and FF6. IE gave the updated value of the text field while FF did not. Here is the fiddle
http://jsfiddle.net/GvNu4/
Well like you said it's an AJAX request so whatever code you have on ... the GWT code will continue to run.
You should use the callback of the request and check the value of the nameFieldElement at that moment.

GWT RequestFactory + CellTable

Does anyone know for an example of GWT's CellTable using RequestFactory and that table is being edited? I would like to list objects in a table (each row is one object and each column is one property), be able to easily add new objects and edit. I know for Google's DynaTableRf example, but that one doesn't edit.
I searched Google and stackoverflow but wasn't able to find one. I got a bit confused with RF's context and than people also mentioned some "driver".
To demonstrate where I currently arrived, I attach code for one column:
// Create name column.
Column<PersonProxy, String> nameColumn = new Column<PersonProxy, String>(
new EditTextCell()) {
#Override
public String getValue(PersonProxy person) {
String ret = person.getName();
return ret != null ? ret : "";
}
};
nameColumn.setFieldUpdater(new FieldUpdater<PersonProxy, String>() {
#Override
public void update(int index, PersonProxy object, String value) {
PersonRequest req = FaceOrgFactory.getInstance().requestFactory().personRequest();
PersonProxy eObject = req.edit(object);
eObject.setName(value);
req.persist().using(eObject).fire();
}
});
and my code for data provider:
AsyncDataProvider<PersonProxy> personDataProvider = new AsyncDataProvider<PersonProxy>() {
#Override
protected void onRangeChanged(HasData<PersonProxy> display) {
final Range range = display.getVisibleRange();
fetch(range.getStart());
}
};
personDataProvider.addDataDisplay(personTable);
...
private void fetch(final int start) {
lastFetch = start;
requestFactory.personRequest().getPeople(start, numRows).fire(new Receiver<List<PersonProxy>>() {
#Override
public void onSuccess(List<PersonProxy> response) {
if (lastFetch != start){
return;
}
int responses = response.size();
if (start >= (personTable.getRowCount()-numRows)){
PersonProxy newP = requestFactory.personRequest().create(PersonProxy.class);
response.add(newP);
responses++;
}
personTable.setRowData(start, response);
personPager.setPageStart(start);
}
});
requestFactory.personRequest().countPersons().fire(new Receiver<Integer>() {
#Override
public void onSuccess(Integer response) {
personTable.setRowCount(response+1, true);
}
});
}
I try to insert last object a new empty object. And when user would fill it, I'd insert new one after it. But the code is not working. I says that user is "attempting" to edit a object previously edited by another RequestContext.
Dilemmas:
* am I creating too many context'es?
* how to properly insert new object into celltable, created on the client side?
* on fieldUpdater when I get an editable object - should I insert it back to table or forget about it?
Thanks for any help.
am I creating too many context'es?
Yes.
You should have one context per HTTP request (per fire()), and a context that is not fire()d is useless (only do that if you/the user change your/his mind and don't want to, e.g., save your/his changes).
You actually have only one context to remove here (see below).
Note that your approach of saving on each field change can lead to "race conditions", because a proxy can be edit()ed by at most one context at a time, and it remains attached to a context until the server responds (and once a context is fired, the proxy is frozen –read-only– also until the server responds).
(this is not true in all cases: when onConstraintViolation is called, the context and its proxies are unfrozen so you can "fix" the constraint violations and fire the context again; this should be safe because validation is done on the server-side before any service method is called).
how to properly insert new object into celltable, created on the client side?
Your code looks OK, except that you should create your proxy in the same context as the one you'll use to persist it.
on fieldUpdater when I get an editable object - should I insert it back to table or forget about it?
I'm not 100% certain but I think you should refresh the table (something like setRowData(index, Collections.singletonList(object)))
BTW, the driver people mention is probably the RequestFactoryEditorDriver from the Editor framework. It won't help you here (quite the contrary actually).