How to add properly UiHandler for BlurEvent in GWT? - java

Normally if we have some textField in GWT we can add a BlurHandler by the following code:
textField.addBlurHandler(new BlurHandler() {
#Override
public void onBlur(BlurEvent event) {
//what we need
}
});
But if we use UiBinder and our textField is annotated by #UiField and it is mentioned in our ui.xml file we can add BlurHandler by this code as well:
#UiHandler("textField")
protected void createBlurHandler(BlurEvent event) {
}
I guess I am right here because it works like this. So, the question is, can we actually define BlurHandler inside ui.xml file?
For example, it is possible to add inputMaxLength and some other attributes there, does GWT has some possibility like onChange method or are these ways that I described the only possibilities?
I would like to have something like this:
<g:TextBox ui:field="textField" onBlur="methodName" />
Is it possible?

I am pretty sure what you are asking is not possible. The problem is that you wouldn't be able to use reflection to figure out which method you want to call. However you can extends the TextBox class and use that inside your template. The extended class could have it's own properties that can be set in the template. An example is as follows where I set the default test on my own DefaultTextBox.
public class DefaultTextBox extends TextBox {
/**
* The text color used when the box is disabled and empty.
*/
private static final String TEXTBOX_DISABLED_COLOR = "#AAAAAA";
private final String defaultText;
public #UiConstructor
DefaultTextBox(final String defaultText) {
this.defaultText = defaultText;
resetDefaultText();
// Add focus and blur handlers.
addFocusHandler(new FocusHandler() {
#Override
public void onFocus(FocusEvent event) {
getElement().getStyle().clearColor();
getElement().getStyle().clearFontStyle();
if (defaultText.equals(getText())) {
setText("");
}
}
});
addBlurHandler(new BlurHandler() {
#Override
public void onBlur(BlurEvent event) {
if ("".equals(getText())) {
resetDefaultText();
}
}
});
}
public String getDefaultText() {
return defaultText;
}
#Override
public void setText(String text) {
if (text == null) {
super.setText(getDefaultText());
} else {
getElement().getStyle().clearColor();
getElement().getStyle().clearFontStyle();
super.setText(text);
}
}
public String getText() {
return super.getText();
}
/**
* This is override so that the editor framework will not get the default
* value but the actual null value when the default text is in the box.
*/
#Override
public String getValue() {
try {
return getValueOrThrow();
} catch (ParseException e) {
return null;
}
}
#Override
public void setValue(String value) {
setText(value);
}
/**
* This is overridden from the parent class because this is
* how the editor gets the value.
*/
public String getValueOrThrow() throws ParseException {
if (defaultText.equals(super.getValueOrThrow())) {
return null;
}
return super.getValueOrThrow();
}
/**
* Reset the text box to the default text.
*/
public void resetDefaultText() {
setText(defaultText);
getElement().getStyle().setColor(TEXTBOX_DISABLED_COLOR);
getElement().getStyle().setFontStyle(FontStyle.ITALIC);
}
}
Then in the template you can set properties like this.
<w:DefaultTextBox defaultText="name" ui:field="nameTextBox" />
This will also work with setters, you can set properties without having to use the #UiConstructor but in my case I wanted to make sure that there was no empty constructor for this class.

Related

JavaFX Display Enum with String field in Combobox as String (in TableView)

