Getting a SharePoint list and attachments at the same time using javascript

Charles Graham 21 Reputation points
2022-05-25T19:37:48.47+00:00

Brief synopsis of what i am trying to do:
I have a list with the following fields: document type, title of the entry, and an attachments field.

I need to get the entries along with the url(s) to the attachment(s) for the list item. I have it mostly figured out, but i can't seen to get the process to wait until i have all the information before sending it to the browser page. I know how to get the things separately, but do not know how to - or if it's even possible - to get the list item and attachment info at the same time - rather than doing two separate async queries.
I would like to get the list items, group them by document type and display separate groupings on the page that open an attachment modal when you click the item's displayed title. When i try to do this, it always paints the items before the attachments are actually added to the JSON object data.

here is the code i have

    var fileList = [];
var finished = false;
function loadBootstrap() {
    //check for the required boostrap stuff
    var bootJs = document.querySelector('head').querySelector('#bootstrap-js');
    if (!bootJs) {
        bootJs = document.createElement('script');
        bootJs.setAttribute("src", "https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js");
        bootJs.id = "bootstrap-js";
        document.querySelector('head').append(bootJs);
    }

    var popper = document.querySelector('head').querySelector('#popper');
    if (!popper) {
        popper = document.createElement('script');
        popper.setAttribute("src", "https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js");
        popper.id = "popper";
        document.querySelector('head').append(popper);
    }
}
function getList() {
    try {
        if (typeof $().jquery === 'unefined') {
            let jqueryJs = document.createElement('script');
            jqueryJs.setAttribute("src", "/netcom/sites/516SB/SiteAssets/JScripts/jquery-3.3.1.min.js");
            jqueryJs.id = "jquery-js";
            document.querySelector('head').append(jqueryJs);
            let jqueryWaiter = setInterval(() => {
                if (typeof $().jquery !== 'unefined') {
                    clearInterval(jqueryWaiter);
                    jqueryWaiter = null;
                    loadBootstrap();
                }
            });
        } else {
            loadBootstrap()
        }
    } catch {
        let jqueryJs = document.createElement('script');
        jqueryJs.setAttribute("src", "/netcom/sites/516SB/SiteAssets/JScripts/jquery-3.3.1.min.js");
        jqueryJs.id = "jquery-js";
        document.querySelector('head').append(jqueryJs);
        let jqueryWaiter = setInterval(() => {
            if (typeof $ !== "undefined" && typeof $().jquery !== 'unefined') {
                clearInterval(jqueryWaiter);
                jqueryWaiter = null;
                loadBootstrap();
            }
        });
    }


    getListItems('Staff Docs List',
        function (listItems) {

            for (var i = 0; i < listItems.length; i++) {
                GetAttachments(listItems[i],
                    function (sendera, args) {
                        console.log(args.get_message());
                    });
                fileList.push(listItems[i]);
            }
            let finishedWaiter = setInterval(() => {
                let totalFilesWithAttachments = fileList.map(x => x.hasAttachments).length;
                let totalAttachmentsFinished = fileList.map(x => x.attachements!==undefined).length;
                if (totalAttachmentsFinished == totalFilesWithAttachments) {
                    clearInterval(finishedWaiter);
                    finished = true;
                    buildIt();
                }
            });
        },
        function (sendera, args) {
            console.log(args.get_message());
        });
}

class AttachmentWindow {
    constructor(attachementList, itemName) {
        this.html = this.build();
    }
    build() {
        let html = document.createElement('div');
        html.className = "modal fade";
        html.id = "myModal";
        html.innerHTML = `<div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header"> Documents for <span class="item-name"></span>
                <button class="close btn bnt-danger" aria-label="close modal" data-dismiss="modal">✕</button>
            </div>
            <div class="modal-body">
            </div>
        </div>`
        return html;
    }
}
class StaffDocs {
    constructor(item) {
        this.title = item.title;
        this.attachments = item.attachments;
        return this.build();
    }
    build() {
        let html = document.createElement('div');
        html.className = "document-item";
        html.innerText = this.title;
        html.title = "click to see attachments";
        html.dataset.toggle = "modal";
        html.dataset.target = "#myModal";
        html.addEventListener('click', () => {
            let modal = document.querySelector('.modal');
            let title = modal.querySelector('.item-name');
            title.innerText = this.title;
            let content = modal.querySelector('.modal-body');
            content.innerHTML = '';
            this.attachements.forEach(item => {
                let attachmentDiv = document.createElement('div');
                attachmentDiv.innerHTML = `<div><a href="${item.url}" target="_blank">${item.name}</a></div>`;
                content.append(attachmentDiv);
            });

        });
        return html;
    }
}

