19.02.2010: Updated the post with a code example for setting this drop downs as required (requested by Larry). You find the code at the bottom of the article.
28.12.2009: Updated to add support for an optional delimiter character. I have also updated the functionality so that the selection now is moved from the drop down to the “Accumulated selection” when selected (and back when removed from the selection).
This article demonstrates a solution for accumulating single choice selections from a drop down to a hidden multi-line text-field.
As always we start like this:
Create a document library to hold your scripts (or a folder on the root created in SharePoint Designer). In this example i have made a document library with a relative URL of “/test/English/Javascript” (a sub site named “test” with a sub site named “English” with a document library named “Javascript”):
The jQuery-library is found here. The pictures and the sourcecode refers to jquery-1.3.2.min. If you download another version, be sure to update the script reference in the sourcecode.
The sourcecode for the file “AccumulateSelectionsToMultilineText.js” is found below.
Add the columns to your list like the picture below. I have two Choice columns with some random choices and a multi-line plain text column to accumulate the selections in.
NOTE:
The default selection must be blank to be able to select the “first value” and accumulate it as the function triggers on change event on the Choice columns.
Add a CEWP below your NewForm list-form (and EditForm if you like) like this:
With this code:
<script type="text/javascript" src="/test/English/Javascript/jquery-1.3.2.min.js"></script> <script type="text/javascript" src="/test/English/Javascript/AccumulateSelectionsToMultilineText.js"></script> <script type="text/javascript"> // Array of fields to accumulate values from - format: FieldInternalName of select [delimiter] FieldInternalName of hidden accumulation field arrFieldsToAccumulate = ['Choice|ChoiceAccumulated','Choice2|Choice2Accumulated']; // Call function to iterate trough all constellations of fields and hidden accumulator-fields in array defined above // The function "addAccumulateFunctionOnLoad" now takes one argument: sepChar. This is the optional delimiter character to separate the selected values addAccumulateFunctionOnLoad(';'); </script>
Your NewForm should now look like this:
Note: The 28.12.2009-update now moves the selection from the dropdown and to the “Accumulated selections”.
And your list view should look like this:
Some info:
- The selected values are stored in custom div’s and added to the hidden multi-line text-field when clicking the “OK” button
- The delimiter in the multi-line text-field is “n”, and the choices therefore is rendered in separate lines in the list-view and in DispForm.aspx
- The code handles page reload due to form validation and “preserves” the values (it reads them back from the hidden field and rewrites the custom div’s)
- Note that the actual selection-dropdown is cleared upon form submission
- When in NewForm or EditForm, a click on one of the accumulated selected values removes the selected element
Sourcecode for the file “AccumulateSelectionsToMultilineText.js”:
/* Accumulate selections from dropdown to multi-line text-column * --------------------------------------------- * Created by Alexander Bautz * alexander.bautz@gmail.com * https://spjsblog.com * LastMod: 26.12.2009 * --------------------------------------------- * Must include reference to jQuery * --------------------------------------------- * * Call from CEWP BELOW the list form in NewForm.aspx or EditForm.aspx with the code provided in the blog post. */ fields = init_fields(); function addAccumulateFunctionOnLoad(sepChar){ if(typeof(sepChar)=='undefined'){ separatorChar = ''; }else{ separatorChar = sepChar; } $.each(arrFieldsToAccumulate, function(idx,item){ var split = item.split('|'); var from = split[0]; var to = split[1]; accumulateOnSelect(from,to); }); } function accumulateOnSelect(fieldFrom,fieldTo){ // Hide the "hidden" accumulation-input $(fields[fieldTo]).hide(); // Add onchange on select $(fields[fieldFrom]).find('select').change(function(){ if($(this).find('option:selected').val()!=''){ appendValuesFromSelect(fieldFrom); } }); var selectFromTd = $(fields[fieldFrom]).find('.ms-formbody'); selectFromTd.append("<span>Accumulated selections:</span>"); var currentSelections = $(fields[fieldTo]).find(':input').val().split("n"); $.each(currentSelections,function(idx,itemValue){ if(itemValue!=''){ var newSel = customAddSelection(fieldFrom,itemValue); // Append new selection selectFromTd.append(newSel); } // Remove the selected value from the drop down selectFromTd.find('select option').each(function(){ if(itemValue!=''){ var iVal = handleSeparatorChar(itemValue,true); if($(this).val()==iVal){ $(this).remove(); } } }); }); addCustomClassAttr(fieldFrom); } function appendValuesFromSelect(fieldFrom){ var selectFromTd = $(fields[fieldFrom]).find('.ms-formbody'); var selectedOpt = selectFromTd.find('select option:selected'); var selectedvalue = selectedOpt.val(); if(selectedvalue!=''){ var newSel = customAddSelection(fieldFrom,selectedvalue); // Append new selection selectFromTd.append(newSel); // Remove the selected value from the drop down selectedOpt.remove(); // Add hover effect addCustomClassAttr(fieldFrom); } } function customAddSelection(fField,sValue){ sValue = handleSeparatorChar(sValue,false); var newDiv = $("<div title='Click to remove from selection' " + "class='dummyClass_" + fField + "' " + "style='cursor:pointer;padding-left:4px'>" + sValue + "</div>"); // Add one time onclick even newDiv.one("click",function(){removeSelectedDiv($(this))}); return newDiv; } function addCustomClassAttr(fieldFrom){ $(".dummyClass_" + fieldFrom).hover(function(){ $(this).addClass("ms-alternating"); }, function(){ $(this).removeClass("ms-alternating"); }); } function removeSelectedDiv(obj){ var objVal = obj.text(); var objVal = handleSeparatorChar(objVal,true); obj.parents('td:first').find('select').append("<option value='" + objVal + "'>" + objVal + "</option>"); obj.css({'background-color':'red'}); obj.fadeTo(450,0,function(){obj.remove();}); } function handleSeparatorChar(str,remove){ var newStr = ''; if(separatorChar==''){ newStr = str; }else{ if(remove){ newStr = str.substring(0,str.indexOf(separatorChar)); }else{ if(str.indexOf(separatorChar)>-1){ newStr = str; }else{ newStr = str + separatorChar; } } } return newStr; } function PreSaveAction(){ $.each(arrFieldsToAccumulate, function(idx,item){ var from = item.split('|')[0]; var to = item.split('|')[1]; // Find all selected values var str = ''; $(".dummyClass_" + from).each(function(){ if($(this).text()!=''){ str += $(this).text() + "n"; } }); // Insert the values in the hidden field $(fields[to]).find(':input').val(str); }); return true; } function init_fields(){ var res = {}; $("td.ms-formbody").each(function(){ if($(this).html().indexOf('FieldInternalName="')<0) return; var start = $(this).html().indexOf('FieldInternalName="')+19; var stopp = $(this).html().indexOf('FieldType="')-7; var nm = $(this).html().substring(start,stopp); res[nm] = this.parentNode; }); return res; }
Save the file as “AccumulateSelectionsToMultilineText.js” and upload to the document library or folder as described above.
How to set the fields as required:
Open the file “AccumulateSelectionsToMultilineText.js” and remove the function PreSaveAction(). Then you add this modified PreSaveAction(), and the lines for setting the red “required field” star after the label to the CEWP code.
You can NOT set the field as requires under list settings.
// Red star $(fields['Choice']).find('.ms-formlabel h3').append("<span class='ms-formvalidation'> *</span>"); $(fields['Choice2']).find('.ms-formlabel h3').append("<span class='ms-formvalidation'> *</span>"); function PreSaveAction(){ var okToSave = true; $.each(arrFieldsToAccumulate, function(idx,item){ var from = item.split('|')[0]; var to = item.split('|')[1]; // Find all selected values var str = ''; $(".dummyClass_" + from).each(function(){ if($(this).text()!=''){ str += $(this).text() + "n"; } }); // Insert the values in the hidden field $(fields[to]).find(':input').val(str); // Remove validation message to avoid multiple messages $(fields[from]).find('.ms-formbody div.ms-formvalidation').remove(); // Check if the "to-field" is empty, and that the field is set to required if(str=='' && $(fields[from]).find('.ms-formlabel span.ms-formvalidation').length==1){ // Add validation message $(fields[from]).find('.ms-formbody').append("<div class='ms-formvalidation'>You must specify a value for this required field.</div>"); // Set "save item" to false okToSave = false; } }); // Returns true or false return okToSave; }
Please ask if something is not clear to you.
Alexander
Hi Alexander, this seems to be a really nice AddOn to whats now are missing for a single Choice column, ok you can use multiple when you are do a looup, but that will be stored separated with ; – not always what we want, for example if you later on will creating views with grouping and sorting.
But, i have no luck with your script, i think i have do this exact like your description, but get this ‘unexpected error’. Have uploaded the scripts to a library and the realtive URL should be ok.
Btw, i have a custom list with this four columns and the original Titel. I have also turned on ‘Content Types’ to be abel to hide away the textboxes. I have no default value for the two Choice columns, and yes they have the same name as in the script. I also have the latest 1.3.2 min.
I would be so happy if you can check this out!
Rgds Christian
Hi again, i got this working now. Noticed that i had to have the reference to the AccumulateSelectionsToMultilineText.js below the Formview to get it work (and of course the array and call), nice work Alexander, good function and a little bonus for the cool fadeout when deleting! I will sure have use to this one in the future.
Rgds Christian
I have not implemented this yet but looks very promising. I can see easily how I can incorporate this script and the hide/show script, getting me closer to the select a region, populate countries in that region, click a country and append a text field. I think that function would work cleaner with the cascading dropdown, but thats a little more advanced for me.
So why did you use the multiple line field to append?
Can it be switched to single line, or would that require code rewrite?
This is some great work! thank you for putting up with me.
I got to install your script, and it is brillant. I am having a issue. When I click ok to save the item my selections are removed and the fields are left blank. Your instructions are very clear, even when through them twice, which means I’m missing something simple, :).
Any suggestions?
Yes, it can work fine with cascading dropdowns as well, just tried this together with SPservices at codplex, as you may have to modified in order to get an empty dropdown when the page loads.
Christian,
When using content types the fields are completely hidden so you can not access them with javascript. To have the ability to write to them using this script, you have to hide the fields with javascript and not by setting the field as hidden in the content type.
Alexander
Hi, yes i was struggle with that at the first.. but now it works just fine, thanks again for this nice JS solution for this, keep on Alexander!
Larry,
You can use a single line text-field, but then you are restricted to 255 characters max.
Regarding the disappearing selections – it is by design. The selection is written to the hidden “accumulator-field” and therefore i thought it would be best to remove the single-choice value before saving.
If you want the last single-choice value to be saved in the choice-field, comment out or remove line 90 in the codeblock above.
Alexander
Yes I began understanding that going thru your code. I like that you actuall hide the multiple lines, I like the delete feature. I am not losing just the last item on save. I am losing all items on save. everything appears to work, no errors. upon return to the view after clicking OK, I only have text in the Title field. I will try your suggestion
interesting out come. When I made the suggested change only the last selection displayed, no matter how many choices I made it returned the last. I place the code back in and reran, no change. the last update remained would not accept new changes.
Larry,
Comment out the line 44. This should show the multi-line text-field.
Do you see it?
You can comment out line 94 to prevent saving of the list form when troubleshooting.
To alert the data collected from all your selections before writing to your “hidden” multi-line text-field, insert this between line 91 and 92:
alert(str);
Alexander
Alexander, interesting outcome. Commenting out line 44 resulted in not hiding the multiple lines, but still the same result. the options append under the choice menu but on save are lost. When I added the alert, it returned the string of options selected for choice, but still not saving them.
Larry,
Is your text-field of type Plain-Text?
Alexander
I think I said I missed something simple. Sorry again for wasting your time. Works perfectly
@Alexander, good day, I have a question for you. I am having an issue, not with your script. I have a choice field, multiple select. this field triggers a workflow. the problem is I think it will not trigger the flow for all selections if multiple items are selected. I am not using if field equals I am using if field contains. for some reason it only picks up one item. My question for you is If I switch to your script, and use the if field contains, can it read all the selections?
Hi,
To compare against a SharePoint multi-lookup field you must use “Eguals” as “Contains” does not work (as you figured out). “Equal” does actually work as you would think the “Contains” would do.
If you go for “my solution”, you can go with “Contains” as it is a plain text-field.
Alexander
thanks I will work on that
Hey again Alexander,
I have tried to add a visual break for each selection on line 37 I added “;” before the ending div, but this causes the script to break and allows duplicate selection. Where is the best place to add a character after each selection like this
item1 ;
item2 ;
Hi,
I have fiddled a bit with the code during the Christmas holiday’s. I will update the code to support a “delimiter character”.
The update also changes the method of adding to the selection in the way that adding an item also removes it from the dropdown.
I will most likely post the updated code tonight.
Alexander
Hi Larry,
The code is updated. Please review and let me know if it you have some comments.
Regards
Alexander
You are the man. thanks!
I am not sure what I did. I have updated with your new code. Using Convert Singleline textfield to filtered lookup dropdown, and this script. the singleline text populates correctly, when I make selections the accumulated test displays the path back the the source item. I created a demo list with just 3 fields Title, TowersSupported, Towers_Supported
the accumulated returns this
/sites/olr/Lists/GKSource/DispForm.aspx?ID=3
this is a link to the selected item, but I have no clue why this is showing and not the selected text
I found part of the problem. I know I was lining to a list in another collection. this did work at one time. Now it is not. I added a regular choice field and multiline field, on the same page and that field works perfectly.
So maybe “‘/sites/olr'” from line 9 can/cannot be used.
any suggestions
I implemented the older version of the Accumulated script, and it is working for me again, but without you upgrades. So to identify what was lost. the new version could not pull source from external list in another collection.
I do love the improvements in the new version.
This has to do with the “val” of the generated dropdown is “/sites/olr/Lists/GKSource/DispForm.aspx?ID=3”. The script is setup to read the “val” and not the “text”. For a standard drop down the “val” equals the “text”.
Try to adapt the script to use “text” in stead of “val” when reading the selected value from the dropdown.
Alexander
Finally got back to this and was able to find the fix. Thanks for not giving me the answer right away. I change line 65 from this
to this
Now the two script work perfect together and it did not impact the way it previously functioned.
I do like this val function, but no control over the value returned. Think i posted something bout this some time back. that for another day. thanks a bunch.
Here is a trick question, lol. This script uses 2 fields to get the results or a multiople select. How can this be made a required field. Cant require the choice or first field because that value is noit stored there. Cant make the multiple line or second field requied, because the value has not reached it by the onclick or OK.
what would be the best way to make this act like a required field?
See updated code.
Alexander
VERY KOOL!
Alexander,
I really like this solution especially the click to delete item from list.
I have an additional requirement and was hoping you could point me in the right direction.(or maybe publish as a new topic).
I need to aggregate the content of a few fields into a single line before accumulating into a list that can contain multiple entries and be easily deleted during data entry (the source fields include Choice (drop downs) a multiline text and possibly a person/group field, I’m basically building a compact log entry) I was thinking that an “Add” button could aggregate the values and place in the accumulated list while still keeping the click to delete funtionality.
I looked at your other solution https://spjsblog.com/2009/11/12/collect-input-from-custom-input-fields-and-store-in-multiline-plain-text-field/ but that doesn’t support multiline or person fields and doesn’t have the delete feature.
Please let me know if you need any additional info
Thanks,
Take a look at this one: vLookup type rollup for SharePoint (SharePoint 2007)
Alexander
Thanks, I took a look but it is geared to referencing a different list. In my case people will be entering a variable amount of log entries that are assosciated with the current list item, I do not need a parent child setup. I’ve actually been working on modifying the above solution to meet my needs and seem to have most of it working, I just stuck on how to trigger the code that concatenates and populates the accumulated field, since I want them to prefill the fields before adding to accumulated field I was thinking of som “Add Items” button but I’m not sure how to add that to the form
Thanks, .
Alexander,
Any chance of this being updated? I actually have a use for this in SP2010.
Thanks,
Bob
Hi,
I took a quick look at the code and cannot see any obvious reasons to why this should not work. Have you tested it?
You might want to change the “fields = init_fields();” to use “init_fields_v2();” from spjs-utility.js.
Alexander
Alexander,
I did not try it but will give it a whirl today.
Bob