Solved

Get repeater XML using JavaScript

  • 8 January 2024
  • 13 replies
  • 205 views

Badge +2

The application that I am working on requires to insert data that is in a in a Nintex form (of one SharePoint list) to a different list (in the same site) when the user clicks on a button that is in the form

 

To clarify, assume the user is editing one of the list items in the list (for which the Nintex form has been built). Now he may change the form data and click the button. Then the  form data should get saved to a completely different list on the same SharePoint site (which is similar in structure to the list whose edit form the user is on).

 

to meet this requirement - I used custom JavaScript code to pull data from the Nintex form and insert it into the other SharePoint list. This approach is working great for all the fields in the form but I am having trouble with the repeater controls that are in the form. As you may be aware Nintex stores the data within a repeater as XML when the OOTB Save is invoked. We would like the same thing to happen when the data is inserted into the other SharePoint  list using custom JavaScript code

 

unfortunately, it seems like we cannot store the repeater in a client JavaScript variable (unlike other simple controls)

 

the goal is to somehow get the XML that gets generated for a repeater control data NOT using OOTB Save but using custom JavaScript

 

any help on this will be much appreciated

 

Also note that we have repeater controls in the form that are 3 levels deep (repeater within repeater within repeater)

 

icon

Best answer by MegaJerk 12 January 2024, 08:28

View original

13 replies

Userlevel 5
Badge +14

Repeating Row Controls do not store any particular value, but act more like a container for other controls that *do* contain values. You won’t be able to just grab all of the xml from the control because the XML values that are stored as the controls ‘value’ in the context of SharePoint in general, is more or less just part of the actual Form Data and is used to build the Repeating Section on form load, as opposed to a ‘live’ value that is updated when the control has been manipulated.

 

So you will either need to loop through each row of each repeating section (remember to ignore the row that’s hidden! (I think the class is .nf-repeater-row-hidden)), and build out an array of data to do whatever it is you’re doing.

 

However, If you really desperately wanted to just copy over a chunk of Repeater XML from one item to another, then I would recommend maybe taking a slightly different approach than how you’re currently making the item from within the form. Instead of having a button that when pressed will create an item in real time. I would instead make it so that the button simply toggles an attached Column’s value (let’s say it’s a yes/no column called [Create Item]). When the Form is submitted, a conditional workflow ([Create Item] Previous Value === “No” && [Create Item] === “Yes”) engages that will easily allow you to push any Repeater Control data from one place to another (along with any other fields you might want).

 

Let me know if that answers your question or if you have further inquiries regarding your objective. 

 

 

Badge +2

thank you so much for taking the time to respond to my question.

just to give you some background-
i am rewriting this application using Nintex forms and workflows.
the original application was built using InfoPath and SPD workflow.
if you have any knowledge of InfoPath, you will be aware that InfoPath has data connections that you can create to other form libraries in the same SharePoint site.
So, in the original InfoPath form there was a Submit button which when clicked would submit the form data to the other form library. And the list item that the user was editing would be left untouched. 
that is the functionality that I am trying to replicate.

regarding your proposed solution of creating another yes/no column in the list and then setting this column to yes when the user clicks the Submit button-
the issue I see is that the workflow will not kick off until the list item (which the user is editing) is saved.
and doing so will overwrite the list item with the changes the user made, and this is something we don't want. (because that's not how the original InfoPath application works)
like I said before in the original InfoPath application, you could click the Submit button which would create a new list item in the other SharePoint library (using the edited data in the current list item) and the list item in the current library which the user was editing would be left untouched.

for instance, assume we have list A which has 5 columns - col1, col2, col3, col4, col5.
we now create another yes/no column - col6
assume the other list in which the new list item needs to get created is called list B.
now assume the user is editing one of the list items in list A and he changes col1, col2 and then he clicks the Submit button.
then per your proposed solution, the col6 value changes to yes
and the form gets saved.
which will overwrite the old values in col1, col2 as well 
this is what we don't want.
what we would like to happen is the new values in col1, col2 and unchanged col3, col4, col5 gets created in new list item in list B but the list item in list A should not be touched.

i hope I made myself clear.

 

again, thank you very much for taking the time to respond.

 

 

Userlevel 5
Badge +14

Remember. That second solution I proposed, was really only if you wanted to strictly work with XML data and is by no means a requirement. If you already have a solution for what it is you’re trying to do with pushing unsaved data from one form into another one, then by all means, keep that solution. You’ll simply need to use JS to iterate over the Repeating Section contents row by row, control by control, to create your own XML to push into the column on List B that is attached to it’s Repeating Section Control (assuming that you have the List B’s Repeating Section Control in question actually connected to a Multiline Column on the List. If you don’t… then you’ll need to update the value of the control in the FormData XML).

 

