Issue with assigning file to attachment field on new record

Brandon P1
Mega Guru

I have a UI macro that calls a UI page popup that presents a basic form. The idea is user fills out form and a new record will be generated in a table I have called attachment. Almost everything works except the actual file selected for the attachment does not get added to the new record.

UI Page:
HTML:

<?xml version="1.0" encoding="utf-8" ?>
<j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide" xmlns:j2="null" xmlns:g2="null">

    <style>
        /* Add custom styles for the table */
        table {
            border-collapse: separate;
            border-spacing: 0 10px; /* Adjust the vertical spacing between rows */
            width: 100%;
        }

        /* Add padding to the table cells */
        td {
            padding: 5px;
        }

        /* Add rounded corners to the input boxes */
        input[type="text"],
        input[type="file"],
        textarea {
            border-radius: 5px;
        }

        /* Add spacing between labels and input fields */
        td > label {
            margin-bottom: 5px; /* Adjust the spacing as needed */
            text-align: left; /* Align labels to the left */
            display: block; /* Place each label on a new line */
            width: 100%; /* Adjust the width of labels as needed */
        }

        /* Add styles for the subtext */
        .subtext {
            font-size: 12px;
            color: #888;
            margin-top: 5px; /* Adjust the spacing between labels and input fields */
            text-align: left; /* Align the subtext to the left */
        }
        
        /* Add class for table rows */
        .input-row {
            display: block; /* Place each input section in a new line */
        }
    </style>

    <g:ui_form>
        <table>
            <tr>
                <td>
                    <label>Title:</label>
                    <div class="subtext">(Please enter short description or name of attachment)</div>
                    <input type="text" name="title" id="title" required="true" />
                </td>
            </tr>
            <tr>
                <td>
                    <label>Alternate Location of saved file:</label>
                    <input type="text" name="location" id="location" class="input-row" />
                </td>
            </tr>
            <tr class="input-row">
                <td>
                    <label>Attachment:</label>
                    <input type="file" name="attachment" id="attachment" />
                </td>
            </tr>
            <tr>
                <td>
                    <label>Notes:</label>
                    <textarea name="notes" id="notes" class="input-row"></textarea>
                </td>
            </tr>
        </table>

        <div>
            <g:dialog_buttons_ok_cancel
                ok_id="submitData"
                ok="return continueOK()"
                ok_type="button"
                ok_text="${gs.getMessage('Okay')}"
                ok_style_class="btn btn-primary"
                cancel_type="button"
                cancel_id="cancelData"
                cancel_style_class="btn btn-default"
                cancel="return continueCancel()" />
        </div>

    </g:ui_form>

</j:jelly>

 

Client Script:

function continueOK() {

	//lets pull in the form input to be used to create an attachment record
	var title = gel('title').value;
	var location = gel('location').value;
	var attachment = gel('attachment').value;
	var notes = gel('notes').value;
	var sysID = g_form.getUniqueValue();
	
	//alert("OK clicked - " + title + ", " + location + ", " + attachment +  ", " + notes + ", " + sysID) ;
	
	// Create a new instance of GlideAjax
    var ga = new GlideAjax('x_cinep_incident_0.iltAjaxUtil');
    ga.addParam('sysparm_name', 'addAttachment');
    ga.addParam('sysparm_sysId', sysID);
    ga.addParam('sysparm_title', title);
    ga.addParam('sysparm_location', location);
    ga.addParam('sysparm_attachment', attachment);
    ga.addParam('sysparm_notes', notes);
    ga.getXML(addAttachmentCallback);
	
	GlideDialogWindow.get().destroy();
}

function continueCancel() {
	//alert("Cancel clicked");
	GlideDialogWindow.get().destroy();
}

function addAttachmentCallback(response) {
    var message = response.responseXML.documentElement.getAttribute('answer');
    alert(message);
}

 

Script include(GlideAjax):