My goal is to display every field of an instance of a Class in a tableView. The Class has a field of type enum which has a field of type String.
The enum shall be displayed in a ComboBox as it's String field name.
Of course it also has to be editable.
Now what isn't working:
The String field of enum class is only displayed if the ComboBox is clicked on, otherwise it is the name of the enum constant. Also, if another enum in the combobox is selected, it can't be commited for edit. Clicking return doesn't deselect the Combobox neither is the method commitEdit invoked. If an other column is selected for edit, the attempted edit is cancelled.
I put some effort into trying to figure this out, so I thought maybe one could help me here.
As the original task is about much bigger classes in enterprise software, I abstracted it to ask this question.
I know I could make the column holding the enum of type String and make it work with MyEnum.values() and MyEnum.valueOf() but that could not go into production due to bad performance as the original classes are too big.
Here is my code as an example, if you don't understand the problems just try to use the combobox once and you'll see.
Class which is type of TableView:
public class MyClass {
private MyEnum myEnum;
private String string;
public MyClass(MyEnum myEnum, String string) {
this.myEnum = myEnum;
this.string = string;
}
public MyEnum getMyEnum() {
return myEnum;
}
public void setMyEnum(MyEnum myEnum) {
this.myEnum = myEnum;
}
public String getString() {
return string;
}
}
It's enum field:
public enum MyEnum {
EnumOne("First Enum"),
EnumTwo("Second Enum");
private String name;
public String getName() {
return name;
}
private MyEnum(String name) {
this.name = name;
}
}
FX App:
public class NewFXMain extends Application {
#Override
public void start(Stage primaryStage) {
ObservableList<MyClass> items = FXCollections.observableArrayList();
items.add(new MyClass(MyEnum.EnumOne, "String"));
TableView<MyClass> table = new TableView(items);
table.setEditable(true);
TableColumn<MyClass, MyEnum> myEnumColumn = new TableColumn();
TableColumn<MyClass, String> stringColumn = new TableColumn();
stringColumn.setCellFactory(TextFieldTableCell.forTableColumn());
stringColumn.setCellValueFactory(data -> new ReadOnlyStringWrapper(data.getValue().getString()));
myEnumColumn.setCellFactory((param) -> new MyEnumComboBoxCell());
myEnumColumn.setCellValueFactory(data -> new ReadOnlyObjectWrapper(data.getValue().getMyEnum()));
myEnumColumn.setOnEditCommit(
event -> {
event.getRowValue().setMyEnum(event.getNewValue());
System.out.println(event.getRowValue().getMyEnum());
});
table.getColumns().addAll(myEnumColumn, stringColumn);
StackPane root = new StackPane();
root.getChildren().add(table);
Scene scene = new Scene(root, 300, 250);
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
class MyEnumComboBoxCell extends ComboBoxTableCell<MyClass, MyEnum> {
private ComboBox<MyEnum> box;
public MyEnumComboBoxCell() {
box = new ComboBox<>(FXCollections.observableArrayList(MyEnum.values()));
box.setCellFactory(new Callback<ListView<MyEnum>, ListCell<MyEnum>>() {
#Override
public ListCell<MyEnum> call(ListView<MyEnum> param) {
return new ListCell<MyEnum>() {
#Override
protected void updateItem(MyEnum item, boolean empty) {
super.updateItem(item, empty);
if ( item != null ) setText(item.getName());
}
};
}
});
}
#Override
public void startEdit() {
super.startEdit();
setGraphic(box);
}
#Override
public void commitEdit(MyEnum newValue) {
super.commitEdit(newValue);
if ( newValue != null ) {
setText(newValue.getName());
getTableView().getSelectionModel().getSelectedItem().setMyEnum(newValue);
box.setValue(newValue);
}
}
#Override
public void updateItem(MyEnum item, boolean empty) {
super.updateItem(item, empty);
if ( empty ) {
setGraphic(null);
} else {
setGraphic(null);
setText(item.getName());
}
}
}
Instead of setting the name in updateItem use a StringConverter like:
public class MyEnumConverter extends StringConverter<MyEnum>{
#Override public String toString(MyEnum enumConstant) {
return enumConstant.getName();
}
#Override public MyEnum fromString(String string) {
return MyEnum.valueOf(string);
}
}
Then in the cell's constructor:
this.setConverter(new MyEnumConverter());
Edit: You may not #Override all of the ComboBoxTableCell's methods, since all of them are working like you want. On the other hand you should not specify an own ComboBox for the table cell, since it has one. You just have to add a StringConverter and set the items.
You may use like this:
myEnumColumn.setCellFactory((param) -> new ComboBoxTableCell<>(new StringConverter<MyEnum>() {
#Override public String toString(MyEnum object) {
return object.getName();
}
#Override public MyEnum fromString(String string) {
return MyEnum.valueOf(string);
}
}, MyEnum.values()));
If you prefer you can create a separate class for the StringConverter like I mentioned earlier, then just simply:
myEnumColumn.setCellFactory(factory -> new ComboBoxTableCell<>(new MyEnumConverter(), MyEnum.values()));
You can also get rid of the myEnumColumn.setOnEditCommit.
Thanks a lot! Actually I've spent a day on this, partially with another person, so this is really appreciated! :)
But:
I have to implement setOnEditCommit or otherwise the myEnum field backing the tableColumn does not change! With this everything works. Without, only what is displayed is changed.
myEnumColumn.setOnEditCommit(
event ->
{
event.getRowValue().setMyEnum(event.getNewValue());
});