If you want help on writing the code to do that, let me know. Likewise, if you’re interested in exploring other ways of doing things, we can also go that route (I’ll just need more understanding of this general process and what the relationship between your different list and list items are)!

 

Either way is fine by me.

Badge +2

Hello - if you can give some sample code on how to construct the XML from the repeater control that would be great.

 

assume repeater is called “repeater” and has 3 columns - col1, col2 , col3 (all single line of text)

assume their JavaScript client ID reference variables are called ID_col1, ID_col2, ID_col3

 

please don't too much time on trying to write this code. 

 

thank you so much for your help on this...

Userlevel 5
Badge +14

One question before I start this - Will this code run on modern browsers (Chrome, FireFox, Edge or Opera), or do you expect it to be ran on something like Internet Explorer?

Badge +2

it will run on modern browsers, not IE.

Userlevel 5
Badge +14

This should be pretty straight forward if you’re already used to messing with JS. I’m did not explain every little detail about the Nintex Forms environment in my code comments to keep this as short as possible, but if you have any questions, just ask.

 

I have two Lists setup, both of which have a Multiline Text Column called RepeaterXML:

 

 

The idea is that I will populate a List Item on the Parent List, run some code to generate some XML of the Repeating Section (as it is in real-time), and then I can (in this case, manually) take that string of xml and drop it directly into the [Repeater XML] Column on the Child List, which should be used by its own Nintex Form to populate the Repeating Section there.

 

Speaking of Forms, Both have identical Forms:

 

 

The Repeating Section Control is named “control_Repeater” and is connected to the [Repeater XML] column:

 

The Single Line Text Controls inside of the Repeating Section are named “control_Col1” through “control_Col3” respectively:

 

On the Parent List I’m going to make an item with a few rows:

 

When I save that, it will take me back to the list so we can see what the XML actually looks like:

 

Let’s look at that in a code editor where it’s formatted:

 

As you can see the structure is pretty straight forward. Each “Row” is represented by an <Item> node, and each control (that contains a value!) is a child of the <Item> node and is given its own Node that is named after the Control’s name. This is why it’s important to name your Controls! Without naming a control, the node value would be the GUID contained in the attribute “formcontrolid” (I think!) on the control. I’m not handling those cases so… name your controls!

 

Now let’s get into the JavaScript. Below is some JS I’ve written that will take a Repeating Section on the form and convert it into an XML string:

/* User Configurable Variable
// This will tell the XML Generator which Controls it's going to try to get
// a value for that are inside of the Repeating Section. */
var controlsToParse = {
"Attachments Control": false,
"Yes/No": true,
"Save and Submit": false,
"Calculated Value": false,
"Change Content Type": false,
"Choice": true,
"SQL Request": false,
"Date/Time": true,
"External Data Column": false,
"Page Viewer": false,
"Geolocation": false,
"Rich Text": false,
"Hyperlink": false,
"Image": false,
"Label": false,
"Border": false,
"List Item": false,
"List View": false,
"Multi Line Textbox": true,
"Panel": false,
"People": false,
"Repeating Section": true,
"List Lookup": false,
"Managed Metadata": false,
"Single Line Textbox": true,
"Workflow Diagram": false,
"Web Request": false
};

