Announcement

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

    DynamicForm.getValue()

    Hi,

    As part of our effort to migrate from version 8.2p to version 8.3p, we encountered the following issue ...

    The Problem:
    ---------------

    Within our ValuesManager, nested data objects get cleared when ValuesManager's setValues() gets called.

    The unexpected field clearing of nested data attributes, occurs right when
    this._setMemberValues(this.members[i]) gets called, within the ValuesManager.setValues() method:

    Code:
        setValues : function (values) {
            if (isc.isAn.Array(values)) {
                var useFirst = isc.isA.Object(values[0]);
                this.logWarn("values specified as an array." +
                            (useFirst ? " Treating the first item in the array as intended values."
                                      : " Ignoring specified values."));
                if (useFirst) values = values[0];
                else values = null;
            }
    
    
    
            // Duplicate the values object so we can manipulate it and apply it directly to
            // this.values and modify without interfering with external code.
            // _duplicateValues does a recursive duplication based on dataPaths
            var clonedVals = {};
            isc.DynamicForm._duplicateValues(this, values, clonedVals);
            values = clonedVals;
    
            this.values = values;
            if (this.members) {
                for (var i = 0; i < this.members.length; i++) {
                    // setMemberValues will update the members' items to display the values passed in
                    // Note that for DynamicForms, it also explicitly calls 'clearValue()' on items
                    // for which we have no member - this re-evaluates default values
                    this._setMemberValues(this.members[i]);
                }
            }
            // remember values for resetting
            this.rememberValues();
    
        },
    Findings:
    ----------

    We determined that this occurs because one of our custom component's getValue() is not being called as it used
    to be in 8.2p. This custom component is a collection holder (listGrid), for which the data is provided from a
    nested XML payload.

    We narrowed down the following change as potentially being responsible for our getValue() not being called:

    DynamicForm.getValue() in 8.2p
    Code:
    getValue : function (fieldName) {
        // This check for item.getValue() should be unnecessary, since this.values is kept in synch 
        // with the values of each form item
    	var item = this.getItem(fieldName);
        if (item && isc.isA.Function(item.getValue)) return item.getValue();
    
        return this._getValue(fieldName);
    },
    DynamicForm.getValue in 8.3p
    Code:
    getValue : function (fieldName) {
    
         var item = this.getItem(fieldName);
         if (item) {
             var fieldName = item.getTrimmedDataPath() || item.name;
         }
        return this._getValue(fieldName);
    },
    In 8.3p, this._getValue(fieldName) ends up being returned in all situations, whereas in 8.2p,
    item.getValue() could be returned instead, if/when applicable.

    Was this intended, because it's breaking our logic?

    Please advise as to what should be done in regards to this issue.

    Thanks,
    Last edited by yavery; 23 Jul 2013, 12:56.

    #2
    Any update on this one ?

    Thanks,

    Comment


      #3
      We're looking into it. We'll let you know what we find.

      Regards
      Isomorphic Software

      Comment


        #4
        Hi Yan
        This appears to be a usage issue but it's a bit hard to pin down exactly what you're doing that's tripping up the system from the information we have. Our best guess is that you have code which is relying on an implementation quirk of 8.2 which has subsequently changed (thus breaking your app code).

        The getValue() logic was changed intentionally as part of the "Opaque" data type feature introduced in 8.3 (See the getAtomicValue() / updateAtomicValue() SimpleType methods).
        Without going into detail - one implication of this feature is that if you're working with a SimpleType with an explicit atomicValue extractor / updator, the result of a call to "item.getValue();" would not match a call to "form.getValue(<fieldName>);" on the form containing the item. As such the 'getValue()' method on the DynamicForm class had to be reworked to avoid falling through to the item-level implementation.

        However it's unclear to us why this would be breaking your usage. Your DynamicForm's values should be being kept in synch with your items' individual values, so we're not seeing how they could get decoupled in a way that could cause the bugs you're describing.
        Would you be able to show us a little test case that we can run which exhibits the problem?

        Thanks
        Isomorphic Software

        Comment


          #5
          I'll further investigate and see what I come up with.

          Thanks,

          Comment


            #6
            At this point, I'm not able to provide a reproducible example, but I've narrowed it down
            to the following (ISC_Forms @ ValuesManager::_setMemberValues @ line ~53,770),
            which I don't quite understand:

            Code:
                // Pick the value back up from the item
                // This will re-evaluate defaults on items, and potentially perform other
                // modification such as type-casting, so store the item's value again here
                if (item.shouldSaveValue != false) {
            
                  var updatedFieldVal = member.getValue(itemPath);
            
                  if (updatedFieldVal === undef) {
                     isc.DynamicForm._clearFieldValue(fullFieldPath, this.values, member, true);
                  } else {
                     isc.DynamicForm._saveFieldValue(fullFieldPath, null,
                                                                     updatedFieldVal,
                                                                     this.values, member, true);
                  }
            
                 hasChanges = true;
            }
            In the above code, "this" is my ValuesManager, "item" is my custom component and "member" is
            the DynamicForm holding my custom component.

            Before this piece of code runs, "this.values" contains the following data:

            Code:
            AccountRelation: Array[1]
            Code: "A0016"
            DimensionLevel_Code: "AC1"
            DistributionChannel_Code: "BANNER"
            EffEndDate: null
            EffStartDate: null
            GenerateScanSales: false
            InsertUser_Code: "000000"
            InsertWhen: Wed Jul 24 2013 13:33:12 GMT-0400 (EDT)
            Name: "Kroger Banners"
            PackageType_Code: ""
            SimilarTo_Code: ""
            SimilarTo_Name: ""
            UpdateUser_Code: "000000"
            UpdateWhen: Wed Jul 24 2013 13:33:12 GMT-0400 (EDT)
            Version: 1
            The problem is that when member.getValue(itemPath) gets called with "AccountRelation" as itemPath, which is
            the nested data object bound to my custom component, "undefined" is returned and the code just after issues
            a _clearFieldValue() on that item and from that point on, this.values no longer contains the "AccountRelation"
            data item.

            Does that help in any way explaining what's going on ?

            Here are the "key" methods to my custom component, in case this could reveal anything wrong in our approach:

            Code:
            	//----------------------------------------------------------------------------------------------------------------------------
            	/**
            	 * Bridge 'out' for extracting data from inner listGrid component.
            	 * 
            	 * @inner
            	 * @return {Object} data
            	 */
            	//----------------------------------------------------------------------------------------------------------------------------
            	getValue : function()
            	{
            		return this.canvas.objListGrid.data
            	},
            
            	//----------------------------------------------------------------------------------------------------------------------------
            	/**
            	 * Bridge 'in' for setting data into inner listGrid component.
            	 * 
            	 * @inner
            	 * @param {Object} value data
            	 * @return {void}
            	 */
            	//----------------------------------------------------------------------------------------------------------------------------
            	setValue : function(value)
            	{
            		this.canvas.setData(value);
            	},
            At this point, this is the best I can provide. Other than that, I guess we'd need to debug thru the code together ...

            Let me know. Thanks,

            Comment


              #7
              Is anyone further investigating this?

              Could tracing/debugging code together (via Skype or other) be an option since I'm not able to provide a reproducible scenario?

              Thanks,

              Comment


                #8
                Hi,

                This issue is preventing me from migrating to SmartClient 8.3p which is a requirement for us in
                order to support Microsoft Internet Explorer 10.

                Our GO LIVE date is coming very quickly and I'd like to setup an action plan to get this resolved ASAP.

                Can someone please propose an approach to get this one figured out?

                Thanks,

                Comment


                  #9
                  Sorry for the delay and frustration.
                  Without seeing the problem in action it's hard to do more than hypothesize as to the cause.

                  The code snippet you've posted looks like perhaps you've attempted to implement a CanvasItem with a simple override to "getValue()" / "setValue()" rather than using the "storeValue()" and "updateValue" APIs described in the docs:

                  If you set shouldSaveValue:true, CanvasItem.showValue() will be called to provide a value that your item should display. Implement showValue() and call methods on the Canvas you've created to cause the value to be displayed.

                  showValue() will be called in various situations where the form receives data, including a call to DynamicForm.setValues(), DynamicForm.editRecord(), or if DynamicForm.fetchData() is called and a Record is returned. Bear in mind that showValue() can be called when the form and your item have not yet been drawn; in this case, store the value for later display.

                  To provide a value to the form, call CanvasItem.storeValue() whenever the user changes the value in your Canvas. Note that the form will not call canvasItem.getValue() in order to discover your item's value, so there is no purpose in overriding this method - use storeValue to proactively inform the form about changes instead. This approach is necessary in order to enable change events.

                  If you cannot easily detect changes to values in your Canvas, a workaround is to call storeValue right before the form saves.
                  Here's a demonstration of this approach we put together to verify this all works as expected, which seems like it should roughly match your use case (based on your description)
                  Code:
                  isc.setAutoDraw(false);
                  
                  isc.defineClass("CustomGridEditor", isc.CanvasItem);
                  isc.CustomGridEditor.addProperties({
                      shouldSaveValue:true,
                      createCanvas:function () {
                          if (this.canvas == null || this.canvas.objListGrid == null) {
                              this.canvas = isc.Canvas.create({
                                  backgroundColor:"gold"
                              });
                              this.canvas.objListGrid = isc.ListGrid.create({
                                  top:10, left:10,
                                  fields:[{name:"field1"}],
                                  // Store changes on edits
                                  canEdit:true,
                                  targetItem:this,
                                  cellChanged:function (record, newValue, oldValue, rowNum, colNum, grid) {
                                      grid.targetItem.storeValue(grid.getData());
                                  }
                              });
                              this.canvas.addChild(this.canvas.objListGrid);
                          }
                          return this.canvas;
                      },
                      // Implement a "showValue" handler to display the array
                      // in the ListGrid
                      showValue : function (displayValue, value) {
                          this.logWarn("Show value passed:" + this.echo(value));
                          if (isc.isAn.Array(value)) {
                              this.canvas.objListGrid.setData(value);
                          } else {
                              this.canvas.objListGrid.setData(null);
                          }
                      }
                  });
                  
                  // Pseudo-application code - valuesManager, containing
                  // a DynamicForm, which is showing a custom CanvasItem editor
                  isc.ValuesManager.create({
                     ID:"vm",
                     members:[
                         isc.DynamicForm.create({
                              ID:"df",
                              items:[
                                 {editorType:"CustomGridEditor", name:"customField"
                                 }
                              ]
                         })
                     ]
                  });
                  
                  // Populate the field we're interested in with an array
                  vm.setValues({
                      customField:[
                          {field1:"v1"},
                          {field1:"v2"}
                      ]
                  });
                  
                  // test explicit call to getValues() / getValue()
                  isc.Button.create({
                      top:400,
                      autoDraw:true,
                      title:"Show Vals",
                      click:function ()  {
                          isc.say(isc.Log.echo(vm.getValues()) + "<BR><BR>" + 
                                  "customField full value:" + isc.Log.echoAll(vm.getValue("customField")) 
                                  );
                      }
                  });
                  df.draw();
                  Perhaps this will be enough to get things working for you?
                  If not, perhaps you could use this as a starting point for a sample showing the problem - perform minimal modifications to bring it closer to your real use case such that it shows the problem and show it to us.

                  Lastly, we could possibly set up some consulting time to have a developer debug your live application. If you'd like to proceed down this route, please contact us via email and we can set something up.

                  Regards
                  Isomorphic Software

                  Comment


                    #10
                    Hi,

                    Preliminary testing shows that using the showValue()/storeValue() approach
                    instead of setValue()/getValue() fixes that particular issue, although it did break
                    a few other areas of the application, which I'm trying to fix right now …

                    At least my issue with the disappearing nested data element is now resolved. Once
                    I'm done, we'll need to regression test all of this to make sure nothing else got
                    broken by this change and that there are no side effects.

                    Thanks for taking some time to look into this and providing help …

                    Comment

                    Working...
                    X