Announcement

Collapse
No announcement yet.
X
  • Filter
  • Time
Clear All
new posts

    setPickListFields within custom class ignored when component is declared as FormItem

    [SmartClient Version: v11.0p_2016-10-07/PowerEdition Deployment (built 2016-10-07)]

    It's probably worth pointing out that this has worked fine for us, and continues to do so in SmartClient Version: v10.0p_2016-02-18/PowerEdition Deployment (built 2016-02-18).

    We have a custom class (IpCodeNameCombobox) that extends ComboBoxItem. This class sets an initHandler which I have simplified in the below example to call setPickListFields() and setFilterFields(), passing valueField and displayField to each.

    If I declare a control as an IpCodeNameCombobox, it renders as expected, with both fields contained in the dropdown and used for filtering purposes.

    However, if I declare the control as a FormItem and allow it to render based on the editorType attribute defined for the corresponding field in the datasource, although the initHander is called with what appear to be the correct values (observed through the browser's debugger), the control does render as a combo but only shows and filters on the displayField.

    To see this just make the modifications included below to the BuiltInDS sample. When you run it, click on "Animals" and a single field will render as a combo, but will only show item names in the dropdown. Change the code that instantiates a FormItem to instead instantiate an IpCodeNameCombobox and the combo will instead render both SKU and itemName, as expected. Remove the boundForm.setFields(combo); line and the field will again render as expected.

    Regards,
    Gary O'Donnell


    1) Add the following class to the project:

    Code:
    package com.smartgwt.sample.client;
    
    import com.google.gwt.core.client.JavaScriptObject;
    import com.smartgwt.client.bean.BeanFactory;
    import com.smartgwt.client.widgets.form.fields.ComboBoxItem;
    import com.smartgwt.client.widgets.form.fields.FormItem;
    import com.smartgwt.client.widgets.form.fields.events.FormItemInitHandler;
    import com.smartgwt.client.widgets.grid.ListGridField;
    
    @BeanFactory.Generate
    
    public class IpCodeNameCombobox extends ComboBoxItem
    {
        public IpCodeNameCombobox() {
            super();
            init();
        }
    
        public IpCodeNameCombobox(JavaScriptObject jso) {
            super(jso);
            init();
        }
    
        public IpCodeNameCombobox(String name) {
            super(name);
            init();
        }
    
        public IpCodeNameCombobox(String name, String title) {
            super(name, title);
            init();
        }
    
        private void init() 
        {
            final ComboBoxItem me = this;
    
            setInitHandler(new FormItemInitHandler() 
            {
                @Override
                public void onInit(FormItem itemIn) 
                {
                    me.setPickListFields(
                            new ListGridField(me.getValueField()),
                            new ListGridField(me.getDisplayField()));
                    me.setFilterFields(me.getValueField(), me.getDisplayField());
                }
            });              
        }
    }

    2) Modify the definition of commonName in animals.ds.xml:

    Code:
           <field name="commonName" title="Animal" type="text" optionDataSource="supplyItem" valueField="SKU" displayField="itemName" editorType="com.smartgwt.sample.client.IpCodeNameCombobox"/>

    3) Modify BuildInDS.java by adding the two lines delineated by "MODIFICATION # 1 START/END":
    Code:
    package com.smartgwt.sample.client;
    
    import com.google.gwt.core.client.EntryPoint;
    import com.smartgwt.client.core.KeyIdentifier;
    import com.smartgwt.client.data.DataSource;
    import com.smartgwt.client.data.Record;
    import com.smartgwt.client.types.SelectionStyle;
    import com.smartgwt.client.types.SortArrow;
    import com.smartgwt.client.util.PageKeyHandler;
    import com.smartgwt.client.util.Page;
    import com.smartgwt.client.util.SC;
    import com.smartgwt.client.widgets.IButton;
    import com.smartgwt.client.widgets.Label;
    import com.smartgwt.client.widgets.events.ClickEvent;
    import com.smartgwt.client.widgets.events.ClickHandler;
    import com.smartgwt.client.widgets.form.DynamicForm;
    import com.smartgwt.client.widgets.form.fields.ComboBoxItem;
    import com.smartgwt.client.widgets.form.fields.FormItem;
    import com.smartgwt.client.widgets.grid.ListGrid;
    import com.smartgwt.client.widgets.grid.ListGridField;
    import com.smartgwt.client.widgets.grid.ListGridRecord;
    import com.smartgwt.client.widgets.grid.events.RecordClickEvent;
    import com.smartgwt.client.widgets.grid.events.RecordClickHandler;
    import com.smartgwt.client.widgets.layout.HLayout;
    import com.smartgwt.client.widgets.layout.VStack;
    import com.smartgwt.client.widgets.viewer.DetailViewer;
    
    /**
     * Entry point classes define <code>onModuleLoad()</code>.
     */
    public class BuiltInDS implements EntryPoint {
        private ListGrid boundList;
        private DynamicForm boundForm;
        private IButton saveBtn;
        private DetailViewer boundViewer;
        private IButton newBtn;
    
        /**
         * This is the entry point method.
         */
        public void onModuleLoad() {
            KeyIdentifier debugKey = new KeyIdentifier();
            debugKey.setCtrlKey(true);
            debugKey.setKeyName("D");
    
            Page.registerKey(debugKey, new PageKeyHandler() {
                public void execute(String keyName) {
                    SC.showConsole();
                }
            });
    
    
            ListGrid grid = new ListGrid();
            grid.setLeft(20);
            grid.setTop(75);
            grid.setWidth(130);
            grid.setLeaveScrollbarGap(false);
            grid.setShowSortArrow(SortArrow.NONE);
            grid.setCanSort(false);
            grid.setFields(new ListGridField("dsTitle", "Select a DataSource"));
            grid.setData(new ListGridRecord[]{
                    new DSRecord("Animals", "animals"),
                    new DSRecord("Office Supplies", "supplyItem"),
                    new DSRecord("Employees", "employees")}
            );
            grid.setSelectionType(SelectionStyle.SINGLE);
            grid.addRecordClickHandler(new RecordClickHandler() {
                public void onRecordClick(RecordClickEvent event) {
                    DSRecord record = (DSRecord) event.getRecord();
                    bindComponents(record.getDsName());
                }
            });
    
            grid.draw();
    
            VStack vStack = new VStack();
            vStack.setLeft(175);
            vStack.setTop(75);
            vStack.setWidth("70%");
            vStack.setMembersMargin(20);
    
            Label label = new Label();
            label.setContents("<ul>" +
                    "<li>select a datasource from the list at left to bind to these components</li>" +
                    "<li>click a record in the grid to view and edit that record in the form</li>" +
                    "<li>click <b>New</b> to start editing a new record in the form</li>" +
                    "<li>click <b>Save</b> to save changes to a new or edited record in the form</li>" +
                    "<li>click <b>Clear</b> to clear all fields in the form</li>" +
                    "<li>click <b>Filter</b> to filter (substring match) the grid based on form values</li>" +
                    "<li>click <b>Fetch</b> to fetch records (exact match) for the grid based on form values</li>" +
                    "<li>double-click a record in the grid to edit inline (press Return, or arrow/tab to another record, to save)</li>" +
                    "</ul>");
            vStack.addMember(label);
    
            boundList = new ListGrid();
            boundList.setHeight(200);
            boundList.setCanEdit(true);
    
            boundList.addRecordClickHandler(new RecordClickHandler() {
                public void onRecordClick(RecordClickEvent event) {
                    Record record = event.getRecord();
                    boundForm.editRecord(record);
                    saveBtn.enable();
                    boundViewer.viewSelectedData(boundList);
                }
            });
            vStack.addMember(boundList);
    
            boundForm = new DynamicForm();
            boundForm.setNumCols(6);
            boundForm.setAutoFocus(false);
            vStack.addMember(boundForm);
    
            HLayout hLayout = new HLayout(10);
            hLayout.setMembersMargin(10);
            hLayout.setHeight(22);
    
            saveBtn = new IButton("Save");
            saveBtn.addClickHandler(new ClickHandler() {
                public void onClick(ClickEvent event) {
                    boundForm.saveData();
                    if (!boundForm.hasErrors()) {
                        boundForm.clearValues();
                        saveBtn.disable();
                    }
                }
            });
            hLayout.addMember(saveBtn);
    
            newBtn = new IButton("New");
            newBtn.addClickHandler(new ClickHandler() {
                public void onClick(ClickEvent event) {
                    boundForm.editNewRecord();
                    saveBtn.enable();
                }
            });
            hLayout.addMember(newBtn);
    
            IButton clearBtn = new IButton("Clear");
            clearBtn.addClickHandler(new ClickHandler() {
                public void onClick(ClickEvent event) {
                    boundForm.clearValues();
                    saveBtn.disable();
                }
            });
            hLayout.addMember(clearBtn);
    
            IButton filterBtn = new IButton("Filter");
            filterBtn.addClickHandler(new ClickHandler() {
                public void onClick(ClickEvent event) {
                    boundList.filterData(boundForm.getValuesAsCriteria());
                    saveBtn.disable();
                }
            });
            hLayout.addMember(filterBtn);
    
            IButton fetchBtn = new IButton("Fetch");
            fetchBtn.addClickHandler(new ClickHandler() {
                public void onClick(ClickEvent event) {
                    boundList.fetchData(boundForm.getValuesAsCriteria());
                    saveBtn.disable();
                }
            });
            hLayout.addMember(fetchBtn);
    
            vStack.addMember(hLayout);
    
            boundViewer = new DetailViewer();
            vStack.addMember(boundViewer);
    
            vStack.draw();
        }
    
        private void bindComponents(String dsName) {
            DataSource ds = DataSource.get(dsName);
            boundList.setDataSource(ds);
            boundViewer.setDataSource(ds);
            boundForm.setDataSource(ds);
            
    // *** MODIFICATION #1 START
            FormItem combo = new FormItem("commonName");
            boundForm.setFields(combo);    
    // *** MODIFICATION #1 END
    
            
            boundList.fetchData();
            newBtn.enable();
            saveBtn.disable();
        }
    }

    #2
    Thanks for the notification. We see the problem you describe and are taking a look.
    We'll follow up when we have more information for you.

    Regards
    Isomorphic Software

    Comment


      #3
      We're having some internal discussion about how best to address this one. It may take a while to get fully ironed out, but in the meantime, here's a workaround you can use.
      Instead of using the setFields(FormItem...) signature, you can use the signature that takes a JavaScriptObject.
      Something like this should do it:

      Code:
      // *** MODIFICATION #1 START
              FormItem combo = new FormItem("commonName");
      //        boundForm.setFields(combo);           
              boundForm.setFields(combo.getJsObj());
              
      // *** MODIFICATION #1 END
      We'll follow up again when we have anything further for you

      Regards
      Isomorphic Software

      Comment


        #4
        Great, thanks - I'll give that a whirl which, since it's in a self-contained class, may be all we need to resolve the current problem in our software.

        [UPDATE] Unfortunately not. Of course, the IpCodeNameCombobox is self-contained, but there are numerous fields in multiple datasources that use it on various screens throughout our application. Most of these screens use the setFields() signature where we pass a list of .FormItems, whereas we can only pass a single JavaScriptObject. Although we could get around this and I've confirmed your workaround does work, it would be pretty invasive for us. Unless you tell me that you can't or won't fix it, then, I'll wait until there's a fix in the library, but thanks for pointing out that a workaround does exist.

        Regards,
        Gary
        Last edited by godonnell_ip; 13 Oct 2016, 10:13.

        Comment


          #5
          We've spent some time debugging this and there were 2 things at play
          Firstly, there was a bad assumption in your original code. Your "me" variable set up here:
          Code:
             private void init()
              {
                  final ComboBoxItem me = this;
          
                  setInitHandler(new FormItemInitHandler()
                  {
                      @Override
                      public void onInit(FormItem itemIn)
                      {
                          me.setPickListFields(
                                  new ListGridField(me.getValueField()),
                                  new ListGridField(me.getDisplayField()));
                          me.setFilterFields(me.getValueField(), me.getDisplayField());
                      }
                  });              
              }
          Isn't guaranteed to refer to the live form-item created on the page in some scenarios.

          The most common case where this comes up is using a FormItem as a template to auto-generate other specific items as in the 'editorProperties' applied to a ListGridField or similar, but this can also arise when you're using reflection to have SmartClient code build an instance of a SmartGWT class at runtime.

          Instead of doing this, you should use the form item passed into onInit as an argument.

          One problem with this - while the item passed in is the "live" item, it's not guaranteed to be of the expected Java class. (Typically it will be but in some cases, including your code, it won't). To workaround this you can do a pseudo cast to the appropriate Java class by creating a new instance of the class you want and passing in the JsObj from the live item. So the pattern becomes:
          Code:
             private void init()
              {
          
                  setInitHandler(new FormItemInitHandler()
                  {
                      @Override
                      public void onInit(FormItem itemIn)
                      {
                          ComboBoxItem me = new ComboBoxItem(itemIn.getJsObj());
                          me.setPickListFields(
                                  new ListGridField(me.getValueField()),
                                  new ListGridField(me.getDisplayField()));
                          me.setFilterFields(me.getValueField(), me.getDisplayField());
                      }
                  });              
              }
          Lastly - you need to be sure you're testing this on a recent (or the latest) nightly build. We have resolved an issue in this area somewhat recently whereby the itemIn passed in could refer to the same object as the "me" in the original code rather than the live form item. We think this pattern should resolve this issue for you. Please let us know if you continue to have problems with this.

          Regards

          Isomorphic Software
          Last edited by Isomorphic; 20 Oct 2016, 21:52. Reason: Formatting Fixes

          Comment


            #6
            Thank you very much for the comprehensive response. Ouch! on the me thing...

            You're right that we have new libs now that I can test this against (there's been a glitch of some kind that meant we new Power Edition libraries were not being generated during your nightly build process, but that's all working now), so I will happily try again with the most recent ones.

            I'll let you know how that goes. Thanks again.

            [UPDATE]
            Well, I can confirm it doesn't work just by updating the libraries, but by also modifying to use the pattern you advised the issue is now resolved in my test case. I'll be trying in the application shortly, but am fairly confident that it will also work there. Thanks a million for your close attention to this!

            Regards,
            Gary
            Last edited by godonnell_ip; 21 Oct 2016, 08:19.

            Comment

            Working...
            X