Showing posts with label CellTable. Show all posts
Showing posts with label CellTable. Show all posts

Monday, 25 February 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) {




Friday, 15 February 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.

Friday, 1 February 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.




Thursday, 3 January 2013

GWT: Celltable Filter

GWT 2.5

Will man eine Celltable filtern (sprich Zeilen verschwinden lassen bei denen kein Element der Zeile dem Filterkriterium entspricht) geht das mit Tabellen, die fix sind und auch keine veränderbaren Elemente beherbergen, ziemlich einfach:

Einfach ein Eingabefeld wo der Filterwert eingegeben wird:


  filterTextBox.addKeyUpHandler(new KeyUpHandler() {

   @Override
   public void onKeyUp(KeyUpEvent event) {
    TableFilter.filterTable(allContent, tableDataProvider.getList(),
     filterTextBox.getText());
   }
  });


Dazu die Util Methode, wobei allContent den gesamten Content der Table darstellt:

 public static List<T> filterTable(List<T> allContent, List<T> actTableContent, String filterText) {
  if (!allContent.isEmpty()) {
   for (T entry : allContent) {
    if (!entry.getName().toLowerCase().contains(filterText.toLowerCase())
      && !entry.getText().toLowerCase().contains(filterText.toLowerCase())) {
     actTableContent.remove(entry);
    } else if (!actTableContent.contains(entry)) {
     actTableContent.add(entry);
    }
   }
  } else {
   actTableContent.clear();
  }
  return actTableContent;
 }

Probleme bereitete mir allerdings die Aufgabe, wie filtern wenn der Inhalt nicht fest steht und während verschiedener Filtereinstellungen verändert werden kann durch Eingabe in Zellen, durch hinzufügen oder löschen von ganzen Zeilen? Mit dem obigen Mechanismus müßte ich nach jeder möglichen Veränderung den "allContent" entsprechend anpassen, kann dazu aber nicht die Liste aus dem ListDataProvider verwenden, da diese ja gerade gefiltert sein könnte und deshalb nicht vollständig. Ich suchte also nach einer Möglichkeit Zeilen zu verstecken und fand schließlich eine super Lösung im Blog artiom.pro. Sie funktioniert einwandfrei, vielen Dank für diese Codeschnipsel.

Hier der Link direkt zur Lösung.

Tuesday, 25 December 2012

GWT: Cell in Celltable dynamisch verändern

GWT 2.5

Wenn die Anforderung laute je nach Cell Value Veränderungen z.B. in der Farbe der Schrift vorzunehmen habe ich eine Lösung gefunden, die wahrscheinlich nicht ideal ist, die ich nichtsdestotrotz hier vorstellen möchte.

Als erstes möchte ich die TextInputCell etwas erweitern, in diesem Fall zum zweiten Mal, da ich vorher schon Ausbauten brauchte, der wichtige Punkt ist in jedem Fall dass ein eigenes Template gestaltet werden kann:


public class MyTextInputCell extends ExtendendedBrowserEventsTextInputCell {

 interface Template extends SafeHtmlTemplates {
  @Template(<input type=\"text\" value=\"{0}\" tabindex=\"-1\" size=\"{1}\" maxlength=\"{1}\" style=\"color:{2}\"></input>")
  SafeHtml input(String value, String size, String textColor);
 }

Zu beachten ist, das die Farbe über style geändert wurde, einfach die color reinzuschreiben reichte aus irgend einem Grund nicht.
Das Ganze kommt dann in der überschriebenen Methode render zum Tragen:


@Override
 public void render(com.google.gwt.cell.client.Cell.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(myTemplate.input(s, size, textColor));
  } else {
   sb.appendHtmlConstant("<input type=\"text\" tabindex=\"-1\"></input>");
  }
 }
textColor ist dabei eine Instanzvariable, doch wann verändere ich diese entsprechend?
Ich greife da in der Column Implementierung in der Methode getValue, hier kann man schön Wertabhängig die InstanzVariable textColor verändern. Gewählt habe ich diesen Weg, weil ich ein großes Array an Namen brauche um die Farbe des Textes zu bestimmen und mir es am geschicktesten schien dieses Array der Column mitzugeben.

private static final class MyColumn extends Column<String[], String> {

  int index;
  Name[] names;

  public MyColumn(Cell<String> cell, int index, Name[] names) {
   super(cell);
   this.index = index;
   this.names = names;
  }

  @Override
  public String getValue(String[] data) {
   if (this.getCell() instanceof MyTextInputCell) {
    MyTextInputCell cell = (MyTextInputCell) this.getCell();
    if ((cell.isUserid || cell.isUseridStrict) && !Utils.isUseridInNames(data[index], this.names)) {
     cell.textColor = "red";
    } else {
     cell.textColor = "black";
    }
   }
   return data[index];
  }
 }

Es gibt sicher elegantere Methoden, ich würde mich freuen wenn jemand mir eine zeigt.

Links:
Frage dazu auf stackoverflow

Saturday, 15 December 2012

GWT: SuggestBox in einer CellTable

GWT 2.5

Hat man sich einmal entschlossen eine CellTable zu verwenden und stößt dann auf Anforderungen die die CellTable nicht von vornherein vorgesehen hat kann das sehr ärgerlich werden. Mir ging es so bei der Anforderung eine SuggestBox in der Table zu verwenden.
Schlußendlich stellte sich heraus daß das Ganze gar nicht so eine Zauberei ist, muß aber auch dazusagen, daß die Lösung noch nicht wirklich den Praxistest erlebt hat.

Meine Lösung besteht darin, einfach die TextInputCell zu extenden und so eine SuggestBox reinzuschummeln:


public class SuggestBoxTextInputCell extends TextInputCell {

 MySuggestBox suggestBox;

 private class MyTextBox extends TextBox {
  public MyTextBox(Element element) {
   super(element);
  }
 }

 private class MySuggestBox extends SuggestBox {
  public MySuggestBox(SuggestOracle suggestOracle, TextBox textBox) {
   super(suggestOracle, textBox);
  }

  @Override
  public void onAttach() {
   super.onAttach();
  }
 }

 public SuggestBoxTextInputCell() {
  super();
 }


 @Override
 public void onBrowserEvent(Context context, Element parent, String value, NativeEvent event,
   ValueUpdater<String> valueUpdater) {
  super.onBrowserEvent(context, parent, value, event, valueUpdater);
  String eventType = event.getType();
  if (BrowserEvents.FOCUS.equals(eventType)) {
   TextBox textBox = new MyTextBox(getInputElement(parent));
   suggestBox = new MySuggestBox(getSuggestOracle(), textBox);
   suggestBox.onAttach();
  }
 }

 private SuggestOracle getSuggestOracle() {
        //this does not work without implementation
        return null;
 }
}

Die Methode gestSuggestOracle muss natürlich noch implementiert werden um den Beispielcode lauffähig zu bekommen.
Links:
Google Groups Beitrag
Stackoverflow Beitrag zum Thema