hamzahamidi/ajsf

Only part of data are displayed for array of arrays

Closed this issue · 11 comments

jma commented

Describe the bug
which template:

  • MaterialDesignFrameworkModule — Material Design
  • Bootstrap3FrameworkModule — Bootstrap 3
  • Bootstrap4FrameworkModule — Bootstrap 4
  • NoFrameworkModule — plain HTML
  • Other (please specify below)

A clear and concise description of what the bug is.

To Reproduce
Steps to reproduce the behavior:

  1. Go to the demo/example site (https://hamidihamza.com/Angular6-json-schema-form/)
  2. Set the configuration as follows:
{
    "schema": {
        "type": "object",
        "properties": {
            "person": {
                "title": "People",
                "type": "array",
                "items": {
                    "type": "object",
                    "properties": {
                        "name": {
                            "type": "array",
                            "items": {
                                "title": "Name",
                                "type": "string"
                            }
                        }
                    }
                }
            }
        }
    },
    "layout": [{
        "key": "person",
        "items":[{
            "key": "person[]",
            "items": [{
                "key": "person[].name",
                "items": [{
                    "key": "person[].name[]"
                }]
            }]
        }]
    }],
    "data": {
        "person": [
            {
                "name": [
                    "name1",
                    "name2"
                    ]
            }
        ]
    }
}
  1. Only the first value for name is shown

Note: removing the layout part fix the problem.

Hi, I think i have the same problem with a config I make for an harmony plugin. Here is a minimal sample to reproduce on https://hamidihamza.com/Angular6-json-schema-form/ .

{
    "schema": {
      "type": "object",
      "properties": {
        "remoteOverrideCommandsList": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "ActivityName": {
                "type": "string"
              },
              "CommandsList": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "CommandName": {
                      "title": "Command Name",
                      "type": "string"
                    },
                    "NewCommand": {
                      "title": "New Command",
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "layout": [
              {
                "key": "remoteOverrideCommandsList",
                "type": "tabarray",
                "items": [
                  "remoteOverrideCommandsList[].ActivityName",
                  {
                    "type": "array",
                    "items": [
                        "remoteOverrideCommandsList[].CommandsList[].CommandName",
                        "remoteOverrideCommandsList[].CommandsList[].NewCommand"
                    ]
                  }
                ]
              }
    ],
    "data": {
      "remoteOverrideCommandsList": [
          {
              "ActivityName": "Activity1",
              "CommandsList": [
                  {
                      "CommandName": "REWIND",
                      "NewCommand": "new command 1"
                  },
                  {
                      "CommandName": "FAST_FORWARD",
                      "NewCommand": "new command 2"
                  }
              ]
          }
      ]
    }
  
  }

What is odd is that if I have 2 objects in the first array, with 2 properties, it loads successfully !

    "data": {
         "remoteOverrideCommandsList": [
          {
              "ActivityName": "Activity1",
              "CommandsList": [
                  {
                      "CommandName": "REWIND",
                      "NewCommand": "new command 1"
                  },
                  {
                      "CommandName": "FAST_FORWARD",
                      "NewCommand": "new command 2"
                  }
              ]
          },
                    {
              "ActivityName": "Activity2",
              "CommandsList": [
                  {
                      "CommandName": "REWIND",
                      "NewCommand": "new command 3"
                  },
                  {
                      "CommandName": "FAST_FORWARD",
                      "NewCommand": "new command 4"
                  }
              ]
          }
      ]
    }

If the second object has only 1 property, it loads only the firston the first object (but adding will fill the form with the correct values).

    "data": {
         "remoteOverrideCommandsList": [
          {
              "ActivityName": "Activity1",
              "CommandsList": [
                  {
                      "CommandName": "REWIND",
                      "NewCommand": "new command 1"
                  },
                  {
                      "CommandName": "FAST_FORWARD",
                      "NewCommand": "new command 2"
                  }
              ]
          },
                    {
              "ActivityName": "Activity2",
              "CommandsList": [
                  {
                      "CommandName": "REWIND",
                      "NewCommand": "new command 3"
                  }
              ]
          }
      ]
    }

I have same issues ,too。
+1

zahmo commented

I've added a sort of workaround patch

json-schema-form-patch.directive.ts.zip

to use it, you need to add the directive json-schema-form-patch.directive.ts from the attachment
to your Angular project (remembering to also add JsonSchemaFormPatchDirective it to your module declarations).
Then add 'jsfPatch' to your <json-schema-form> tag
it should look something like this:
<json-schema-form jsfPatch
...
...
...
...
...
>
</json-schema-form>

I've added a sort of workaround patch

json-schema-form-patch.directive.ts.zip

to use it, you need to add the directive json-schema-form-patch.directive.ts from the attachment
to your Angular project (remembering to also add JsonSchemaFormPatchDirective it to your module declarations).
Then add 'jsfPatch' to your <json-schema-form> tag
it should look something like this:
<json-schema-form jsfPatch
...
...
...
...
...
>
</json-schema-form>

Thanks, it works for array in array.

but array in array in array not work.

zahmo commented

I've added a sort of workaround patch
json-schema-form-patch.directive.ts.zip
to use it, you need to add the directive json-schema-form-patch.directive.ts from the attachment
to your Angular project (remembering to also add JsonSchemaFormPatchDirective it to your module declarations).
Then add 'jsfPatch' to your <json-schema-form> tag
it should look something like this:
<json-schema-form jsfPatch
...
...
...
...
...
>
</json-schema-form>

Thanks, it works for array in array.

but array in array in array not work.

Hi, I've tested with with this form and it seems to be displaying all of the items:

{
    "schema": {
        "type": "object",
        "properties": {
            "nestedArrays": {
                "type": "array",
                "title": "level1",
                "items": {
                    "type": "array",
                    "title": "level2",
                    "items": {
                        "type": "array",
                        "title": "level3",
                        "items": {
                            "type": "array",
                            "items": {
                                "type": "array",
                                "title": "level4",
                                "items": {
                                    "type": "string"
                                }
                            }
                        }
                    }
                }
            }
        }

    },
    "layout": [
        {
            "key": "nestedArrays",
            "items": [{
                "key": "nestedArrays[]",

                "items": [{
                    "key": "nestedArrays[][]",
                    "type": "array",
                    "items": [{
                        "key": "nestedArrays[][][]",
                        "items": [{
                            "key": "nestedArrays[][][][]",
                            "items": [{

                                "key": "nestedArrays[][][][][]",
                            }]
                        }]

                    }]

                }]
            }]
        }
    ],
    "data": {
        "nestedArrays": [
            [
                [
                     [
                         ["111-1","111-2"]
                     ],
                     [
                         ["112-3","112-4","112-5"]
                     ],
                     [
                         ["113-6"]
                     ]
                ],
                [
                     [
                         ["121-7","121-8"]
                     ],
                     [
                         ["122-9","122-10","122-11"]
                     ]
                ]
            ],
            [
                [
                     [
                         ["211-12"],["212-13","212-14"] 
                     ]
                ]
            ]
                
        ]
    }

}

you can maybe post your form

For example:when view the data in schema form ,the ui display error,the js error :form-group.functions.ts line 528 ,529,530
{ "a1": [{ "a2": [{ "a3": [{ "a4": { "name": "name1-1-1-1" } }, { "a4": { "name": "name1-1-1-2" } }, { "a4": { "name": "name1-1-1-3" } }] }, { "a3": [{ "a4": { "name": "name1-1-2-1" } }, { "a4": { "name": "name1-1-2-2" } }] }, { "a3": [{ "a4": { "name": "name1-1-3-1" } }] }] }, { "a2": [{ "a3": [{ "a4": { "name": "name1-2-1-1" } }, { "a4": { "name": "name1-2-1-2" } }] }] }] }

Hey guys, this is the same issue as #47 as far as I can tell.

From my own research this is what I've found: (copy pasting the same response from that issue):

This problem is being caused by a tandem of these two things inside of buildLayout:
code piece 1:

var nodeValue = JsonPointer.get(jsf.formValues, newNode.dataPointer.replace(/\/-/g, '/1'));

and code piece 2:

// Add any additional default items
                if (!newNode.recursiveReference || newNode.options.required) {
                    var arrayLength = Math.min(Math.max(newNode.options.tupleItems + newNode.options.listItems, isArray(nodeValue) ? nodeValue.length : 0), newNode.options.maxItems);
                    for (var i = newNode.items.length; i < arrayLength; i++) {
                        newNode.items.push(getLayoutNode({
                            $ref: itemRefPointer_1,
                            dataPointer: newNode.dataPointer,
                            recursiveReference: newNode.recursiveReference,
                        }, jsf, widgetLibrary));
                    }
                }

Based on how this creates layouts, it goes bottom up. If you have a child array, it will create its layout first. Then when it gets to the parent array, it will use code piece 2 to add items to the parent array based on the child it already created. It only creates one version of the child, which is where code piece 1 comes in. If you create data that has two elements in the parent and the second parent item has elements in the child array, the child array of the first parent element will have the same number of items as the second element. (This is because it's replacing the dataPointer dashes with a 1 always and not 0 or a correct index).

I am currently investigating a fix for this! Will respond back soon!

Hey guys, I've coded somewhat of a workaround, I'm not entirely sure it's worth the pull request until we can figure out a possible better fix, so I'll post it here.

I've added a new repairLayoutData function that descends the created layout and fixes it based on the data associated with it. This fix requires that you have these commits in your code: https://github.com/hamzahamidi/ajsf/pull/68/commits

You will need to change arrayLength to 0 (this prevents it from adding any items except the buttons):

// Add any additional default items
        if (!newNode.recursiveReference || newNode.options.required) {
          const arrayLength = 0;
          for (let i = newNode.items.length; i < arrayLength; i++) {
            newNode.items.push(getLayoutNode({
              $ref: itemRefPointer,
              dataPointer: newNode.dataPointer,
              recursiveReference: newNode.recursiveReference,
            }, jsf, widgetLibrary));
          }
        }

This exert is from the bottom of the buildLayout function to show you where to place repairLayoutData.

repairLayoutData(jsf, widgetLibrary, formLayout);
  if (jsf.hasRootReference) {
    const fullLayout = cloneDeep(formLayout);
    if (fullLayout[fullLayout.length - 1].type === 'submit') { fullLayout.pop(); }
    jsf.layoutRefLibrary[''] = {
      _id: null,
      dataPointer: '',
      dataType: 'object',
      items: fullLayout,
      name: '',
      options: cloneDeep(jsf.formOptions.defautWidgetOptions),
      recursiveReference: true,
      required: false,
      type: 'section',
      widget: widgetLibrary.getWidget('section'),
    };
  }
  if (!hasSubmitButton) {
    formLayout.push({
      _id: uniqueId(),
      options: { title: 'Submit' },
      type: 'submit',
      widget: widgetLibrary.getWidget('submit'),
    });
  }
  return formLayout;

repairLayoutData method:

function repairLayoutData(jsf, widgetLibrary, layoutItem, dataIndex?) {

  let nodeValue;
  let newDataIndex = dataIndex ? [...dataIndex] : [];
  let path;
  if (layoutItem) {
    for (let i = 0; i < layoutItem.length; i++) {
      if (layoutItem[i].items) {
        if (layoutItem[i].arrayItem) { // If item is arrayItem, add to dataIndex
          newDataIndex = dataIndex ? [...dataIndex, i] : [i];
        }
        if (layoutItem[i].type === 'array') { // If item is array, get data for the array
          path = layoutItem[i].dataPointer;
          if (dataIndex) {
            for (let j = 0; j < dataIndex.length; j++) {
              path = path.replace('-', dataIndex[j]);
            }
          }
          nodeValue = JsonPointer.get(jsf.formValues, path); // Get value for array
        }
        if (layoutItem[i].options.minItems > 0) { // Add Items if there's a minimum
          for (let k = 0; k < layoutItem[i].options.minItems; k++) {
            layoutItem[i].items.unshift(getLayoutNode({
              $ref: layoutItem[i].dataPointer + '/-',
              dataPointer: layoutItem[i].dataPointer + '/-',
              recursiveReference: layoutItem[i].recursiveReference,
            }, jsf, widgetLibrary));
          }
        }

        if ((Array.isArray(nodeValue) && nodeValue.length > 0)) { // Add necessary items to array item
          for (let k = layoutItem[i].options.minItems; k < nodeValue.length; k++) {
            if (k < layoutItem[i].options.maxItems) {
              layoutItem[i].items.unshift(getLayoutNode({
                $ref: layoutItem[i].dataPointer + '/-',
                dataPointer: layoutItem[i].dataPointer + '/-',
                recursiveReference: layoutItem[i].recursiveReference,
              }, jsf, widgetLibrary));
            }
          }
        }
        if (layoutItem[i]) {
          // Loop through new children for any new arrays in them
          layoutItem[i].items = repairLayoutData(jsf, widgetLibrary, layoutItem[i].items, newDataIndex);
        }
      }
    }
  }
  return layoutItem;
}

@hamzahamidi
Just wanted to check on the above issue by @joenarus , Any plan on merging this in a new version?

I am able to replicate the same nested array issue here

angular-tngstw - StackBlitz

Thank you

Stale issue

@hamzahamidi Can you please reopen as this issue is still unresolved?