0

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);
        }
  • I have used the Developer tools (F12) and looked at the code written after attaching but not clicking the save button yet, it seems the "Attach" button in the ribbon for standard list items creates a new HTML FileUpload Control each time it is clicked and just appends an # after it. Now I have to figure out how the code is tracking the controls, is it a counter, an array of control object, or perhaps it is re-iterating thru the attachments every time the button its clicked and getting count that way and just appending it to the name of the newly created HTML FileUpload control? – Shane Gib. Mar 27 '17 at 18:13
0

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.

  • I thought about the idea of creating a separate attachment page after the item is create as per your suggestion, but client doesn't like this behavior. I also thought about adding a fixed number of HTML FileUpload controls i.e. 5 and that way I could use the code here and attach the items: sharepoint.stackexchange.com/questions/136577/… I may just create a button that when clicked will create a new HTML FileUpload Control on the fly & have a counter, use code from previous link above & just check for blanks before saving form. Any thoughts? – Shane Gib. Mar 27 '17 at 18:16
  • My suggestion is transparent for the user. He/she will upload the attachment on the first form. When the form is saved, the item is created and the second page appears just to show a progress bar. In the "backend" you just need to get the new created item ID and use it to move the file from the document library/localstorage to the item itself. It should take just a few seconds while the user will see a progress bar. I don't understand your solution: in all cases you first need to save your item into the list before attaching any file. You cannot attach a file if the item doesn't exist... – AymKdn Mar 28 '17 at 8:13
  • Do this in a test environment it will help explain what I am referencing in the process. Pick any list, click new item on ribbon (this opens the newform.aspx) Now click the attach item on the ribbon (this opens the Attachfile.aspx) Now select a file from local PC click OK, you are now back on the new item form (newform.aspx), repeat process, a couple of times, ending on the new item form Now you have i.e. 3 documents READY to be attached when you click OK on the new item form, BUT no item has yet to be created nor was any uploaded to server YET! This is the process I want to mimic... – Shane Gib. Mar 29 '17 at 15:48
  • What happens if a user changes there mind 1/2 thru filling out the form, decides they don't need to fill this form out, they click the cancel button on the new item form (newform.aspx) According to your approach an item would have been created but is now not needed... So maybe I should be saying pre-attachment behavior is what I want to mimic. And not the attachment behavior I examined the OOTB code and every time you click the attach item button (as outlined in my previous comment) and attach an item MS is just creating a new hidden HTML File Control and appending # to name for this – Shane Gib. Mar 29 '17 at 16:06
  • As I've already explained, the files are sent to the server with the form, and the server handles all together. In JavaScript we cannot replicate this behavior with the Web services... Because with the JavaScript API provided by Microsoft we can only add attachments to existing items. Once again, my solution provides the exact same behavior than the actual Microsoft form: the files are stored while the user fills the form, so if he decided to cancel then the files are deleted. You don't seem to understand so I cannot help you more. – AymKdn Mar 29 '17 at 20:05
0

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.