Discard changes of TextField value by pressing Escape key

Hi every1!

What i try to do:

  1. Table with Items
  2. One Column of table have TextField in it with some value and is readonly at the beginning.
  3. User click on this Number and field gets “editdMode” so user can change value.
  4. User decide to cancel changing of the field and press Escape key.

Expected:

  1. Field gets his old value again. If user already has wroten some other value in this field, this gets lost.
  2. Field goes in read only mode again.

So far i’ve done all points except Expected.1
EscapeKey catch i do with this:

textField.addShortcutListener(new FocusShortcut(textField, KeyCode.ESCAPE, null) {

					@Override
					public void handleAction(Object sender, Object target) {
						System.out.println("Escape pressed");
						((TextField) target).setReadOnly(true);
					}
				});

My table got datasource: BeanItemContainer with my DataBase elements.

How can i make by the Escape Press so field takes the value before it gone to edit? Any help is appriciated.

Thanks in advance

textfield.discard() doesn’t do what you want? if your textfield is set up to write directly to the source, then you’d maybe have to have textfield.setWriteTrough(false).

Note also that you should do the changes to value before you do setReadOnly(true). “Read only” doesn’t only affect the UI, it affects the setting of value programmatically as well.

Hi,

i set value this way

textField.setValue("" + ((LieferungItem) itemId).getAnzahl_bestellt());

I tryed to use discard but this do nothing for me, value still changes (can see it in debugger).

Maybe i use this discard thing somehow wrong?

How u would make this for some abstract Object from DB?

Thanks in advance

If discard does nothing for you, then the value is probably already commited back to the datasource (container). Using setWriteTrough(false) and commit() and discard() manually would fix that.

How do i set this setWriteThrough to textfield, there is no method called this way by the textfield.

Update: found that write thorugh is set by form. But my field lives in the table and i have no form on the page. So i cant make setWriteThrough :frowning:

Gonna look further, maybe some dirty hack with hashmap of values of this table helps me a bit.

Thanks for your help

Hi every1! again. I’ve done my job :slight_smile:

Done it pretty hardcore way, but for my solution it is ok, sonce i m gonna have like very small quantity of possible fields in table for edit.

So review:

  1. I wanted to edit some value in the table.
  2. Edit begin is Click on this field, which makes this field editable.
  3. Than you can change value or let it be same.
  4. User can press Enter, Escape or just click outside this field.
  5. Depends of what user did Value will be saved in DB, or Discarded with notification of action into tray of the application.

How i did it:

  1. Got Table with my values using BeanItemContainer of my objects and make some hashmap who knows what edit field u got in these table. Here u see my container and HashMap for edit fields.

public HashMap<String, TextField> allEditFields = new HashMap<String, TextField>();
private LieferungItemContainer container;

setContainerDataSource(container);
setVisibleColumns(LieferungItemContainer.NATURAL_COL_ORDER);
setColumnHeaders(LieferungItemContainer.COL_HEADERS_DEUTSCH);

My container:


public class LieferungItemContainer extends BeanItemContainer<LieferungItem> implements Serializable {
	
	public static final Object[] NATURAL_COL_ORDER = new Object[]
 { "produktid", "produktname", "anzahl_bestellt", "bestellnr" };

	public static final String[] COL_HEADERS_DEUTSCH = new String[]
 { "ArtikelNr", "Name", "Menge", "BestellNr" };

