Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nested CollectionField Data #457

Closed
1 task done
walleford opened this issue May 20, 2022 · 24 comments
Closed
1 task done

Nested CollectionField Data #457

walleford opened this issue May 20, 2022 · 24 comments

Comments

@walleford
Copy link

Category

  • Question

Version

Please specify what version of the library you are using: [ 3.6.0 ]

Expected / Desired Behavior / Question

If you are reporting an issue please describe the expected behavior. If you are suggesting an enhancement please
describe thoroughly the enhancement, how it can be achieved, and expected benefit. If you are asking a question, ask away!

Hello,

I am trying to nest a collection data inside another collection data that renders only when one of the fields in the original collection data is checked. Is there a way to do this?
I have this below but it does not work so far:

PropertyFieldCollectionData("gridItems", {
                  key: "gridItemsFieldId",
                  label: "Grid Data",
                  panelHeader: "Grid Data Panel",
                  manageBtnLabel: "Manage grid data",
                  value: this.properties.gridItems,
                  fields: [
                    {
                      id: "Title",
                      title: "Item Title",
                      type: CustomCollectionFieldType.string,
                      required: true,
                    },
                    {
                      id: "Description",
                      title: "Item Description",
                      type: CustomCollectionFieldType.string,
                    },
                    {
                      id: "Hyperlink",
                      title: "Link to Open",
                      type: CustomCollectionFieldType.url,
                      required: true,
                      },
                    {
                      id: "isDropdown",
                      title: "Does this need a dropdown?",
                      type: CustomCollectionFieldType.boolean,
                      required: false,
                      },
                      {
                          id: "dropData",
                          title: "Manage Drop Down Data",
                          type: CustomCollectionFieldType.custom,
                          isVisible: this.dropDataVisible,
                          onCustomRender: (field, value, onUpdate, item, itemId, onError) => {
                              return (
                                  React.createElement(ICustomCollectionField, {
                                      key: "dropItemsFieldId",
                                      label: "Dropdown Data",
                                      panelHeader: "Drop Data Panel",
                                      manageBtnLabel: "Manage drop down data",
                                      value: this.properties.gridItems[0].dropData,
                                      fields: [
                                          {
                                              id: "dropText",
                                              title: "Item Title",
                                              type: CustomCollectionFieldType.string,
                                              required: true,
                                          },
                                          {
                                              id: "dropLink",
                                              title: "Text Link",
                                              type: CustomCollectionFieldType.url,
                                          },
                                      ]
                                  }))
                            }
                      },
                      {
                          id: "backgroundColor",
                          title: "Pick background color:",
                          type: CustomCollectionFieldType.dropdown,
                          options: this.backgroundOptions,
                          required: true
                      },
                      
                      {
                          id: "filePicker",
                          title: "Select File",
                          type: CustomCollectionFieldType.custom,
                          onCustomRender: (field, value, onUpdate, item, itemId, onError) => {
                              return (
                                  React.createElement(FilePicker, {
                                      key: itemId,
                                      context: this.context,
                                      buttonLabel: "Select File",
                                      onChange: (filePickerResult: IFilePickerResult[]) => {
                                          console.log('changing....', field);
                                          onUpdate(field.id, filePickerResult[0]);

                                          this.context.propertyPane.refresh();
                                          this.render();
                                      },
                                      onSave:
                                          async (filePickerResult: IFilePickerResult[]) => {
                                              for (const filePicked of filePickerResult) {
                                                  if (filePicked.fileAbsoluteUrl == null) {
                                                      filePicked.downloadFileContent().then(async r => {
                                                          let fileresult = await this.sp.web.getFolderByServerRelativePath(`${this.context.pageContext.site.serverRelativeUrl}/SiteAssets/SitePages`).files.addChunked(filePicked.fileName, r);
                                                          this.properties.gridItems[0].filePickerResult = filePicked;
                                                          this.properties.gridItems[0].filePickerResult.fileAbsoluteUrl = `${this.context.pageContext.site.absoluteUrl}/SiteAssets/SitePages/${fileresult.data.Name}`;
                                                          this.context.propertyPane.refresh();
                                                          this.render();
                                                      });
                                                  } else {
                                                      console.log('saving....', filePicked);
                                                      onUpdate(field.id, filePicked);
                                                      this.context.propertyPane.refresh();
                                                      this.render();
                                                  }
                                              }
                                          },
                                      hideLocalUploadTab: false,
                                      hideLocalMultipleUploadTab: true,
                                      hideLinkUploadTab: false,
                                  })
                              );
                          },
                          required: true
                      },
                  ],
                  disabled: false,
                },

I would really appreciate any help with this! Thank you.

@ghost
Copy link

ghost commented May 20, 2022

Thank you for reporting this issue. We will be triaging your incoming issue as soon as possible.

@ghost ghost added the Needs: Triage 🔍 label May 20, 2022
@edarroudi
Copy link

Maybe it's possible with my new TreeCollectionField Control PR #456 , also see Issue #451

@walleford
Copy link
Author

@edarroudi Is this available for use?

@edarroudi
Copy link

@walleford not right, PR has to be looked at and then finished. Can't give you a timeline on that.

@walleford
Copy link
Author

I see @edarroudi until then do you happen to know of a way to conditionally render a field within the collection field data? I have tried Boolean && {field element} as well as using (…{} : {}) for determine whether something is true and rendering an object based on that.

@IRRDC
Copy link
Contributor

IRRDC commented Jul 7, 2022

@walleford I just managed to create several levels of nested PropertyFieldCollectionDataHost. I don't know if it is what you are looking for but maybe it helps you along:
I have a filterConfig property that has a listConfig array sub property. The filterConfig property is the target of the PropertyFieldCollectionData on the top level and one of the fields is a custom render of a PropertyFieldCollectionDataHost to configure the listConfig sub property (and in that there is another sub property ... (not shown))
{ id: 'listConfig', type: CustomCollectionFieldType.custom, isVisible: (field: ICustomCollectionField, items: any[]) => { return items && items.filter(i => i.inputType === FilterControlTypes.DropDown).length > 0; // Whole column is only visible if at least one row needs it. }, onCustomRender: (field, value, onUpdate, item: IFilterConfig, rowUniqueId, onCustomFieldValidation) => { return ( React.createElement(PropertyFieldCollectionDataHost, { onChanged: (items) => { onUpdate(field.id, items); }, disabled: (item.inputType !== FilterControlTypes.DropDown && item.inputType !== FilterControlTypes.MultiDropDown), label: '', value: **item.listConfig**, key: 'listConfigSubControl', fields: [ { id: 'listId', title: strings.PropertyPaneList, type: CustomCollectionFieldType.custom, required: false, onCustomRender: (**subField, subValue, subOnUpdate, subItem: IListConfig, subRowUniqueId, subOnCustomFieldValidation**) => { return ( React.createElement('div',... )) ); } }, ] }) ); }
By using different names for the parameters of onCustomRender on each level of the nesting I can still access the values of the row item from each previous level.

@walleford
Copy link
Author

@IRRDC Thank you for that, it is really appreciated. This is what I have gotten to so far,

{
                          id: "dropData",
                          title: "Manage Drop Data",
                          type: CustomCollectionFieldType.custom,
                          onCustomRender: (field, value, onUpdate, item, itemId, onError) => {
                              return (
                                  React.createElement(FieldCollectionData, {
                                      key: 'dropDataFieldId',
                                      label: "Drop Data",
                                      panelHeader: "Drop Data Panel",
                                      manageBtnLabel: "Manage drop data",
                                      fields: [
                                          {
                                              id: "Title",
                                              title: "Item Title",
                                              type: CustomCollectionFieldType.string,
                                          },
                                          {
                                              id: "Link",
                                              title: "Item Description",
                                              type: CustomCollectionFieldType.url,
                                          },
                                      ],
                                      value: this.properties.gridItems ? this.properties.gridItems[0].dropData : null,
                                      onChanged: (changedDropData: ICustomCollectionField[]) => {
                                          console.log('changing....', field);
                                          onUpdate(field.id, changedDropData[0]);
                                          this.context.propertyPane.refresh();
                                          this.render();
                                      }
                                  })
                                  )
                          }
                    },

However, I can't seem to get the value of the custom rendered field collection data to work properly, I don't really know what to set it as. I am also wanting to us isVisible to make it available based on whether another field within the top level collection data is checked or not, but haven't been able to get that to work either. Let me know if you have any suggestions!

@IRRDC
Copy link
Contributor

IRRDC commented Jul 7, 2022

Do you specifically want to access the first item here? this.properties.gridItems[0].dropData The gridItems collection will only have updated values once the control closes. To access the gridItem that matches the current row you can refer to the "item" property of the surrounding collection (just rename the inner "item" as I did in my badly formatted example to "subitem" to distinguish them). That surrounding "item" instance will have the (as yet unsaved) current value of the topmost collection.

@walleford
Copy link
Author

@IRRDC so when you used subItem: IListConfig, this is allowing the nested fields to reference their parent row? What I sent you above is a single field within the collection data, that is custom rendering another collection data group. Just want to make sure I am tracking what you are saying correctly.

@IRRDC
Copy link
Contributor

IRRDC commented Jul 7, 2022

Yes, you are nesting to custom renders, each with a signature of (field, value, onUpdate, item, itemId, onError). If you change the parameter names in the signature of the nested custom renders you can refer to the parent field, value, etc. The "item" parameter of the parent contains the parent row value that is currently being edited.

@walleford
Copy link
Author

I see, thank you for that. Would this also theoretically work for referencing another field in the parent collection?

{
                      id: "isDropdown",
                      title: "Does this need a dropdown?",
                      type: CustomCollectionFieldType.boolean,
                      required: false,
                    },
                    {
                      id: "backgroundColor",
                      title: "Pick background color:",
                      type: CustomCollectionFieldType.dropdown,
                      options: this.backgroundOptions
                      },
                      {
                          id: "dropData",
                          title: "Manage Drop Data",
                          type: CustomCollectionFieldType.custom,
                          onCustomRender: (field, value, onUpdate, item, itemId, onError) => {
                              return (
                                  React.createElement(FieldCollectionData, {
                                      key: 'dropDataFieldId',
                                      label: "Drop Data",
                                      panelHeader: "Drop Data Panel",
                                      manageBtnLabel: "Manage drop data",
                                      fields: [
                                          {
                                              id: "Title",
                                              title: "Item Title",
                                              type: CustomCollectionFieldType.string,
                                          },
                                          {
                                              id: "Link",
                                              title: "Item Description",
                                              type: CustomCollectionFieldType.url,
                                          },
                                      ],
                                      value: item.dropData,
                                      onChanged: (changedDropData: ICustomCollectionField[]) => {
                                          console.log('changing....', field);
                                          onUpdate(field.id, changedDropData[0]);
                                          this.context.propertyPane.refresh();
                                          this.render();
                                      }
                                  })
                                  )
                          }
                    },

I want dropData to reference isDropdown to determine whether or not it should render, or be available I guess.

@IRRDC
Copy link
Contributor

IRRDC commented Jul 7, 2022

Yes. In your code snippet you have to change
onCustomRender: (field, value, onUpdate, item, itemId, onError)
to
onCustomRender: (field, value, onUpdate, subItem, itemId, onError)
and
value: item.dropData
to
value: subItem
since in the context of the custom render "subItem" is your parent row's "dropData" property. Within the custom render you can access "item.isDropdown" (not "subItem") to get the current value of that control.

@walleford
Copy link
Author

@IRRDC wow, thanks. This should help me almost finish lol. Now to the tsx and actually rendering everything correctly

@walleford
Copy link
Author

@IRRDC However, when I use it this way:


                                      value: subitem,
                                      disabled: item.isDropdown ? false : true,

I am getting an error on item.isDropdown saying it can't be found. If the parent isn't a "Custom collection field" rather a regular PropertyFieldCollectionData, will item reference it properly?

@IRRDC
Copy link
Contributor

IRRDC commented Jul 7, 2022

Sorry, I was thinking in too many nested layers just then. The code I'm working on right now is messing with my head. You don't need to rename item, set value: item.dropData, and disabled: item.isDropdown ? false : true.

@walleford
Copy link
Author

image
It worked, and just how I wanted it to. Thank you for the help

@walleford
Copy link
Author

@IRRDC One last question for you, how are you getting the nested items to persist after clicking add and save? I am using this onChanged function (which works in a non-nested field) but it isn't working for this nested fieldcollectiondata. I would appreciate any help you can give.

                                      onChanged: (fieldCollectionData: ICustomCollectionField[]) => {
                                          console.log('changing....', field);
                                          onUpdate(field.id, fieldCollectionData[0]);

                                          this.context.propertyPane.refresh();
                                          this.render();

@IRRDC
Copy link
Contributor

IRRDC commented Jul 13, 2022

@walleford I'm just using onChanged: (items) => { onUpdate(field.id, items); }, in the nested PropertyFieldCollectionDataHost.

@walleford
Copy link
Author

@IRRDC thank you for all of the help, I am going to close this now because that solved the issue.

@walleford
Copy link
Author

walleford commented Sep 23, 2022

@IRRDC hello again... I had to rewrite this code... long story short, backups didn't happen so I lost it. I am pretty sure I wrote it the exact same way, but it isn't persisting after I click add and save and I am not sure why, do you see anything wrong with this?

{
                      id: "dropData",
                      title: "Manage Drop Data",
                      type: CustomCollectionFieldType.custom,
                      onCustomRender: (field, value, onUpdate, item, itemId, onError) => {
                        return (
                          React.createElement(FieldCollectionData, {
                            key: 'dropDataFieldId',
                            panelHeader: "Drop Data Panel",
                            manageBtnLabel: "Manage drop data",
                            disabled: item.needsDropdown ? false : true,
                            fields: [
                              {
                                id: "Title",
                                title: "Item Title",
                                type: CustomCollectionFieldType.string,
                              },
                              {
                                id: "Link",
                                title: "Item Hyperlink",
                                type: CustomCollectionFieldType.url,
                              },
                            ],
                            value: item.dropData,
                            onChanged: (items) => { onUpdate(field.id, items); },
                          })
                        )
                      }
                    },

@walleford walleford reopened this Sep 23, 2022
@IRRDC
Copy link
Contributor

IRRDC commented Sep 26, 2022

@walleford I'm sorry but I don't see what is wrong with your code.

@AJIXuMuK
Copy link
Collaborator

@walleford - I assume the question was more or less answered and the issue could be closed.

@ghost
Copy link

ghost commented Feb 19, 2023

This issue has been automatically marked as stale because it has marked as requiring author feedback but has not had any activity for 7 days. It will be closed if no further activity occurs within next 7 days of this comment. Thank you for your contributions to SharePoint Developer activities.

@ghost ghost closed this as completed Feb 26, 2023
@ghost
Copy link

ghost commented Feb 26, 2023

Closing issue due no response from original author. If this issue is still occurring, please open a new issue with additional details. Notice that if you have included another related issue as additional comment on this, please open that also as separate issue, so that we can track it independently.

This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants