javascriptjquerysharepointinfopathsharepoint-online

Workaround to add repetitive controls to a SharePoint form without InfoPath


I'm trying to create a SharePoint form with repetitive controls.I'm a SharePoint Online user(Office 365).

I'm looking for a workaround to do this without using InfoPath or any third party software like Nintex. A JavaScript solution would be ideal.

Any suggestion is highly appreciated.


Solution

  • Since you know how to insert JavaScript into the form, you can store your repeating data in a multiline text field.

    Store the data in a structured format, such as JSON, within the multiline text field, then use JavaScript to read the value of that field and render appropriate repeating controls (labels, text inputs, dropdowns) on the screen.

    Check out my ugly but functional example below (replace the words "Multiline Text Column" with the display name of the multiline text field in which you want to store the repeating data):

    (function(){
      // define the data you want captured in the repeating field
      var fields = [{type:"text",label:"Employee Name"},
                  {type:"text",label:"ID Number"},
                  {type:"choice",label:"Status",choices:["Full Time","Part Time","Intern","Consultant"]}
                 ];
      // find and hide the text area
      var textarea = document.querySelector("[title=\"Multiline Text Column\"]");
      // textarea.style.display = "none"; // uncomment this line to hide the text area
      // create a new container for your repeating field
      var repeatingData = [];
      var container = document.createElement("div");
      textarea.parentNode.appendChild(container);
      // add a button for adding new rows
      var addButton = document.createElement("input"); 
      addButton.setAttribute("type","button");
      addButton.value = "Add row";
      addButton.addEventListener("click",function(){addRow();});
      textarea.parentNode.appendChild(addButton);
      // get the existing data, if any
      var existing = JSON.parse(textarea.value);
      for(var i = 0; i < existing.length; i++){
        addRow(existing[i]);
      }
      function addRow(data){
        if(typeof data === "undefined"){
          data = {};
        }
        repeatingData.push(data);
        var last = repeatingData.length-1;
        var index = last;
        if(last > 0){index = repeatingData[index-1]["data-id"] + 1;}
        repeatingData[last]["data-id"] = index;
        var row = document.createElement("div");   
        row.setAttribute("data-id",index);
        row.style.border = "1px solid black";
        row.style.margin = "2px"; row.style.padding = "2px";
        for(var i = 0; i < fields.length; i++){
          var field = fields[i];
          var lbl = document.createElement("span");
          lbl.insertAdjacentHTML("beforeend",field.label+": ");
          row.appendChild(lbl);
          switch(field.type){
              case "text":      
                var txt = document.createElement("input");
                txt.setAttribute("type","text");
                if(data[field.label]){
                   txt.value = data[field.label];
                }
                (function(label){
                  txt.addEventListener("keyup",function(){
                    getRecord(index)[label]=this.value;
                    updateTextArea();});
                })(field.label);
                row.appendChild(txt);
                break;
              case "choice":
                var sel = document.createElement("select");
                var option = sel.appendChild(document.createElement("option"));
                option.value = "";
                option.innerHTML = "";
                for(var j = 0; j < field.choices.length; j++){
                  option = document.createElement("option");
                  option.value = field.choices[j];
                  option.appendChild(document.createTextNode(field.choices[j]));
                  sel.appendChild(option);
                }
                if(data[field.label]){
                   sel.value = data[field.label];
                }
                (function(label){
                  sel.addEventListener("change",function(){
                    getRecord(index)[label]=this.value;
                    updateTextArea();});
                })(field.label);
                row.appendChild(sel);
                break;
          }
          row.appendChild(document.createElement("br"));
        }
        var remove = document.createElement("a");
        remove.href = "return false;";
        row.appendChild(remove);
        remove.innerHTML = "remove";    
        remove.addEventListener("click",function(event){
          event.preventDefault(); 
          repeatingData.splice(getRecordIndex(index),1); 
          container.removeChild(row); 
          updateTextArea();
          return false;});
        container.appendChild(row);
        updateTextArea();
      }
      function getRecord(i){
        return repeatingData[getRecordIndex(i)];
      }
      function getRecordIndex(i){
        for(var j = 0; j < repeatingData.length; j++){
          if(repeatingData[j]["data-id"] == i){
            return j;
          }
        }
        return -1;
      }
      function updateTextArea(){
        textarea.value = JSON.stringify(repeatingData);
      }
    })();
    .ms-formtable{
      font-family:"Segoe UI","Segoe",Tahoma,Helvetica,Arial,sans-serif;
      font-size:13px;
      font-weight:400;
    }
    .ms-formlabel{  
      padding-right:5px;
    }
    .ms-long{ border:1px solid rgb(186,186,186); padding-left:5px; padding-right:5px; padding-bottom:2px; padding-top:2px;
    <table class="ms-formtable">
    <tr>
      <td width="113" class="ms-formlabel" valign="top">
        <span class="ms-h3 ms-standardheader"><nobr>Multiline Text Column</nobr></span>
      </td>
      <td width="350" class="ms-formbody" valign="top">
         <span dir="none"><textarea class="ms-long" title="Multiline Text Column" cols="20">[{"Employee Name":"Joe Blow","ID Number":"123","Status":"Full Time"},{"Employee Name":"Jane Doe","ID Number":"321","Status":"Consultant"}]</textarea></span>
      </td>
    </tr>
    </table>

    Notice that whenever the values are changed in the row controls, the underlying value in the textarea changes. When you save the form, the value in the textarea is what will be saved to SharePoint.

    Note that you'd need slightly different (simpler) code to make this work for display forms as well as edit forms.