var iltAjaxUtil = Class.create();
iltAjaxUtil.prototype = Object.extendsObject(global.AbstractAjaxProcessor, {

    addAttachment: function() {

        try {

            var iltSysID = this.getParameter('sysparm_sysId');
            var title = this.getParameter('sysparm_title');
            var location = this.getParameter('sysparm_location');
            var attachment = this.getParameter('sysparm_attachment');
            var notes = this.getParameter('sysparm_notes');

            //lets create a new Attachment record and populate it with the provided information.
            var grAttach = new GlideRecordSecure('x_cinep_incident_0_attachments');
            grAttach.initialize();
            grAttach.setValue("title", title);
            grAttach.setValue("ilt", iltSysID);
            grAttach.setValue("attachment", attachment);
            grAttach.setValue("alternate_location_of_saved_file", location);
            grAttach.setValue("notes", notes);

            grAttach.insert();

            return 'Attachment added successfully';
        } catch (ex) {
			
            return ex;
        }
    },

Here are some screen grabs of what is happening 
POPUP:

Add atachment popup.png

List showing no file attached:

list.png

Record with no file attached

record.png

 

Any assistance would be appreciated.

Thanks,

Brandon 

2 REPLIES 2

Sai Shravan
Mega Sage

Hi @Brandon P1 ,

It seems that the issue lies in how you handle the file attachment in your UI macro and script. In JavaScript, you cannot directly access the file path or content of a file input field due to security restrictions. Instead, you need to use the FormData object to handle file uploads

UI Page:

Add an onsubmit attribute to your form tag to call the 'continueOK' function:

<g:ui_form onsubmit="return continueOK()">

Client Script:

Replace the continueOK function with the updated version below:

function continueOK() {
    // Create a new instance of FormData
    var formData = new FormData();

    // Append the form data to the FormData object
    formData.append('title', gel('title').value);
    formData.append('location', gel('location').value);
    formData.append('attachment', gel('attachment').files[0]);
    formData.append('notes', gel('notes').value);
    formData.append('sysID', g_form.getUniqueValue());

    // Create a new XMLHttpRequest object
    var xhr = new XMLHttpRequest();
    xhr.open('POST', 'x_cinep_incident_0.iltAjaxUtil.addAttachment', true);

    // Set the onload callback function
    xhr.onload = function () {
        if (xhr.status === 200) {
            var response = JSON.parse(xhr.responseText);
            addAttachmentCallback(response);
        } else {
            // Handle the error case
            addAttachmentCallback({ answer: 'Error: ' + xhr.status });
        }
    };

    // Send the FormData object with the XMLHttpRequest
    xhr.send(formData);

    // Prevent the default form submission
    return false;
}

Script Include (GlideAjax):

Modify the addAttachment function to handle file attachments properly and return the response as JSON:

addAttachment: function() {
    try {
        var iltSysID = this.getParameter('sysparm_sysId');
        var title = this.getParameter('sysparm_title');
        var location = this.getParameter('sysparm_location');
        var attachment = this.getParameter('sysparm_attachment');
        var notes = this.getParameter('sysparm_notes');

        // Handle file attachment
        var attachmentSysID = '';
        if (attachment) {
            var grAttachment = new GlideSysAttachment();
            attachmentSysID = grAttachment.write('x_cinep_incident_0_attachments', iltSysID, attachment);
        }

        // Create a new Attachment record and populate it with the provided information
        var grAttach = new GlideRecordSecure('x_cinep_incident_0_attachments');
        grAttach.initialize();
        grAttach.setValue('title', title);
        grAttach.setValue('ilt', iltSysID);
        grAttach.setValue('attachment', attachmentSysID);
        grAttach.setValue('alternate_location_of_saved_file', location);
        grAttach.setValue('notes', notes);
        grAttach.insert();

        return { answer: 'Attachment added successfully' };
    } catch (ex) {
        return { answer: 'Error: ' + ex };
    }
},

 

With these modifications, the file attachment will be properly handled using FormData in the client script, and the attachment will be written to the record in the script include. The response from the server will be returned as JSON and passed to the "addAttachmentCallback" function for further processing.

 

Regards,

Shravan

Please mark this as helpful and correct answer, if this helps you

Regards,
Shravan
Please mark this as helpful and correct answer, if this helps you

I had posted a response to this yesterday but seems to have been removed. 

First, thanks for the response! 

 

So I have implemented the changes suggested, however I am now running into a client side error.  The 

var response = JSON.parse(xhr.responseText);

line is not receiving a JSON as expected, instead is receiving a large HTML doc.  the error that the browser is throwing is as follows:

3545:1 Uncaught SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON
    at JSON.parse (<anonymous>)
    at xhr.onload (eval at evalScript (VM2826 js_includes_doctype.jsx:76:29), <anonymous>:16:33)
xhr.onload @ VM3541:16
load (async)
continueOK @ VM3541:13
onclick @ VM3544 x_cinep_incident_0_incident_loss_tracker.do:1

I am including the updated code for reference

 

UI page: HTML

<?xml version="1.0" encoding="utf-8" ?>
<j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide" xmlns:j2="null" xmlns:g2="null">

    <style>
        /* Add custom styles for the table */
        table {
            border-collapse: separate;
            border-spacing: 0 10px; /* Adjust the vertical spacing between rows */
            width: 100%;
        }

        /* Add padding to the table cells */
        td {
            padding: 5px;
        }

        /* Add rounded corners to the input boxes */
        input[type="text"],
        input[type="file"],
        textarea {
            border-radius: 5px;
        }

        /* Add spacing between labels and input fields */
        td > label {
            margin-bottom: 5px; /* Adjust the spacing as needed */
            text-align: left; /* Align labels to the left */
            display: block; /* Place each label on a new line */
            width: 100%; /* Adjust the width of labels as needed */
        }

        /* Add styles for the subtext */
        .subtext {
            font-size: 12px;
            color: #888;
            margin-top: 5px; /* Adjust the spacing between labels and input fields */
            text-align: left; /* Align the subtext to the left */
        }
        
        /* Add class for table rows */
        .input-row {
            display: block; /* Place each input section in a new line */
        }
    </style>

    <g:ui_form onsubmit="return continueOK()">
        <table>
            <tr>
                <td>
                    <label>Title:</label>
                    <div class="subtext">(Please enter short description or name of attachment)</div>
                    <input type="text" name="title" id="title" required="true" />
                </td>
            </tr>
            <tr>
                <td>
                    <label>Alternate Location of saved file:</label>
                    <input type="text" name="location" id="location" class="input-row" />
                </td>
            </tr>
            <tr class="input-row">
                <td>
                    <label>Attachment:</label>
                    <input type="file" name="attachment" id="attachment" />
                </td>
            </tr>
            <tr>
                <td>
                    <label>Notes:</label>
                    <textarea name="notes" id="notes" class="input-row"></textarea>
                </td>
            </tr>
        </table>

        <div>
            <g:dialog_buttons_ok_cancel
                ok_id="submitData"
                ok="return continueOK()"
                ok_type="button"
                ok_text="${gs.getMessage('Okay')}"
                ok_style_class="btn btn-primary"
                cancel_type="button"
                cancel_id="cancelData"
                cancel_style_class="btn btn-default"
                cancel="return continueCancel()" />
        </div>

    </g:ui_form>

</j:jelly>

UI Page: Client Script

function continueOK() {
	
    var formData = new FormData();

    formData.append('title', gel('title').value);
    formData.append('location', gel('location').value);
    formData.append('attachment', gel('attachment').files[0]);
    formData.append('notes', gel('notes').value);
    formData.append('sysID', g_form.getUniqueValue());

    var xhr = new XMLHttpRequest();
    xhr.open('POST', 'x_cinep_incident_0.iltAjaxUtil.addAttachment', true);

    xhr.onload = function() {
        if (xhr.status === 200) {
            console.log('<BP>continueOK: Response - ' + xhr.responseText);
            var response = JSON.parse(xhr.responseText);
            addAttachmentCallback(response);
        } else {
            console.log('<BP>continueOK: Error - ' + xhr.status);
            addAttachmentCallback({
                answer: 'Error: ' + xhr.status
            });
        }
    };

    xhr.send(formData);

    return false;
}

function continueCancel() {
    GlideDialogWindow.get().destroy();
}

function addAttachmentCallback(response) {
    var message = response.answer; // Access the 'answer' property directly
    alert(message);
}

Script Include

var iltAjaxUtil = Class.create();
iltAjaxUtil.prototype = Object.extendsObject(global.AbstractAjaxProcessor, {

    addAttachment: function() {
        try {
			
			gs.info('<BP> iltAjaxUtil:addAttachment: start ');
			
            var iltSysID = this.getParameter('sysparm_sysId');
            var title = this.getParameter('sysparm_title');
            var location = this.getParameter('sysparm_location');
            var attachment = this.getParameter('sysparm_attachment');
            var notes = this.getParameter('sysparm_notes');

            var attachmentSysID = '';
            if (attachment) {
                var grAttachment = new GlideSysAttachment();
                attachmentSysID = grAttachment.write('x_cinep_incident_0_attachments', iltSysID, attachment);
            }

            var grAttach = new GlideRecordSecure('x_cinep_incident_0_attachments');
            grAttach.initialize();
            grAttach.setValue('title', title);
            grAttach.setValue('ilt', iltSysID);
            grAttach.setValue('attachment', attachmentSysID);
            grAttach.setValue('alternate_location_of_saved_file', location);
            grAttach.setValue('notes', notes);
            grAttach.insert();

            return this.newItem('answer', 'Attachment added successfully');
        } catch (ex) {
			
            gs.info('<BP> iltAjaxUtil:addAttachment: Error - ' + ex);
            return {
				
                answer: 'Error: ' + ex
            };
        }

    },

    type: 'iltAjaxUtil'
});

and here is a picture of the document being logged

html doc.png

Any suggestions or help would be very much appreciated, and thank you so much.

 Brandon