I am still working through change handling for user-input fields that are not pre-filled by the Lookup list but I will share the basic of how this functionality was used to accomplish my goals:
- Auto-populate temporary fields to display values (I set the color as blue to help differentiate) while users actively select References in Step #2.
- Then, once the user saves the form and comes back to review their selections, the saved values appear (in black) as expected.
In this example above, the fields in blue are all Plumsail COMMON text fields and the fields in black are SharePoint Lookup list additional fields added to the Plumsail Form Designer. Plus, the Reference X (Step #2) calls out these and other columns in the “Extra fields” panel. Let’s break that down.
In SharePoint Child List (named References in this case)
Several columns with values in all - like a spreadsheet full of data which has been hidden here for security purposes.

In SharePoint Parent List (name not shared for security purposes)
Several columns with values in all - like a spreadsheet full of data which has been hidden in this post for security purposes.
I have three Lookup fields that use the Child List for References (A, B, C). I will show “reference-a” plus additional fields in this example.
In Plumsail, the additional fields from SP (as shown above) are included in the extra fields panel. Some fields are not supported in Lookup fields in SharePoint, such as Choice fields, so I call them out later in Plumsail. I am not sure if I fully understand why but that’s how I have it configured currently.
In the Parent SP List Settings, this is a snippet of what those columns look like for SharePoint Parent columns and then how the Lookup columns appear extended from the Child.
This is an example of some of the JS for Ref A - there is a copious amount of code to get all three reference sections to perform correctly. And there are a lot of fields in SharePoint and in Plumsail to make everything work.
// REFERENCE A — load-safe, clear-safe mapper
fd.spRendered(function () {
const lu = fd.field('reference_x002d_a');
if (!lu) return;
function populateFrom(value) {
// Clear all temp fields first
fd.field('txt-ref-a-id').value = '';
fd.field('txt-core-title-a').value = '';
fd.field('txt-core-edition-a').value = '';
fd.field('txt-core-year-a').value = '';
fd.field('txt-core-editors-a').value = '';
fd.field('txt-core-publisher-a').value = '';
fd.field('txt-journal-name-a').value = '';
fd.field('txt-journal-abbreviation-a').value = '';
fd.field('txt-website-a').value = '';
fd.field('txt-website-url-a').value = '';
fd.field('txt-utd-date-a').value = '';
fd.field('txt-utd-editor-a').value = '';
fd.field('txt-utd-place-a').value = '';
fd.field('txt-utd-publisher-a').value = '';
fd.field('txt-utd-website-a').value = '';
if (!value) return; // done (cleared)
// Populate when value exists (guard each property)
fd.field('txt-ref-a-id').value = value.ID ?? '';
fd.field('txt-core-title-a').value = value.core_x002d_title_x002d_a ?? '';
fd.field('txt-core-edition-a').value = value.core_x002d_edition_x002d_a ?? '';
fd.field('txt-core-year-a').value = value.core_x002d_year_x002d_a ?? '';
fd.field('txt-core-editors-a').value = value.core_x002d_editors_x002d_a ?? '';
fd.field('txt-core-publisher-a').value = value.core_x002d_publisher_x002d_a ?? '';
fd.field('txt-journal-name-a').value = value.journal_x002d_name_x002d_a ?? '';
fd.field('txt-journal-abbreviation-a').value = value.journal_x002d_abbreviation ?? '';
fd.field('txt-website-a').value = value.website ?? '';
fd.field('txt-website-url-a').value = value.website_x002d_url ?? '';
fd.field('txt-utd-date-a').value = value.utd_x002d_date ?? '';
fd.field('txt-utd-editor-a').value = value.utd_x002d_editor ?? '';
fd.field('txt-utd-place-a').value = value.utd_x002d_place ?? '';
fd.field('txt-utd-publisher-a').value = value.utd_x002d_publisher ?? '';
fd.field('txt-utd-website-a').value = value.utd_x002d_website ?? '';
}
// Wire change safely
lu.ready(field => {
field.$on('change', populateFrom);
// Initial populate (if the lookup already has a value on load)
try { populateFrom(field.value); } catch(_) {}
});
});
Change Handling
Fields that are not auto-populated from the Child List Lookup column have extra fields to help with temporary values, then changing those values once saved (if a change occurs). This is probably more difficult that the rest of the operations. For example, if the user picks a specific type for Ref A (Core Book, Journal, Website); in this example, let’s say the user selects Core Book, inputs non-LU-field values for Core Pages A, then saves the form. We know that value for Core Pages A is set. If the user edits the form again, this time changing the type (say from Core Book to Journal), that previously saved Core Pages A value still remains in the list causing now stale values to remain. This is complex in my opinion and not a fun part of the build because, you can see below, it takes about 4 columns to support a single value in this workflow.
Again, here is a very small snippet of some of the code that makes this work. I relied very heavily on MS Copilot to figure this out and it still is pretty detailed.
// =====================================
// Call it three times (A, B, C)
// =====================================
// ---------- A Call ----------
wireReferenceSlot({
mediaField: 'pri_x002d_ref_x002d_a',
lookupField: 'reference_x002d_a',
sourceMediaColumn: 'primarytype',
accordionControl: 'RefAccordionA',
primaryTypeLabelCtrl: 'primarytypeswitchera',
// TEMP groups (existing)
groups: {
'Core Book': ['txt-core-title-a','txt-core-edition-a','txt-core-year-a','txt-core-editors-a','txt-core-publisher-a','txt-core-pages-a'],
'Journal': ['txt-journal-name-a','txt-journal-abbreviation-a','txt-journal-article-a','txt-journal-url-a','txt-journal-volume-a','txt-journal-issue-a','txt-journal-year-a','txt-journal-pages-a','txt-journal-article-author-a','txt-journal-doi-a'],
'Website': ['txt-website-a','txt-website-url-a','txt-website-title-a','txt-website-author-a','txt-website-title-url-a','txt-website-publish-date-a','txt-website-publish-update-a'],
'Up To Date':['txt-utd-article-a','txt-utd-author-a','txt-utd-editor-a','txt-utd-date-a','txt-utd-website-a','txt-utd-place-a','txt-utd-publisher-a']
},
// NEW: SAVED groups (backend fields shown after save for each type)
savedGroups: {
'Core Book': [
'reference_x002d_a_x003a_core_x00',
'reference_x002d_a_x003a_core_x003',
'reference_x002d_a_x003a_core_x000',
'sp_x002d_core_x002d_pages_x002d_',
'reference_x002d_a_x003a_core_x001',
'reference_x002d_a_x003a_core_x002'
],
'Journal': [
'reference_x002d_a_x003a_journal_',
'reference_x002d_a_x003a_journal_0',
'sp_x002d_journal_x002d_article_x',
'sp_x002d_journal_x002d_url_x002d',
'sp_x002d_journal_x002d_volume_x0',
'sp_x002d_journal_x002d_issue_x00',
'sp_x002d_journal_x002d_year_x002',
'sp_x002d_journal_x002d_pages_x00',
'sp_x002d_journal_x002d_article_x0',
'sp_x002d_journal_x002d_doi_x002d'
],
'Website': [
'reference_x002d_a_x003a_website',
'reference_x002d_a_x003a_website_',
'sp_x002d_website_x002d_title_x00',
'sp_x002d_website_x002d_author_x0',
'sp_x002d_website_x002d_title_x000',
'sp_x002d_website_x002d_publish_x',
'sp_x002d_website_x002d_publish_x3'
],
'Up To Date': [
'sp_x002d_utd_x002d_author_x002d_',
'sp_x002d_utd_x002d_article_x002d',
'reference_x002d_a_x003a_utd_x0023',
'reference_x002d_a_x003a_utd_x0020',
'reference_x002d_a_x003a_utd_x0022',
'reference_x002d_a_x003a_utd_x0021',
'reference_x002d_a_x003a_utd_x002'
]
},
// NEW: Always hide (backend helpers/readonly controls you said should never show)
alwaysHide: [
'roPagesTxtPSA','roCoreBookPages',
'roJournalArticleTxtPSA','roJournalArticleA',
'roJournalURLTxtPSA','roJournalURLA',
'roJournalVolumeA','roJournalIssueA',
'reference_x002d_a_x003a_primaryt'
]
});
This is a block I have used in the past and still lean on here.
//>>>>>>>>>>>>>> Update SharePoint Core Book values when temp text values change txt-core-pages-a
fd.spRendered(function() {
fd.field('txt-core-pages-a').$on('change', function(value) {
fd.control('roPagesTxtPSA').text = fd.field('txt-core-pages-a').value;
fd.field('roCoreBookPages').value = fd.field('txt-core-pages-a').value;
fd.field('sp_x002d_core_x002d_pages_x002d_').value = fd.control('roPagesTxtPSA').text;
});
});
//
I hope some of this helps others to have a few bread crumbs on their own journey. When I say I relied on Copilot heavily, here is what I did. I built the site, styling and coding in ways I have done for several years. Once I was at a place where I was happy with base functionality, I asked MS365 Copilot to review my JavaScript document. From there, it was a mad exercise trying to redirect the output when I knew it was wrong, and also to reframe my prompts to ask questions in more meaningful ways. The bottom line where AI is concerned is this: does it work? It can. Is it accurate? Not exactly? Does it intuit what you already know about Plumsail and its API? Not reliably. Can it offer new ways to refine code? Almost always? Do I wish it could have greater insights into my use cases and understand what has already been done? Yes. Meaning - I can’t just tell MS365 Copilot to browse my site URL and tell me about what it would improve; I tried
, but I had to explicitly tell it my requirements, column names, workflow, etc. What I haven’t done yet is create an agent to set base expectations but I know I will need to eventually. I end up telling and retelling the prompt things it “should” already have logged in chats.
Good luck out there!