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);