Why page cannot get bookmarked with History mechanism in GWT/MVP4G.?

I am trying to implement history mechanism with a GWT app but has problem with page bookmark i.e. in my case, I have created 3 pages where one get invoked from another. Now, the problem is if page3 is bookmarked then while invoking that bookmark it should open page3 instead now it opens Home page.
Why is it so.? What can be the issue.?
I have implemented HistoryConverter as,
#History(type=HistoryConverterType.SIMPLE)
public class MyHistoryConverter implements HistoryConverter<HistoryManagerEventBus> {
public MyHistoryConverter() {
}
#Override
public void convertFromToken(String historyName, String param,HistoryManagerEventBus eventBus) {
eventBus.dispatch(historyName);
}
public String convertToToken(String eventType){
return eventType;
}
public String convertToToken(String eventType,HistoryPageTwoView view){
return view.getClass().getName();
}
public String convertToToken(String eventType,HistoryPageThreeView view){
return view.getClass().getName();
}
#Override
public boolean isCrawlable() {
return false;
}
}
and eventBus as,
#Events(startPresenter = HistoryPageOnePresenter.class,historyOnStart=true)
public interface HistoryManagerEventBus extends EventBusWithLookup {
/**
* Start event will be fired internally
*/
#Start
#Event(handlers = HistoryPageOnePresenter.class,historyConverter=MyHistoryConverter.class)
void start();
#InitHistory
#Event(handlers = HistoryPageOnePresenter.class)
void init();
#Event(handlers = HistoryPageTwoPresenter.class,historyConverter=MyHistoryConverter.class)
void getHistoryPageTwo();
#Event(handlers=HistoryPageThreePresenter.class,historyConverter=MyHistoryConverter.class)
void getHistoryPageThree();
#Event(handlers=HistoryPageOnePresenter.class,historyConverter=MyHistoryConverter.class)
void getHistoryPageOne();
#Event(handlers=HistoryPageOnePresenter.class)
void setHistoryPageTwo(HistoryPageTwoView view);
#Event(handlers=HistoryPageOnePresenter.class)
void setHistoryPageThree(HistoryPageThreeView view);
}
Assuming that:
#Event(handlers = HistoryPageTwoPresenter.class,historyConverter=MyHistoryConverter.class)
void getHistoryPageTwo();
#Event(handlers=HistoryPageThreePresenter.class,historyConverter=MyHistoryConverter.class)
void getHistoryPageThree();
#Event(handlers=HistoryPageOnePresenter.class,historyConverter=MyHistoryConverter.class)
void getHistoryPageOne();
are your navigation events, there is no need to have the following methods inside the MyHistoryConverter class defined:
public String convertToToken(String eventType,HistoryPageTwoView view){
return view.getClass().getName();
}
public String convertToToken(String eventType,HistoryPageThreeView view){
return view.getClass().getName();
}
as they are not called to create history tokens.
If your history converter works, you should see something like that in your URL:
[myURL]#getHistoryPageOne
or
[myURL]#getHistoryPageTwo
or
[myURL]#getHistoryPageThree
If you entering:
[myURL]#getHistoryPageThree
to start your application, the tokens will be handle in the convertFromToken-method.
You can add the #Debug-annotation to your eventBus to verify that the bookmarked event is fired at the start of your application.
So everything looks good, except the fact, that the Start-event should not have a historyConverter-attribute.

GWT : Render a hyperlink in a TextColumn of a CellTable

