Donnerstag, 28. Februar 2013

GWT: TabLayoutPanel wird im IE nicht angezeigt

GWT 2.5

Immer wieder der IE, diesmal in allen von mir getesteten Versionen (7, 8, 9, 10).
Die Tableiste wurde nicht angezeigt, Lösung war bei der Erzeugung des TabLayoutPanel die Unit nicht in EM sondern in PX anzugeben. Gefunden habe ich die Lösung hier: Google Code - Issue 6506: TabLayoutPanel UiBinder Tabs aren't displayed in IE9

Montag, 25. Februar 2013

GWT: SuggestBox und Tabulator KeyStroke in der CellTable

GWT 2.5

In letzter Zeit handelten meine Beiträge größtenteils von CellTable und SuggestBox und auch diesmal geht es so weiter. Folgende Problemstellung: Ich habe in der Table die Möglichkeit geschaffen daß man mit Tab-Sprüngen die Zellen wechselt, hier der Link dazu wie man das macht: stackoverflow Frage + Antwort.

Nun ist es aber mal so, das wenn man mit Tab die Zelle wechselt der KeyUp Event schon in der neuen Zelle stattfindet und hier beginnen die Probleme: Die SuggestBox glaubt durch das KeyUp das der User etwas in der TextBox verändert hat und läuft gleich mal los um Vorschläge einzuholen... für meine Erfordernisse ganz und gar unnötig bzw. will ich nicht daß der im Feld stehende Wert gleich mal dazu verwendet wird Services aufzurufen.

Die SuggestBox behandelt das in der privaten Methode addEventsToTextBox(), und da privat kann man das Teil nicht einfach überschreiben. Eine Möglichkeit ist es nun die gesamte Klasse zu kopieren und bei der Behandlung des KeyUp Events den Tab KeyStroke daran zu hindern die Methode refreshSuggestions() aufzurufen. Das funktioniert, nur ist das Kopieren ganzer Klassen für eine Zeile Änderung und die Übernahme der Package Struktur (notwendig weil die SuggestBox auf protected Teile im Package com.google.gwt.user.client.ui zugreift) einfach nur gruselig.

Ich habe mich deshalb entschlossen der TextBox einen KeyUpHandler hinzuzufügen der der Textbox die Möglichkeit gibt sich zu merken welcher Key losgelassen wurde. Das verwende ich nun in meinem SuggestOracle in der Methode requestSuggestions. Den gesamten Code der Implementierung kann man sich  aus meinen vorherigen Beiträgen zusammenreimen, hier die aktuell nötigen Änderungen damit das ganze klappt:

Hier die erweiterte TextBox die ich dann zum Erzeugen meiner SuggestBox verwende:

 private class MyTextBox extends TextBox {
  public int currentKeyUpStroke;
  private MyTextBox(Element element) {
   super(element);
   this.addKeyUpHandler(new KeyUpHandler() {
    @Override
    public void onKeyUp(KeyUpEvent event) {
     MyTextBox.this.currentKeyUpStroke = event.getNativeKeyCode();
    }
   });
  }
  public int getCurrentKeyUpStroke() {
   return currentKeyUpStroke;
  }
 }


