Question

I found the link below kind of useful, and it works with my sample code below, but the problem is, this is for an existing item... I want to know how to do it if the item has not been created yet in a custom new items html form. In this scenario there is no itemID to pass to the dialog (as per the code below), this would be the same even with standard new item form, the attachment button, and dialog out of the box from Microsoft, so how do we handle this? How does Microsoft handle this?

Reason I don't want to create the itme first is that a user may start a form then changes there mind and just closes the form, if we create an item to attach to then you have a new item that is left in the list, further, workflows would fire as well cause new item was created.

I could use the HTML FileInput Control and rest API, but was hoping to use the SharePoint Dialog Framework and understand how to replicate what MS is already doing.

Perhaps this is done with just adding it to a hidden folder I don't know about and then moving the item after the submit button is clicked, only thing I could think of but then how does that folder get cleaned up, and what would the move code look like.

PS I am currently using SharePoint 2013 Enterprise on-prem

Thanks in advance for the help and suggestions. SG.

Link reference:

Using Sharepoint client API to upload file as attachment in javascript

My code (Basically from link above but tested in my environment):

HTML File:

    <!--Your Custom CSS reference-->
    <link rel="stylesheet" type="text/css" href="/SiteAssets/CSS/CSS_File.css">
    <link rel="stylesheet" type="text/css" href="/SiteAssets/CSS/jquery.ui.css">

    <!--JQuery reference-->
    <script type="text/javascript" src="/SiteAssets/JQuery/jquery.min.js"></script>
    <script type="text/javascript" src="/SiteAssets/JQuery/jquery.ui.js"></script>

    <!--SharePoint CSOM references-->
    <script type="text/javascript" src="/_layouts/15/sp.js"></script>
    <script type="text/javascript" src="/_layouts/15/sp.runtime.js"></script>
    <script type="text/javascript" src="/_layouts/15/sp.core.js"></script>
    <script type="text/JavaScript" src="/_layouts/15/clienttemplates.js"></script>
    <script type="text/JavaScript" src="/_layouts/15/clientforms.js"></script>
    <script type="text/JavaScript" src="/_layouts/15/clientpeoplepicker.js"></script>
    <script type="text/JavaScript" src="/_layouts/15/autofill.js"></script>

    <!--Your Custom JS reference-->
    <script type="text/javascript" src="/SiteAssets/JS/Getting-Started.js"></script>

    <div>
     <table>
      <tr>
        <td>
        <span>Attachment Test</span><br />
        </td>
        </tr>
        <tr>
        <td>
        <span>This form is for testing SharePoint Attachments</span><br /><br />
        <div id="h_dummi1"><a href="javascript:openpageindialog();">Add attachment using dialog framework</a></div>
        <div id="h_dummi2"></div>
        </td>
        </tr>
     </table>
    </div>

JS:

    $(
    function ()
    {
        //alert("Hello World from Attachment Form");
    }
    );

     function openpageindialog() {
            var options = {
                url: '/_layouts/Attachfile.aspx?ListId={5505AAAD-B3D8-4D69-8E3D-EE3739D9CB08}&ItemId=1',
                title: 'Add Attachment',
                width:600,
                height: 125,
                dialogReturnValueCallback: function(result, fileAdded){

                if(result == SP.UI.DialogResult.OK){
                    v_ItemAttachments = '<p>attachments added</p>';
                    $("#h_dummi2").html(v_ItemAttachments);
                }
            }            
            }; 
            SP.UI.ModalDialog.showModalDialog(options);
        }
Was it helpful?

Solution 2

I have written the follow code using the W3School tool and was able to make it replicate the functionality and pre-attachment behavior I was looking for using JavaScript without needed to leave the form itself. Then adapted it into the SharePoint platform using JSOM/JQuery I already had in my forms JS file. For the actual attaching process I just used the code from this link by Vadim Gremyachev with a while loop and called the processUpload() function from that code sample, passing in the HTML fileUpload Control value and other parameters needed.

Thanks for the comments and thoughts I appreciate the insight.

Code: Copy code below > click the W3School's link above > paste code > click 'Run'.

<!DOCTYPE html>
<html>
<body>
<table>
  <tr>
    <td colspan="3" style="padding: 10px; vertical-align:middle; text-align:left;">
    <div style="padding: 10px; vertical-align:middle; text-align:left;">
      Attachments
    </div>
    <div id="h_ItemAttachments"></div>
    <br>
    <input type="button" id="h_btnAddFileUploadControl" value="Add Attachment" onclick="Clicked_h_btnAddFileUploadControl()" class="btn_Standard" />
    <div id="h_ItemAttachmentControls"></div>    
    </td>
  </tr>
</table>
<script>
g_FileUploadControlCounter = 0;

function Clicked_h_btnAddFileUploadControl() {

var v_btnFileUploadControl = document.getElementById("h_btnAddFileUploadControl");  
    v_btnFileUploadControl.value = "Add Another Attachment";

var n="h_Item_Attachments_FileInput" + g_FileUploadControlCounter;
var z="h_Item_Attachment" + g_FileUploadControlCounter;
var x = document.createElement("INPUT");
var y = document.createElement("br");

    x.setAttribute("type", "file");
    x.setAttribute("id", z);
    x.setAttribute("name", n);
    x.setAttribute("onchange", "UpdateAttachmentsDisplayList()");
    x.setAttribute("class", "Otr_Std_pad");
    document.getElementById("h_ItemAttachmentControls").appendChild(y);
    document.getElementById("h_ItemAttachmentControls").appendChild(x);
    g_FileUploadControlCounter++;
}

