Hybrid View
-
19 Jan 2013 9:02 AM #1
Answered: Grid in a form
Answered: Grid in a form
Hey guys,
first of all congrats on the great product. I am, however, stuck on a problem I cannot solve already a few days. I implemented a window with different fields in it (TextField, HtmlEditor, SpinnerFields, etc.) and I used driver so that when the user clicks on the save button to pass his data to a GWT RPC service. This all worked great, so I decided to throw a Grid in the mix. The grid has a context menu with Add and Remove MenuItems and I want to add/remove rows from the grid. Again it all works good - the context menu is shown and when Remove is selected, all I do is get the selected entities in the grid, iterate over them and remove them from the store:
After that the items are moved from the grid, but as soon as I hit Save I see that the server receives ALL of the items - the original collection. I somehow am not able to pass the new collection to the server.Code:for (EntityDto entityToDelete : selected) { getGrid().getStore().remove(entityToDelete); }
Here's my grid implementation (I extend ListStoreEditor for what matters):
I really hope someone can help me.Code:public class EditableGrid extends ListStoreEditor<EntityDto> implements IsWidget, HasHandlers { interface ListDriver extends SimpleBeanEditorDriver<List<EntityDto>, EditableGrid> { } private List<EntityDto> results; private String entityClas; private ListDriver driver = GWT.create(ListDriver.class); public EditableGrid(ListStore<EntityDto> gridStore, List<EntityDto> results, String entityClas) { super(gridStore); this.results = results; this.entityClas = entityClas; driver.initialize(this); } @Override public Widget asWidget() { EntityDto header = results.get(0); final List<ColumnConfig<EntityDto, ?>> l = new ArrayList<ColumnConfig<EntityDto, ?>>(); ColumnConfig<EntityDto, ImageResource> iconCC = new ColumnConfig<EntityDto, ImageResource>(new ImageResourceProvider(header.getIcon())); iconCC.setWidth(20); ImageResourceCell imageCell = new ImageResourceCell() { @Override public void render(com.google.gwt.cell.client.Cell.Context context, ImageResource value, SafeHtmlBuilder sb) { super.render(context, value, sb); } }; iconCC.setCell(imageCell); l.add(iconCC); for (PropertyDto dto : header.getSections().get(0).getProperties()) { ColumnConfig<EntityDto, String> cc = new ColumnConfig<EntityDto, String>(new CustomValueProvider(dto.getId())); cc.setHeader(dto.getName()); l.add(cc); } if (results.subList(1, results.size()) != null) { setValue(results.subList(1, results.size())); } else { Info.display("", "WAS NULL"); } ColumnModel<EntityDto> cm = new ColumnModel<EntityDto>(l); final Grid<EntityDto> grid = new Grid<EntityDto>(getStore(), cm); grid.setLoadMask(true); grid.setBorders(true); grid.getView().setStripeRows(true); grid.getView().setColumnLines(true); grid.getView().setEmptyText("No entities found."); grid.getView().setAutoFill(true); grid.getView().setForceFit(true); grid.setWidth(500); grid.setStateful(true); grid.setStateId("gridExample"); grid.setContextMenu((Menu)(new ItemGridContextMenu(entityClas, grid)).asWidget()); grid.addRowDoubleClickHandler(new RowDoubleClickHandler() { @Override public void onRowDoubleClick(RowDoubleClickEvent event) { ItemPopupWindow w = new ItemPopupWindow(getStore().get(event.getRowIndex())); com.sencha.gxt.widget.core.client.Window window = (com.sencha.gxt.widget.core.client.Window) w.asWidget(); fireEvent(new WindowEvent(window)); window.show(); } }); return grid; } @Override public void fireEvent(GwtEvent<?> event) { EventUtils.EVENT_BUS.fireEvent(event); } }
-
Best Answer Posted by Colin Alworth
Extending a class like ListStoreEditor wouldn't be my first choice - I'd tend to encourage composition over inheritance, unless this specific setup is going to be reused quite frequently.
I'm also not understanding why the EditableGrid (which implements Editor) has an internal Driver - shouldn't this be *part* of the form rather than its own distinct editor?
Design issues aside, your problem is almost certainly that you never call flush() on the driver. My guess is that there is also an outer driver, and that this mix of two different drivers talking to this same sub-editor is where the confusion is coming from.
Check out this example http://www.sencha.com/examples/#Exam...ropertybinding especially in the contents of the PersonEditor class. Here's a slightly modified version of that class with a CheckBoxSelectionModel and a button to delete selected items, based roughly on your loop code. I can't run your code (missing EntityDto, plus whatever containing editor/form you've got), so this is the closest I can do to suggest. I think this would even be a good idea to add to the official example.
I also modified the example itself to display the results after flush() was called to show that the kids list was correct:Code:public class PersonEditor implements IsWidget, Editor<Person> { interface KidProperties extends PropertyAccess<Kid> { @Path("id") ModelKeyProvider<Kid> key(); ValueProvider<Kid, String> name(); ValueProvider<Kid, Integer> age(); } private static final KidProperties props = GWT.create(KidProperties.class); TextField name = new TextField(); TextField company = new TextField(); TextField location = new TextField(); NumberField<Double> income = new NumberField<Double>(new DoublePropertyEditor()); ListStore<Kid> kidStore = new ListStore<Kid>(props.key()); ListStoreEditor<Kid> kids = new ListStoreEditor<Kid>(kidStore); @Override public Widget asWidget() { FlowPanel container = new FlowPanel(); // should be layout based int w = 275; name.setWidth(w); company.setWidth(w); location.setWidth(w); income.setWidth(w); container.add(new FieldLabel(name, "Name")); container.add(new FieldLabel(company, "Company")); container.add(new FieldLabel(location, "Location")); container.add(new FieldLabel(income, "Income")); List<ColumnConfig<Kid, ?>> columns = new ArrayList<ColumnConfig<Kid,?>>(); final CheckBoxSelectionModel<Kid> selection = new CheckBoxSelectionModel<Kid>(new IdentityValueProvider<Kid>()); selection.setSelectionMode(SelectionMode.MULTI); columns.add(selection.getColumn()); ColumnConfig<Kid, String> name = new ColumnConfig<Kid, String>(props.name(), 200, "Name"); columns.add(name); ColumnConfig<Kid, Integer> age = new ColumnConfig<Kid, Integer>(props.age(), 100, "Age"); columns.add(age); final Grid<Kid> grid = new Grid<Kid>(kidStore, new ColumnModel<Kid>(columns)); grid.setBorders(true); grid.setSelectionModel(selection); grid.getView().setForceFit(true); GridInlineEditing<Kid> inlineEditor = new GridInlineEditing<Kid>(grid); inlineEditor.addEditor(name, new TextField()); inlineEditor.addEditor(age, new NumberField<Integer>(new IntegerPropertyEditor())); grid.setWidth(382); grid.setHeight(200); FieldLabel kidsContainer = new FieldLabel(); kidsContainer.setText("Kids"); kidsContainer.setLabelAlign(LabelAlign.TOP); kidsContainer.setWidget(grid); container.add(kidsContainer); container.add(new TextButton("Delete selected rows", new SelectHandler() { @Override public void onSelect(SelectEvent event) { for (Kid entityToDelete : selection.getSelectedItems()) { kidStore.remove(entityToDelete); } } })); return container; } }
Code:panel.addButton(new TextButton("Save", new SelectHandler() { @Override public void onSelect(SelectEvent event) { Person p = driver.flush(); new MessageBox(p.getKids().size() + " kids").show(); } }));
-
19 Jan 2013 6:34 PM #2
Extending a class like ListStoreEditor wouldn't be my first choice - I'd tend to encourage composition over inheritance, unless this specific setup is going to be reused quite frequently.
I'm also not understanding why the EditableGrid (which implements Editor) has an internal Driver - shouldn't this be *part* of the form rather than its own distinct editor?
Design issues aside, your problem is almost certainly that you never call flush() on the driver. My guess is that there is also an outer driver, and that this mix of two different drivers talking to this same sub-editor is where the confusion is coming from.
Check out this example http://www.sencha.com/examples/#Exam...ropertybinding especially in the contents of the PersonEditor class. Here's a slightly modified version of that class with a CheckBoxSelectionModel and a button to delete selected items, based roughly on your loop code. I can't run your code (missing EntityDto, plus whatever containing editor/form you've got), so this is the closest I can do to suggest. I think this would even be a good idea to add to the official example.
I also modified the example itself to display the results after flush() was called to show that the kids list was correct:Code:public class PersonEditor implements IsWidget, Editor<Person> { interface KidProperties extends PropertyAccess<Kid> { @Path("id") ModelKeyProvider<Kid> key(); ValueProvider<Kid, String> name(); ValueProvider<Kid, Integer> age(); } private static final KidProperties props = GWT.create(KidProperties.class); TextField name = new TextField(); TextField company = new TextField(); TextField location = new TextField(); NumberField<Double> income = new NumberField<Double>(new DoublePropertyEditor()); ListStore<Kid> kidStore = new ListStore<Kid>(props.key()); ListStoreEditor<Kid> kids = new ListStoreEditor<Kid>(kidStore); @Override public Widget asWidget() { FlowPanel container = new FlowPanel(); // should be layout based int w = 275; name.setWidth(w); company.setWidth(w); location.setWidth(w); income.setWidth(w); container.add(new FieldLabel(name, "Name")); container.add(new FieldLabel(company, "Company")); container.add(new FieldLabel(location, "Location")); container.add(new FieldLabel(income, "Income")); List<ColumnConfig<Kid, ?>> columns = new ArrayList<ColumnConfig<Kid,?>>(); final CheckBoxSelectionModel<Kid> selection = new CheckBoxSelectionModel<Kid>(new IdentityValueProvider<Kid>()); selection.setSelectionMode(SelectionMode.MULTI); columns.add(selection.getColumn()); ColumnConfig<Kid, String> name = new ColumnConfig<Kid, String>(props.name(), 200, "Name"); columns.add(name); ColumnConfig<Kid, Integer> age = new ColumnConfig<Kid, Integer>(props.age(), 100, "Age"); columns.add(age); final Grid<Kid> grid = new Grid<Kid>(kidStore, new ColumnModel<Kid>(columns)); grid.setBorders(true); grid.setSelectionModel(selection); grid.getView().setForceFit(true); GridInlineEditing<Kid> inlineEditor = new GridInlineEditing<Kid>(grid); inlineEditor.addEditor(name, new TextField()); inlineEditor.addEditor(age, new NumberField<Integer>(new IntegerPropertyEditor())); grid.setWidth(382); grid.setHeight(200); FieldLabel kidsContainer = new FieldLabel(); kidsContainer.setText("Kids"); kidsContainer.setLabelAlign(LabelAlign.TOP); kidsContainer.setWidget(grid); container.add(kidsContainer); container.add(new TextButton("Delete selected rows", new SelectHandler() { @Override public void onSelect(SelectEvent event) { for (Kid entityToDelete : selection.getSelectedItems()) { kidStore.remove(entityToDelete); } } })); return container; } }
Code:panel.addButton(new TextButton("Save", new SelectHandler() { @Override public void onSelect(SelectEvent event) { Person p = driver.flush(); new MessageBox(p.getKids().size() + " kids").show(); } }));
-
20 Jan 2013 5:02 AM #3
Hi Colin,
thank you so much for your response. Yes, the code is a bit of a mess, unfortunately. I did flush the driver, so that wasn't the case, and the rest of the Field did populate successfully. The problem was that inside the form window that I opened, I was setting a GridWrapper - another object that encapsulated the editable grid in itself and the getValue() method was a simple getter method that returned all of the elements. Also, right after I delete the elements from the grid I now call gridStore.commitChanges() (not sure if that helps too).
Anyways, it is now resolved.
-
20 Jan 2013 11:35 AM #4
The Store.commitChanges method has only to do with changes made to Record objects - these track changes to properties of models, not to adding, moving, removing models, so this will make no difference.
Additionally, the ListStoreEditor calls commitChanges() as part of flush(), so as to make any changes the user made to models permanent.


Reply With Quote