1. #1
    Sencha User
    Join Date
    Jan 2013
    Posts
    17
    Answers
    2
    Vote Rating
    0
    paranoiabla is on a distinguished road

      0  

    Default 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:
    Code:
                                for (EntityDto entityToDelete : selected) {
                                    getGrid().getStore().remove(entityToDelete);
                                }
    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.
    Here's my grid implementation (I extend ListStoreEditor for what matters):
    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);
        }
    }
    I really hope someone can help me.

  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.

    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;
      }
    
    }
    I also modified the example itself to display the results after flush() was called to show that the kids list was correct:
    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();
          }
        }));

  3. #2
    Sencha - GXT Dev Team
    Join Date
    Feb 2009
    Location
    Minnesota
    Posts
    2,732
    Answers
    109
    Vote Rating
    90
    Colin Alworth is a glorious beacon of light Colin Alworth is a glorious beacon of light Colin Alworth is a glorious beacon of light Colin Alworth is a glorious beacon of light Colin Alworth is a glorious beacon of light

      0  

    Default


    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.

    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;
      }
    
    }
    I also modified the example itself to display the results after flush() was called to show that the kids list was correct:
    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();
          }
        }));

  4. #3
    Sencha User
    Join Date
    Jan 2013
    Posts
    17
    Answers
    2
    Vote Rating
    0
    paranoiabla is on a distinguished road

      0  

    Default


    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.

  5. #4
    Sencha - GXT Dev Team
    Join Date
    Feb 2009
    Location
    Minnesota
    Posts
    2,732
    Answers
    109
    Vote Rating
    90
    Colin Alworth is a glorious beacon of light Colin Alworth is a glorious beacon of light Colin Alworth is a glorious beacon of light Colin Alworth is a glorious beacon of light Colin Alworth is a glorious beacon of light

      0  

    Default


    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.

Thread Participants: 1

Tags for this Thread