First of all - I am a beginner with Java and GWT. I have a scripting language background so please be explicit.
I have a CellTable that is populated with data from a database( ServerKeyWord class gets the data ).
myCellTable.addColumn(new TextColumn<ServerKeyWord>() {
#Override
public String getValue(ServerKeyWord object) {
// TODO Auto-generated method stub
return object.getName();
}
});
The example from above works, but it only shows the data as a text. I need to make it a hyperlink, that when you click it, it opens a new tab to that location.
I've surfed the web and got to the conclusion that I need to override render.
public class HyperTextCell extends AbstractCell<ServerKeyWord> {
interface Template extends SafeHtmlTemplates {
#Template("<a target=\"_blank\" href=\"{0}\">{1}</a>")
SafeHtml hyperText(SafeUri link, String text);
}
private static Template template;
public static final int LINK_INDEX = 0, URL_INDEX = 1;
/**
* Construct a new linkCell.
*/
public HyperTextCell() {
if (template == null) {
template = GWT.create(Template.class);
}
}
#Override
public void render(Context context, ServerKeyWord value, SafeHtmlBuilder sb) {
if (value != null) {
// The template will sanitize the URI.
sb.append(template.hyperText(UriUtils.fromString(value.getName()), value.getName()));
}
}
}
Now ... How do I use the HyperTextCell class with the addColumn method as in the first code example?!
Thank you in advance!
HyperTextCell hyperTextCell = new HyperTextCell();
Column<ServerKeyWord, ServerKeyWord> hyperColumn = new Column<ServerKeyWord, ServerKeyWord>(
hyperTextCell) {
#Override
public ServerKeyWord getValue(ServerKeyWord keyWord) {
return keyWord;
}
};
myCellTable.addColumn(hyperColumn);

GWT Editor Framework - Show ENUM using ValueListBox in own editor

I have an Enum SupplierCode:
public enum SupplierCode
{
BG("British Gas"), CNG("Contract Natural Gas"), COR("Corona Energy");
private String value;
SupplierCode(String value)
{
if(value != "")
{
this.value = value;
}
}
// ... toString() and fromString() omitted for brevity
// for editor framework (?)
public String getValue()
{
return value;
}
public void setValue(String value)
{
this.value = value;
}
}
I display it in my editors using a ValueListBox:
#UiField(provided = true)
ValueListBox<SupplierCode> supplierCode = new ValueListBox<SupplierCode>(new AbstractRenderer<SupplierCode>()
{
#Override
public String render(SupplierCode object)
{
return object == null ? "" : object.toString();
}
});
// in the constructor
public ContractEditor()
{
initWidget(uiBinder.createAndBindUi(this));
supplierCode.setAcceptableValues(Arrays.asList(SupplierCode.values()));
}
I have to edit this type a few times in my app so I wanted to make an editor for just this dropdown, called SupplierCodeEditor:
public class SupplierCodeEditor extends Composite implements Editor<SupplierCode>
{
private static SupplierCodeEditorUiBinder uiBinder = GWT.create(SupplierCodeEditorUiBinder.class);
interface SupplierCodeEditorUiBinder extends UiBinder<Widget, SupplierCodeEditor>
{
}
#UiField(provided = true)
ValueListBox<SupplierCode> value = new ValueListBox<SupplierCode>(new AbstractRenderer<SupplierCode>()
{
#Override
public String render(SupplierCode object)
{
return object == null ? "" : object.toString();
}
});
public SupplierCodeEditor()
{
initWidget(uiBinder.createAndBindUi(this));
value.setAcceptableValues(Arrays.asList(SupplierCode.values()));
}
}
However, when I use it, although it renders the list ok with the options, it doesn't select the actual value from the list. I thought having the getValue() and setValue() methods would work but seemingly not.
Does anyone know of a way to put this in one editor file? Then I won't have to repeat the code for the renderer and call setAcceptableValues() every place I want to use it.
Use LeafValueEditor<SupplierCode>:
public class SupplierEditor extends Composite implements LeafValueEditor<SupplierCode> {
interface SupplierEditorUiBinder extends UiBinder<Widget, SupplierEditor> {
}
private static SupplierEditorUiBinder uiBinder = GWT.create(SupplierEditorUiBinder.class);
#UiField(provided = true)
ValueListBox<SupplierCode> codes;
public SupplierEditor() {
codes = new ValueListBox<>(new AbstractRenderer<SupplierCode>() {
#Override
public String render(SupplierCode object) {
return object == null ? "" : object.toString();
}
});
initWidget(uiBinder.createAndBindUi(this));
codes.setAcceptableValues(Arrays.asList(SupplierCode.values()));
}
#Override
public SupplierCode getValue() {
return codes.getValue();
}
#Override
public void setValue(SupplierCode value) {
codes.setValue(value);
}
}
This way, your widget will be easily pluggable in a Editor hierarchy.
And you don't need the get/set methods in your SupplierCode enum.
You have to either:
use #Editor.Path("") on your child ValueListBox
make your SupplierCodeEditor implement LeafValueEditor<SupplierCode>, with delegating getValue and setValue to the ValueListBox
make your SupplierCodeEditor implement IsEditor<LeafValueEditor<SupplierCode>, returning the ValueListBox's asEditor() from your own asEditor().
BTW, you absolutely don't need the getValue and setValue on your enum values.