/* used as lookup bridge between controlsToParse and allowedControlTypeIDs */
/* do not change */
var controlNameToTypeID = {
"Attachments Control":"5f8b447a-4195-485b-9a04-477d7f24be73",
"Yes/No":"c0a89c70-0781-4bd4-8623-f73675005e04",
"Save and Submit":"c0a89c70-0781-4bd4-8623-f73675005e09",
"Calculated Value":"c0a89c70-0781-4bd4-8623-f73675005e17",
"Change Content Type":"ff9f65fe-f979-4312-a35b-50f0d3769069",
"Choice":"c0a89c70-0781-4bd4-8623-f73675005e02",
"SQL Request":"7733d5bf-11c6-4bdc-a430-79c3065a796c",
"Date/Time":"c0a89c70-0781-4bd4-8623-f73675005e03",
"External Data Column":"c0a89c70-0781-4bd4-8623-f73675005e21",
"Page Viewer":"c0a89c70-0781-4bd4-8623-f73675005e13",
"Geolocation":"c0a89c70-0781-4bd4-8623-f73675005e19",
"Rich Text":"c0a89c70-0781-4bd4-8623-f73675005e07",
"Hyperlink":"a0c89d70-0781-4bd4-8623-a73675005a05",
"Image":"c0a89c70-0781-4bd4-8623-f73675005e08",
"Label":"c0a89c70-0781-4bd4-8623-f73675005e00",
"Border":"c0a89c70-0781-4bd4-8623-f73675005e11",
"List Item":"2c285c16-d4e6-49eb-8a6a-d9aa41e9e71b",
"List View":"4420d111-8869-49bb-8685-c1b6cdec4873",
"Multi Line Textbox":"c0a89c70-0781-4bd4-8623-f73675005e06",
"Panel":"c0a89c70-0781-4bd4-8623-f73675005e14",
"People":"c0a89c70-0781-4bd4-8623-f73675005e12",
"Repeating Section":"c0a89c70-0781-4bd4-8623-f73675005e16",
"List Lookup":"c0a89c70-0781-4bd4-8623-f73675005e15",
"Managed Metadata":"b612705d-96ee-4824-90e2-4f37ee78a36c",
"Single Line Textbox":"c0a89c70-0781-4bd4-8623-f73675005e05",
"Workflow Diagram":"2212c7db-a29d-4666-86dd-14e8ad4b3fc9",
"Web Request":"aeada2b6-24ad-46e2-894f-562c2a01d38a"
};
/* do not change */
var allowedControlTypeIDs = {
"5f8b447a-4195-485b-9a04-477d7f24be73": false,
"c0a89c70-0781-4bd4-8623-f73675005e04": false,
"c0a89c70-0781-4bd4-8623-f73675005e09": false,
"c0a89c70-0781-4bd4-8623-f73675005e17": false,
"ff9f65fe-f979-4312-a35b-50f0d3769069": false,
"c0a89c70-0781-4bd4-8623-f73675005e02": false,
"7733d5bf-11c6-4bdc-a430-79c3065a796c": false,
"c0a89c70-0781-4bd4-8623-f73675005e03": false,
"c0a89c70-0781-4bd4-8623-f73675005e21": false,
"c0a89c70-0781-4bd4-8623-f73675005e13": false,
"c0a89c70-0781-4bd4-8623-f73675005e19": false,
"c0a89c70-0781-4bd4-8623-f73675005e07": false,
"a0c89d70-0781-4bd4-8623-a73675005a05": false,
"c0a89c70-0781-4bd4-8623-f73675005e08": false,
"c0a89c70-0781-4bd4-8623-f73675005e00": false,
"c0a89c70-0781-4bd4-8623-f73675005e11": false,
"2c285c16-d4e6-49eb-8a6a-d9aa41e9e71b": false,
"4420d111-8869-49bb-8685-c1b6cdec4873": false,
"c0a89c70-0781-4bd4-8623-f73675005e06": false,
"c0a89c70-0781-4bd4-8623-f73675005e14": false,
"c0a89c70-0781-4bd4-8623-f73675005e12": false,
"c0a89c70-0781-4bd4-8623-f73675005e16": false,
"c0a89c70-0781-4bd4-8623-f73675005e15": false,
"b612705d-96ee-4824-90e2-4f37ee78a36c": false,
"c0a89c70-0781-4bd4-8623-f73675005e05": false,
"2212c7db-a29d-4666-86dd-14e8ad4b3fc9": false,
"aeada2b6-24ad-46e2-894f-562c2a01d38a": false
};

/* Using the controlsToParse object, we configure the allowedControlTypeIDs values
// so that as we iterate through a Repeating Section's controls, we can reference the
// Control's [data-formcontroltypeid] attribute against the value of the key of the
// same name inside of allowedControlTypeIDs */
Object.keys(controlsToParse).forEach(function(key){
var controlID = controlNameToTypeID[key];
if (allowedControlTypeIDs[controlID] !== undefined) {
allowedControlTypeIDs[controlID] = controlsToParse[key];
}
});

