Announcement

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

    Disconnect Between Docs and Implementation

    I think I may have stumbled across a disconnect between your documentation and the actual implementation. This relates to v11.1, so it's possible you have already caught this and corrected it in later builds but I thought I'd mention it just in case...


    I believe there is no way to explicitly specify the parentId field to use with a TreeGrid and it assumes that the first field with a foreignKey on the TreeGrid's datasource is the parentID.

    However, see https://www.smartclient.com/smartcli...reeDataBinding, which says:
    To declare the Tree.parentIdField, declare a DataSourceField.foreignKey field with the name of the primaryKey field.

    ...and the following logic that doesn't quite implement this approach. It instead uses the first field with a foreignKey attribute, not the (first) field with a foreignKey attribute that points to the primaryKey field:

    Code:
    [INDENT][B]  getTreeRelationship : function (parentDS, foreignKeyFieldName, multiple) {[/B]
    
    [B]      // make sure we have DS instance[/B]
    [B]      if (isc.isA.String(parentDS)) parentDS = this.getSchema(parentDS);[/B]
    
    [B]      // find multiple foreign key fields if requested[/B]
    [B]      var foreignNames, targetNames;[/B]
    [B]      if (multiple && parentDS) {[/B]
    [B]          foreignNames = [];[/B]
    [B]          targetNames = [];[/B]
    [B]      }[/B]
    
    [B]      // if the name of the foreignKey wasn't passed in, autodetect it by looking for the first[/B]
    [B]      // field on this ds with a foreignKey pointing at the appropriate dataSource.[/B]
    [B]      var fields = this.getFields();[/B]
    [B]      if (foreignKeyFieldName == null) {[/B]
    [B]          for (var fieldName in fields) {[/B]
    [B]              var currentField = fields[fieldName];[/B]
    [B]              if (currentField.foreignKey != null) {[/B]
    [B]                  // If we were passed no parentDS and no foreignKeyFieldName, always use the[/B]
    [B]                  // first field with a specified foreignKey[/B]
    [B]                  if (!parentDS ||[/B]
    [B]                      (parentDS.getID() == isc.DataSource.getForeignDSName(currentField, this)))[/B]
    [B]                  {[/B]
    [B]                      if (!foreignKeyFieldName) foreignKeyFieldName = fieldName;[/B]
    [B]                      // if we're not returning all the foreign key fields, bail out[/B]
    [B]                      if (foreignNames) foreignNames.add(fieldName);[/B]
    [B]                      else              break;[/B]
    [B]                  }[/B]
    [B]              }[/B]
    [B]          }[/B]
    [B]      }
    [etc..][/B][/INDENT]
    The effect of this for me is as follows (I'll be working around this now that I've identified ).

    Since our datasources go through some transformation dynamic loading, the first such field in the base definition of our MerchandiseHierarchyGroup datasource is ItemSellingRuleID and so the TreeGrid is picking up that fieldas being the parentIDField instead of the preferred parent field. That causes it to generate this invalid criteria in requests to fetch children of the expanded node and therefore returns the entire tree again:[

    Code:
    [INDENT][B]{[/B]
    [B]  "actionURL":"http://127.0.0.1:8080/SmartMDMTest/coreiso/sc/IDACall",[/B]
    [B]  "showPrompt":true,[/B]
    [B]  "prompt":"Finding Records that match your criteria...",[/B]
    [B]  "transport":"xmlHttpRequest",[/B]
    [B]  "promptStyle":"cursor",[/B]
    [B]  "bypassCache":true,[/B]
    [B]  "data":{[/B]
    [B]      "criteria":{[/B]
    [B]          "ItemSellingRuleID":null[/B]
    [B]      },[/B]
    [B]      "operationConfig":{[/B]
    [B]          "dataSource":"MerchandiseHierarchyGroup",[/B]
    [B]          "repo":null,[/B]
    [B]          "operationType":"fetch",[/B]
    [B]          "textMatchStyle":"exact"[/B]
    [B]      },[/B]
    [B]      "componentId":"isc_TreeGrid_0",[/B]
    [B]      "appID":"builtinApplication",[/B]
    [B]      "operation":"fetchMerchTree",[/B]
    [B]      "oldValues":{[/B]
    [B]          "ItemSellingRuleID":null[/B]
    [B]      },[/B]
    [B]      "progressiveLoading":false,[/B]
    [B]      "resultTreeIdField":"ItemSellingRuleID",[/B]
    [B]      "resultTreeParentIdField":"ItemSellingRuleID"[/B]
    [B]  }[/B]
    [B]}[/B][/INDENT]
    Regards,
    Gary O'Donnell
    Last edited by godonnell_ip; 3 Mar 2020, 07:30.

    #2
    Hi Gary,

    The logic you are pointing to appears correct - note how it checks the foreignDS name on the foreignKey field against the passed-in parentDS. So it is definitely checking that the foreignKey points to a specific DataSource.

    Tree relationships can be asked for either within-DS, in which case the passed parentDS would be expected to be the same as “this”, or can be cross-DS, but the latter is a special case.

    Since there’s nothing wrong with the logic here, if you think you’ve found a framework bug, we need a test case that shows the problem.

    Comment


      #3
      Thanks for the quick reply.

      I do see how it checks against the parentDS, but I observed that the call to getTreeRelationship() was being made as follows with no parentDS being passed in See the last line in this snippet from ISC_DataBinding.js:

      Code:
      init : function (a,b,c,d,e,f) {
          // create a pointer to us in the global context
          isc.ClassFactory.addGlobalID(this);
      
          if (!this.criteria) this.criteria = {};
      
          if (!this.operation) this.operation = {operationType : "fetch"};
      
          // dataSource can be specified either on the operation or the ResultTree.
          if (!this.dataSource) this.dataSource = this.operation.dataSource;
          if (!this.operation.dataSource) this.operation.dataSource = this.dataSource;
      
      
          if (isc.isAn.Array(this.dataSource)) {
              this.dataSource = this.dataSource[0];
              this.operation.dataSource = this.dataSource;
          }
      
          // If any of rootValue, idField, parentIdField are not explicitly specified on this
          // ResultTree, autodetect them from the DataSource relationship.
          if (!this.isMultiDSTree()) {
      
              // root node has to exist for getTreeRelationship to work, so create it now if it
              // doesn't exist
              if (!this.root) this.root = this.makeRoot();
              var relationship = this.getTreeRelationship(this.root);
          [...etc...]
      ...which then executes:

      Code:
      getTreeRelationship : function (parentNode) {
          var childDS = this.getChildDataSource(parentNode);
          // ask the datasource for a tree relationship, which can be declared explicitly or
          // autodetected from field declarations
          var relationship = childDS.getTreeRelationship();
          return relationship;
      }
      ...which finally ends up in the method I included in my first post.

      I have worked around this by modifying my dynamic datasource generator to move the first field that references it's own primaryKey field to be the first in the definition we return. I realize this won't work for relating to alternate child datasources, but this isn't currently a use case for us.

      Comment


        #4
        An example of it behaving this way is your own Tree Load on Demand example.

        You can see that my breakpoint (on the line equivalent to if (!foreignKeyFieldName) foreignKeyFieldName = fieldName; in my original post) has been hit. This is the first field with a foreignKey definition which in this case is only coincidentally in this case "a DataSource.foreignKey field with the name of the primaryKey field."

        Click image for larger version

Name:	Screenshot 2020-03-03 at 5.10.24 PM.png
Views:	106
Size:	57.0 KB
ID:	261264

        As I mentioned before, I don't really need help fixing this, thanks. I just wanted to point out that you may agree there is a discrepancy between your documentation and the actual implementation.

        Comment


          #5
          Hmmm - unless I'm misunderstanding and your documentation means any field with a foreignKey specification, and not just one that matches the primaryKey of this datasource...?

          Or perhaps I overlooked something that describes how to specify the parentId field within the datasource definition (I noticed documentation for just that it in the documentation for TreeNode, but didn't immediately click how that might be relevant to the datasource definition).
          Last edited by godonnell_ip; 3 Mar 2020, 09:24.

          Comment


            #6
            A TreeGrid that is looking at just a single DataSource is intended to find the first self-referencing FK field in your DS (technically, doesn't need to point to a PK, just a field with unique values). We'll be double-checking that it does this, based on your comments.

            Note that if you have multiple such self-referencing FK fields, the correct approach to selecting which one to use is to set ResultTree.parentIdField via TreeGrid.dataProperties. Regardless of whether we uncover a real bug here or not, you could use this approach rather than re-ordering you fields definitions.

            Comment


              #7
              Ah, I much prefer explicitly setting the parentIdField on the TreeGrid itself. I think the piece I was missing was the use of TreeGrid.dataProperties to do that, but now that I look at it I see the documentation to do what I needed was there all the time :-)

              Thank you!

              Comment


                #8
                While researching something else I came across this old post that suggests the functionality is behaving as expected - it certainly seems to be behaving as described in the post, which makes me think the TreeDataBinding documentation is merely ambiguous rather than incorrect.

                Comment


                  #9
                  The intent in this case was that we should use the first foreignKey in the DataSource definition that refers to another field in that same DataSource. This may be a primary key, but all that's required is that the field have unique values across all records.

                  We've updated the Tree DataBinding documentation topic.

                  Comment

                  Working...
                  X