GWT - Issue with property change member variable being raised while loading the data first time

I have a GWT application that loads a product when the page is loaded. I am using PropertyChangeEvent on the product object (and its sub-objects) to update the values of fields, whenever a change happens.
Of course, I do not want this PropertyChangeEvent to raise when the product is loaded for the first time. For this, I am setting the raisePropertyChange value to false, but it doesn't seem to work. Please find below the code base:
// Class ProductBaseImpl
public abstract class PropChangeImpl {
// The raise property change event, should be turned off conditionally
private boolean raisePropertyChangeEvent = true;
protected boolean getRaisePropertyChangeEvent() {
return this.raisePropertyChangeEvent;
}
protected void setRaisePropertyChangeEvent(final boolean value) {
this.raisePropertyChangeEvent = value;
}
protected void raisePropertyChangeEvent(String fieldName, Object oldValue, Object newValue) {
if (this.raisePropertyChangeEvent ) {
// --> HERE IS THE PROBLEM <--
// This IF loop must not be true when loading the product first time
System.out.println("Property change event raised!");
// the update operations go here
} else {
System.out.println("Property change event not raised!");
}
}
}
// Class ProductBaseImpl
public abstract class ProductBaseImpl extends PropChangeImpl {
private static HandlerRegistration productChangeBeginRegistration;
private static HandlerRegistration productChangeEndRegistration;
protected E instance;
protected ProductBaseImpl(final E instance) {
this.instance = instance;
// Stop updates when a new product loads
if (ProductBaseImpl.productChangeBeginRegistration == null) {
ProductBaseImpl.productChangeBeginRegistration = Core.getEventBus().addHandler(ProductChangeBeginEvent.TYPE, new ProductChangeBeginEventEventHandler() {
#Override
public void onProductChangeBegin(final ProductChangeBeginEvent event) {
ProductBaseImpl.this.raisePropertyChangeEvent(false);
}
});
}
if (ProductBaseImpl.productChangeEndRegistration == null) {
ProductBaseImpl.productChangeEndRegistration = Core.getEventBus().addHandler(ProductChangeEndEvent.TYPE, new ProductChangeEndEventtHandler() {
#Override
public void onProductChangeEnd(final ProductChangeEndEvent event) {
ProductBaseImpl.this.raisePropertyChangeEvent(true);
}
});
}
}
}
// Class ProductSubObj1
public class ProductSubObj1 extends ProductBaseImpl {
public ProductSubObj1 (final E instance) {
super(instance);
// some other operations
}
}
// similar to above, I have classes ProductSubObj1, ProductSubObj2 ...
// Class ProductProvider, that fetches the product from service to UI
public class ProductProvider {
// some properties and members
public void fetchProduct(String productId) {
// Let listeners know the product is about to change
Core.getEventBus().fireEvent(new ProductChangeBeginEvent(productId));
// Call the service to get the product in Json data
// After processing the data to be available for the UI (and scheduleDeferred)
Core.getEventBus().fireEvent(new ProductChangeEndEvent(productId));
}
}
As commented inline in the code, the control always goes within the
if (this.raiseDataChangeEvent)
block which I don't want to happen when the product is loaded for the first time.
Could you please advise what am I doing wrong?
Thanks.
Can you just do this:?
protected void raisePropertyChangeEvent(String fieldName, Object oldValue, Object newValue) {
if (this.raisePropertyChangeEvent && oldValue != null /*Or whatever your default unloaded value is*/) {
// --> HERE IS THE PROBLEM <--
// This IF loop must not be true when loading the product first time
System.out.println("Property change event raised!");
// the update operations go here
} else {
System.out.println("Property change event not raised!");
}
}

Categories

Resources