function repeaterToXML(repeaterControl, isNested){
repeaterControl = NWF$(repeaterControl);
isNested = isNested === true;

if (repeaterControl.attr("data-formcontroltypeid") !== "c0a89c70-0781-4bd4-8623-f73675005e16") {
return "";
}
/* this will be the:
// <Item>
// <controlName type="System.String">someValue</controlName>
// </Item>
// portion of the xml, for EACH row we go through.
// We will wrap it in the other XML before we return it. */
var xmlItemsContents = "";

var repeaterRows = repeaterControl.find(".nf-repeater-row:not" + "(.nf-repeater-row-hidden)");
repeaterRows.each(function(index, row){
row = NWF$(row);
var xmlItemContents = "";
var rowControls = row.find(".nf-filler-control");
rowControls.each(function(index, control){
control = NWF$(control);
controlTypeID = control.attr("data-formcontroltypeid");
controlName = control.attr("data-controlname");
if (allowedControlTypeIDs[controlTypeID]) {
/*
Right now I'm only handling what to do with the control if it's either a Repeating Section
Or (else) a Single Line Text Control. That else block code will NOT get the value for
Controls of some other types (like Choice Controls or certain Date Time Controls).

If you wanna actually parse those types of things, you'll need to add your else if() blocks
to deal with getting the value the way it needs to be got!
*/
if (controlTypeID === "c0a89c70-0781-4bd4-8623-f73675005e16") {
/*
call the function again recursively! this will make for nested XML!
While I have a flag for nested on the function, I'm not currently doing anything with it.
If you are actually using nested repeating sections, then the return value of XML that is
nested will likely need to be ENCODED as XML contains characters that are not considered
valid as an XML Node's value!
*/
xmlItemContents += `<${controlName}>${repeaterToXML(control, true)}</${controlName}>`;
} else {
xmlItemContents += `<${controlName} type="System.String">${control.find(".nf-associated-control").val()}</${controlName}>`;
}
}
});

/* we'll wrap the contents of xmlItemContents with the <Item> tag to indicate a completed Row */
xmlItemsContents += `<Item>${xmlItemContents}</Item>`;
});

/* finally we wrap the contents of xmlItemsContens with the correct XML that the Repeating Section produces */
return `<?xml version="1.0" encoding="utf-8"?><RepeaterData><Version /><Items>${xmlItemsContents}</Items></RepeaterData>`;
}

 

It looks kind of daunting here (especially because of the terrible formatting this forum has for JS now...), but it’s really quite straight forward. There is however one section you can configure for your own environment, and that’s the very first variable “controlsToParse”. This variable tells the function which controls inside of your Repeating Section Row you wanna get the value for. Right now I have it setup for a few different controls but don’t really have the code written to handle anything more than Single Line Text controls. It was more of a “this might be useful to flesh out one day, so I’ll make it obvious where to start” kinda thing. It should also make it easier for you if you ever wanna tweak it down the road.

 

Any control you want to process, simply change the value of the property to true, or to false if you want it to be ignored:

 

Now that we have the JS, we can load it on the form however you’d like (I just used the dev console since I don’t have a drop of code to push stuff from a form to a sharepoint item in realtime and don’t wanna mess with it), and once in place, I can execute the function with an argument that’s the jQuery object of my Repeating Section:

repeaterToXML(NWF$("[data-controlname='control_Repeater']"));

 

This produces the following output:

 

If I copy that string and paste it into a new item on the Child List:

 

and then examine the Item. I’ll find that it has worked!:

 

 

And that’s about all there is to it. Let me know if anything was too confusing or unclear, and I’ll try to explain best I can. 

 

Hope this helps

 

EDIT: Adding some additional Resources / Info in Q and A form below that might be helpful.

 

Q: Where did I get all of those ControlTypeID GUIDs from!?:

A: I don’t have *that* good of a memory. I used this https://help.nintex.com/en-us/sdks/sdk2013/FormSDK/Topics/SDK_NF_REF_XML_e_Form_Control_Properties.htm

 

Q: Why don’t I use JS IDs on the Controls?:

A: Because Repeating Sections do not utilize them in a way that is useful. IDs should be unique for the HTML to be valid, so while the first instance of a Control in a Repeating Section might be assigned the ID variable name you used in the Control’s Settings, the subsequent occurrences of that control (in other rows) will be given a generated ID that does not match the original one! Therefore it’s actually infinitely better to use the Control Name of a Control instead as those references work in ANY context.

 

Q: What’s that NWF$() thing?

A: That’s Nintex’s global reference to jQuery that’s included with the form.

 

 

 

Badge +2

hello friend,

thanks for taking the time to write that code and also for the explanation with the screenshots

seems like you put in quite a bit of your time into this.

thank you for your kindness.

 

i can't believe there are people still out there who will put in so much time and effort into helping others especially on the internet.

 

the code seems to be complicated and I think I will need some time to understand it .

i will post here if i have any questions...

 

thank you once again for all your efforts.

 

Userlevel 5
Badge +20

Thank you @MegaJerk for these great replies! @vivekmang please remember to mark the best answer if a reply helped solve your question!

Badge +2

@MillaZ - I marked @MegaJerk response as the best answer

Userlevel 5
Badge +20

Thank you @vivekmang!

Userlevel 5
Badge +14

hello friend,

thanks for taking the time to write that code and also for the explanation with the screenshots

seems like you put in quite a bit of your time into this.

thank you for your kindness.

 

i can't believe there are people still out there who will put in so much time and effort into helping others especially on the internet.

 

the code seems to be complicated and I think I will need some time to understand it .

i will post here if i have any questions...

 

thank you once again for all your efforts.

 

 

No problem. Nobody learns alone. Glad to have helped, and thank you for the kind words.

Badge +2

no problem @MegaJerk 

that was the least I could do.

Reply