class DocCategory {
    constructor(typeName, files) {
        this.typeName = typeName;
        this.items = files;
        this.css();
        return this.build();
    }
    build() {
        let div = document.createElement('div');
        div.className = this.typeName.replace(' ', '');
        div.className += " flex flex-box";
        div.innerHTML = `<h2>${this.typeName}</h2><hr />`;
        let fileContents = document.createElement('div');
        div.append(fileContents);
        fileContents.className = "fileList";
        this.items.forEach(item => {
                let doc = new StaffDocs(item);
                fileContents.append(doc);           
        });
        return div;
    }
    css() {
        var style = document.querySelector('head').querySelector('#staffDocsCss');
        if (!style) {
            style = document.createElement('style');
            document.querySelector('head').append(style)
            style.id = "staffDocsCss";
            style.type = "text/css";
            let cssText = `.${this.typeName.replace(' ', '')}{display:flex; width:100%;flex-overflow:wrap;margin-bottom:50px;}
        .${this.typeName.replace(' ', '')} .document-item{flex - basis:row;width:100%}`;
            style.append(document.createTextNode(cssText));
        }
    }
}
function buildIt() {
    var waiter = setInterval(() => {
        if (finished) {
            clearInterval(waiter);
            let modal = new AttachmentWindow().html;
            document.querySelector('body').append(modal);
            let docTypes = fileList.map(x => x.documentCategory);
            let target = document.querySelector('.staff-doc-area');
            target.innerHTML = '';
            docTypes.forEach(doc => {
                let files = fileList.filter(x => x.documentCategory == doc);
                let category = new DocCategory(doc, files);              
                target.append(category);
            });
        }
    }, 10)
}

function getListItems(listTitle, success, error) {
    let ctx = SP.ClientContext.get_current();
    let list = ctx.get_web().get_lists().getByTitle(listTitle);
    let qry = new SP.CamlQuery();
    let listItems = list.getItems(qry);
    ctx.load(listItems);
    ctx.executeQueryAsync(
        function () {
            let listItemEnumerator = listItems.getEnumerator();
            let attachmentsList = [];
            while (listItemEnumerator.moveNext()) {
                let item = listItemEnumerator.get_current();
                Item = item;
                let title = item.get_item('Title');
                let documentCategory = item.get_item('Document_x0020_Type');
                let hasAttachments = item.get_fieldValues()['Attachments'];
                attachmentsList.push({
                    title: title,
                    documentCategory: documentCategory,
                    hasAttachments: hasAttachments,
                    fileDirRef: item.get_fieldValues()['FileDirRef'],
                    Id: item.get_fieldValues()['ID'],
                    shown: item.get_fieldValues()["Shown_x0020_on_x0020_List"]
                });
            }
            success(attachmentsList);
        },
        error);
}
function GetAttachments(listItem, error) {
    if (listItem.hasAttachments) {
        let ctx = SP.ClientContext.get_current();
        let attachmentFolderUrl = String.format('{0}/Attachments/{1}', listItem.fileDirRef, listItem.Id);
        let folder = ctx.get_web().getFolderByServerRelativeUrl(attachmentFolderUrl);
        let files = folder.get_files();
        ctx.load(files);
        ctx.executeQueryAsync(
            function () {
                let attachments = [];
                for (let i = 0; file = files.get_item(i); i++) {
                    attachments.push({ url: file.get_serverRelativeUrl(), name: file.get_name() });
                }
                listItem["attachments"] = attachments;
            },
            error);
    }
}

SP.SOD.executeFunc('sp.js', 'SP.ClientContext', getList);
SharePoint Development
SharePoint Development
SharePoint: A group of Microsoft Products and technologies used for sharing and managing content, knowledge, and applications.Development: The process of researching, productizing, and refining new or existing technologies.
2,668 questions
{count} votes

Accepted answer
  1. Tong Zhang_MSFT 9,116 Reputation points
    2022-05-30T01:16:26.917+00:00

    Hi @Charles Graham ,

    Thanks for the detailed description of the case. When I received the case, I searched a lot of documents did a lot of researches. And I feel regretful to inform you that according to my research, there is no way to get a SharePoint list and attachments at the same time. It is only possible to get this information separately. Thanks for your understanding and support.


    If the answer is helpful, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".
    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.


    0 comments No comments

0 additional answers

Sort by: Most helpful