Und hier die Stelle wo ich, bevor ich meine Services aufrufe, überprüfe ob nicht etwa ein Tab der Auslöser des ganzen war:

 private SuggestOracle getSuggestOracle() {
  final SuggestOracle so = new SuggestOracle() {

   @Override
   public void requestSuggestions(final Request request, final Callback callback) {

    if (request.getQuery().length() > 3
      && ((MyTextBox) RegeldatenTextInputCell.this.suggestBox.getTextBox()).getCurrentKeyUpStroke() != KeyCodes.KEY_TAB) {




Freitag, 15. Februar 2013

GWT: SuggestBox in CellTable -> Auswahl per Maus klappt nicht.

GWT 2.5

In diesem Post habe ich eine SuggestBox mit einer Cell in einer CellTable verbunden, das klappte auch überraschend gut. Man konnte einen Vorschlag auswählen und der würde auch übernommen. Nur klappte das leider nur mit den Pfeiltasten und einer Enter Bestättigung, wollte man per Maus auswählen wurde der Wert einfach nicht übernommen.

Analog zu diesem Problem gibt es ein bisschen etwas im Netz zu finden, aber wirklich den Durchbruch brachte keiner der Tips:

codinginthetrenches.com: catching value change events from the gwt suggestbox
Google Code: google-web-toolkit: SuggestBox OnChange fired before suggestion text is set

Ich brachte das Teil endlich dazu die Maus Auswahl zu schlucken, aber nun nahm er die Pfeil/Enter Auswahl nicht mehr an, dazu die Codezeilen die in der onBrowserEvent Methode in meiner schon sehr erweiterten TextInputCell:


if (BrowserEvents.FOCUS.equals(eventType) && (isUserid || isUseridStrict || connectedUserid != null)) {
   TextBox textBox = new MyTextBox(getInputElement(parent));
   suggestBox = new MySuggestBox(getSuggestOracle(), textBox);
   suggestBox.onAttach();

   suggestBox.addSelectionHandler(new SelectionHandler<Suggestion>() {
    @Override
    public void onSelection(SelectionEvent<Suggestion> event) {
     final String selected = event.getSelectedItem().getReplacementString();
     ValueChangeEvent.fire(suggestBox, selected);
    }
   });
   suggestBox.addValueChangeHandler(new ValueChangeHandler<String>() {

    @Override
    public void onValueChange(ValueChangeEvent<String> event) {
     valueUpdater.update(event.getValue());
     suggestBox.setValue(event.getValue());
    }
   });
  }

Ich hatte soweit herausgefunden daß ich den valueUpdater benutzen musste damit die Table überhaupt mitbekommt daß ich etwas verändert habe, bei der Pfeil/Enter Auswahl kam das aber gar nicht gut an, aus welchem Grund auch immer nahm nun diese keine Auswahl mehr, wenn wer eine Erklärung dafür hat, ich würde mich freuen.

Mein größtes Problem war, wie unterscheide ich im Valuechangehandler ob ich durch einen Mausklick oder durch Pfeil/Enter updaten will? Schlussendlich hab ich dann irgendwann geschnallt dass wenn ich mit Pfeil/Enter in die onValueChange Methode rutsche den Value der suggestBox schon irgendwo gesetzt hatte, wenn also der Wert des Events und der in der suggestBox gleich sind brauche ich den valueUpdater nicht mehr zu bemühen, bzw. darf ihn gar nicht anwerfen, da sonst ja meine Pfeil/Enter Auswahl nicht mehr funktioniert:


suggestBox.addValueChangeHandler(new ValueChangeHandler<String>() {

    @Override
    public void onValueChange(ValueChangeEvent<String> event) {
     if (!suggestBox.getValue().equals(event.getValue())) {
      valueUpdater.update(event.getValue());
     }
     suggestBox.setValue(event.getValue());
    }
   });


Nachtrag 21.2.2013:

Leider funktioniert meine Lösung nicht für den IE8, darum hier Änderungen die mit dem aktuellen Chrome, FF und IE8 funktionieren, aber imho noch viel lausiger sind als die ursprünglich schon sehr dreckige Lösung:


suggestBox.addSelectionHandler(new SelectionHandler<Suggestion>() {
    @Override
    public void onSelection(SelectionEvent<Suggestion> event) {
     final String selected = event.getSelectedItem().getReplacementString();
     if (nativeEvent.getType().equals(BrowserEvents.CLICK)
       || !Window.Navigator.getUserAgent().contains("MSIE 8.0")) {
      ValueChangeEvent.fire(suggestBox, selected);
     }
    }
   });

   suggestBox.addValueChangeHandler(new ValueChangeHandler<String>() {

    @Override
    public void onValueChange(ValueChangeEvent<String> event) {
     if (!event.getValue().equals(suggestBox.getValue()) || Window.Navigator.getUserAgent().contains("MSIE 8.0")) {
      valueUpdater.update(event.getValue());
     }
    }
   });


nativeEvent ist einfach der Parameter der onBrowserEvent Methode.

Freitag, 1. Februar 2013

GWT Celltable: Bestimmte Input Felder in einer Zeile verstecken

GWT 2.5

Die Zellen einer Celltable werden ja normalerweise in über die Column definiert, allerdings kann es ja sein dass bestimmte Zellen in einer Zeile "anders" sein sollen. In meinem Fall wollte ich zeilenabhängig bestimmte Felder verstecken, der Rest sollten ganz normale TextInputCells sein. Dazu habe ich mir einfach den Code der TextInputCell kopiert und ein bisschen erweitert.

Zum einen eine Instanzvariable hidden hinzugefügt, die ich beim Instanzieren mal false setze, da wenn nicht anders entschieden das Feld nicht versteckt sein soll.

 public ExtendendedBrowserEventsTextInputCell() {
  super(BrowserEvents.CHANGE, BrowserEvents.KEYUP, BrowserEvents.KEYPRESS);
  this.hidden = false;
  if (template == null) {
   template = GWT.create(Template.class);
  }
 }


Und zum anderen in der getValue der Spalte mal abgefragt ob ich in der richtigen Zeile bin und dort das hidden gesetzt.

 private final class TestColumn extends Column<String[], String> {
  int index;
  ListDataProvider<String []> provider;

  public TestColumn(int index, ListDataProvider<String[]> provider, Cell cell) {
   super(cell);
   this.index = index;
   this.provider = provider;
  }

  @Override
  public String getValue(String[] object) {

   if (provider != null && provider.getList().indexOf(object) > 0
     && this.getCell() instanceof ExtendendedBrowserEventsTextInputCell) {
    ExtendendedBrowserEventsTextInputCell cell = (ExtendendedBrowserEventsTextInputCell) this.getCell();
    cell.hidden = true;
   }

   return object[index];
  }
 }

Die render Methode der Cell erweitern, da ich weiß das die Felder die ich treffen will, keinen Inhalt haben funktioniert das so:

@Override
 public void render(Context context, String value, SafeHtmlBuilder sb) {
  // Get the view data.
  Object key = context.getKey();
  ViewData viewData = getViewData(key);
  if (viewData != null && viewData.getCurrentValue().equals(value)) {
   clearViewData(key);
   viewData = null;
  }

  String s = (viewData != null) ? viewData.getCurrentValue() : value;
  if (s != null) {
   sb.append(template.input(s));
  } else if (hidden) {
   sb.appendHtmlConstant("<input type=\"hidden\" tabindex=\"-1\"></input>");
  } else {
   sb.appendHtmlConstant("<input type=\"text\" tabindex=\"-1\"></input>");
  }
 }

Schon verschwinden Felder aus der CellTable.