function Clicked_h_hrefRemoveFileUploadControl(v_Item_Attachment) {

    document.getElementById(v_Item_Attachment.id).value = null;
    UpdateAttachmentsDisplayList();
}

function UpdateAttachmentsDisplayList() {

var inputs = document.getElementsByTagName('input');
var txt='';

for(var i = 0; i < inputs.length; i++) {
      if(inputs[i].type.toLowerCase() == 'file') {
          if(inputs[i].value.length > 0)
          {
            var x = inputs[i];
              txt += "<strong>" + inputs[i].value + "</strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href='javascript:Clicked_h_hrefRemoveFileUploadControl(" + x.id + ");'>Delete</a><br>";
              document.getElementById(inputs[i].id).style.visibility = "hidden";
          }else{document.getElementById(inputs[i].id).style.visibility = "visible";}
      }
      document.getElementById("h_ItemAttachments").innerHTML = txt;
    }
}
</script>

</body>
</html>

Special Attention needed to be paid to IE browser b/c IE 8 and ^ have had the security changed on the HTML File Upload Control to Read-Only, to work around this for when users decide they want remove/delete a file from the new form before actually submitting form. I had to use bowser.js and check if browser was IE or Other (i.e. Chrome which the original code worked for) since JQuery removed there checking in any version higher than 1.7.2, this affects the following function and when actually implementing in SharePoint I re-wrote it like example below:

function Clicked_h_hrefRemoveFileUploadControl(v_Item_Attachment) {

    document.getElementById(v_Item_Attachment.id).value = null;
    UpdateAttachmentsDisplayList();
}

Actual code implemented in SharePoint (vs W3School sample above):

function Clicked_h_hrefRemoveFileUploadControl(v_Item_Attachment) 
{
    if(bowser.msie){
        $('#'+v_Item_Attachment).replaceWith($('#'+v_Item_Attachment).clone());
    }
    else{
        $('#'+v_Item_Attachment).val('');
    }

    UpdateAttachmentsDisplayList();
}

By using the clone method for IE it keeps all the events and listeners available behind the scenes, it was in my opinion the best choice out of the many I researched, downside, I had to add the bowser.js file to make it work, but since I am already referencing about 10 other .js files i.e. JQuery.min.js what is one more...

Also below is the code for wiring up the submit button click event > to the attachment function that checks the HTML File Upload Controls for a file > then calls the processUpload() function if the control is not null as previously mentioned:

Button Click (Check global counter to see if attachment button was clicked):

function Clicked_h_btnSubmit () {
    g_spItemID=1;

    alert("Submit button was clicked");

    if(g_FileUploadControlCounter > 0){
        Attach_Files ();
    }   
}

attachment function that checks the HTML File Upload Controls for a file:

function Attach_Files (){
    var v_AttachmentCounter = 0;
    var listTitle = g_MainListName;
    var itemId = g_spItemID;   

    while (v_AttachmentCounter < g_FileUploadControlCounter){
        var fileInput = document.getElementById("h_Item_Attachment" + v_AttachmentCounter);
        if(fileInput.files[0] != null){
            var file = fileInput.files[0];
                        processUpload(file,  listTitle, itemId,
            function(){
                console.log('Attachment file has been uploaded');  
                //alert("Attachment file has been uploaded");

            }, 
            function(sender,args){
                console.log(args.get_message());
                alert(args.get_message());
            });
        }
        v_AttachmentCounter++;
    }
}

Special notes on this code, as @Aymkdn points out, the actual attachment can not happen until you have a item to attach too, so for new items you have to in your submit button call the create_List_Item function that you will need to write I have not supplied it here mostly for out of scope and time saving reasons, just note that when you create the item to make sure and set a the global variable g_spItemID to its id so the Attach_Files () function knows what item to put the files with. ALSO, make sure to use the deffered/promise in your code and wait for results before continuing on to the attach_file () functions, otherwise you might get item doesn't exist (yet) errors. For now I just manually created the first item (SP List Id #1) and hard coded it g_spItemID with it to test with.

Take special note that before calling the processUpload() function, we check if the control is not null, if you don't check for these things in various scenarios you may end up with errors.

my personal coding standard:

g_ prefix = global variable

lcl_ prefix = local variable (usually do this when I am in a small functions or loop condition i.e. for or while)

v_ prefix = local variable to that function (usually matches an HTML Element/Control)

h_ prefix = HTML element\control id i.e.

"<input type="button" id="h_btnAddFileUploadControl" value="Add Attachment" onclick="Clicked_h_btnAddFileUploadControl()" class="btn_Standard" />"

or

document.getElementById("h_btnAddFileUploadControl");

I like to keep things transparent between the HTML and JavaScript so often I do this to keep the JavaScript variables understandable in code:

var v_btnFileUploadControl = document.getElementById("h_btnAddFileUploadControl");

This way I know the JS variable holds the Add fileUpload Control Button object never have to guess for element id's this way...

I truly hope this helps some folks in the future.

Thanks.

SG.

OTHER TIPS

Microsoft does the attachment on server side: the form and the attachments are sent to the server (at least it's my understanding).

In JavaScript is going to be more tricky....

For example you could save the file (in a document library or localstorage) and when the user saves the form you can redirect him/her to another page where you'll handle the transfer of the document to the new created item. If you use a document library to store the temporary files, then you can use a workflow or the retention rules to delete the uploaded documents there after 30 minutes to remove the uploaded files that haven't been attached to an item.

Licensed under: CC-BY-SA with attribution
Not affiliated with sharepoint.stackexchange
scroll top