	public LieferungItemContainer() throws IllegalArgumentException {
		super(LieferungItem.class);
	}

}

  1. Made generated column for my Field i want to be editable and add click listener to the table:

		// anzahl_bestellt
		addGeneratedColumn("anzahl_bestellt", new ColumnGenerator() {
			@Override
			public Object generateCell(Table source, Object itemId, Object columnId) {

				TextField textField = new TextField();
				textField.setWidth("40px");
				textField.setValue("" + ((LieferungItem) itemId).getAnzahl_bestellt());
				textField.setReadOnly(true);
				// put id of the edit field same as your Database ID of the Item
				// u got in the Table as main object. These allow you finding
				// item later on :) dirty but works for me.
				textField.setId("" + ((LieferungItem) itemId).getId());
				textField.setBuffered(true);
				allEditFields.put("" + textField.getId(), textField);

				// this makes react when user clicks outside edit field
				textField.addBlurListener(new BlurListener() {

					@Override
					public void blur(BlurEvent event) {
						if (!((TextField) event.getSource()).isReadOnly()) {
							System.out.println("BLUR - make this field not editable");
							// save action
							speichernAction((TextField) event.getSource(), false);
						}
					}
				});

				// this work when user press ESCAPE.
				textField.addShortcutListener(new FocusShortcut(textField, KeyCode.ESCAPE, null) {

					@Override
					public void handleAction(Object sender, Object target) {
						// note u have to do this, coz ESCAPE listener is on
						// TABLE, and u can easily press escape on some other
						// field which will not be TextField u wanted to edit
						// and u get Exception than
						if (target instanceof TextField) {
							System.out.println("Escape pressed");
							// save action
							speichernAction((TextField) target, false);
						}
					}
				});

				// this works when u press Enter, same as Escape proove you
				// catch real edit field u wanted and not some other trash :)
				textField.addShortcutListener(new FocusShortcut(textField, KeyCode.ENTER, null) {

					@Override
					public void handleAction(Object sender, Object target) {
						if (target instanceof TextField) {
							System.out.println("ENTER pressed");
							if (!((TextField) target).isReadOnly()) {
								// save action
								speichernAction((TextField) target, true);
							}
						}
					}
				});

				return textField;
			}
		});

		// this works ob every click on the table. So catch the needed field.
		// i've done it with looking what property of the item was clicked
		addItemClickListener(new ItemClickListener() {

			@Override
			public void itemClick(ItemClickEvent event) {
				System.out.println("CLICK ON LIEFERUNG ITEMS TABLE");
				// only if this anzahl_bestellt clicked, i do something
				if (event.getPropertyId().equals("anzahl_bestellt")) {
					TextField txt = allEditFields.get("" + ((LieferungItem) event.getItemId()).getId());
					// do something only if field is still not editable. this
					// prevents of some missfire when u click into field when it
					// is open for edit.
					if (txt.isReadOnly()) {
						System.out.println("Needed field clicked. lets open it for edit");
						txt.setReadOnly(false);
						txt.focus();
						txt.selectAll();
						allEditFields.put("" + txt.getId(), txt);
					}
				}
			}
		});

  1. After the field is designed, i add save method:

	// this will do save changes
	private void speichernAction(TextField txt, boolean saveData) {

		// find which item of the table was clicked!
		LieferungItem sItem = null;
		for (LieferungItem lItem : container.getItemIds()) {
			if (lItem.getId().toString().equals(txt.getId())) {
				sItem = lItem;
				break;
			}
		}

		// if this happens i m really dead - still dont know what to do in this
		// case exactly
		if (sItem == null) {
			System.out.println("SUPER BAD!! Kann eigentlich NICHT SEIN!! SOFORT MICH ANRUFEN!");
			com.vaadin.ui.Notification.show("Daten sind kaputt! Sofort support anrufen!", Type.ERROR_MESSAGE);
			txt.setReadOnly(true);
			return;
		}

		// my field needs to be integer, so i test it and if it not fit i change
		// to old value and get user back to edit
		Integer newValueInteger;
		try {
			newValueInteger = new Integer(txt.getValue());
		} catch (Exception e) {
			// falsche dateneingaben im feld, wir schmeissen die weg und machen
			// die alten daten aus elementitem rein
			newValueInteger = sItem.getAnzahl_bestellt();
			txt.selectAll();
			return;
		}

		// validaton done.
		// does something changedin these field, so we need to make something ?
		//if nothing changed, we do nothing.
		if (!sItem.getAnzahl_bestellt().equals(newValueInteger)) {
			if (saveData) {
				// save part
				sItem.setAnzahl_bestellt(newValueInteger);
				FacadeFactory.getFacade().store(sItem);
				//notifiy user of what have been done:)
				com.vaadin.ui.Notification.show("Änderungen Gespeichert", Type.TRAY_NOTIFICATION);
			} else {
				// escape part
				txt.setValue("" + sItem.getAnzahl_bestellt());
				//notifiy user of what have been done:)
				com.vaadin.ui.Notification.show("Änderungen Verworfen", Type.TRAY_NOTIFICATION);
			}
		}
		
		//close field for edit
		txt.setReadOnly(true);
		allEditFields.put("" + txt.getId(), txt);
	}

Voila! I got my problem solved :slight_smile: Someday i will find out how to do this with Best Practice of Vaadin, but for now i am happy. Leaving it here, maybe some1 looks for these as i was looking, and can save himself some hours of work.

Long live Vaadin!

Here are Files attached maybe some1 need these :slight_smile:
13012.java (7.45 KB)
13013.java (666 Bytes)