0

I will first start out with saying that this solution was originally implemented as CSR/JSLink, but then a requirement came that all the functionality had to work in any personal views the users might create. I tried for quite some time to find a way to apply the jslink to personal views, but was never successful.

So, here I am instead trying to make the same functionality work from an _spBodyOnLoadFunctionNames.push within some custom js that is included in our master page.

The basic issue is that if the list contains multiple pages of items, and you navigate to any of the pages past the first page, ctx.ListData.Row only contains the rows from the FIRST page if the user happens to refresh (F5) the page. Due to that the code does not work as expected as it only sees the rows from Page 1.

Below is the function I have been using (called from _spBodyOnLoadFunctionNames as mentioned above), and as mentioned to replicate this issue make sure you have a list that has more then one page of rows, navigate to the next page, and then refresh the page.

function setProtectedIcon(){

if (ctx == undefined || ctx.ListData == undefined){

    return;
}

var listRows = ctx.ListData.Row;
var includedContentTypes = ["Project Documents",
                            "Excel Project Documents",
                            "Comment Response Sheet",
                            "General Document",
                            "Meeting Minutes",
                            "Presentation 4x3 standard",
                            "Presentation 16x9 wide screen",
                            "Task Deliverable",
                            "Transmittal"];

for (var i = 0; i < listRows.length; i++){

    //we only need to perform the check for certain content types
    if (includedContentTypes.includes(listRows[i].ContentType)){

        //check to make sure this view shows the three fields we require
        var field1 = listRows[i].Field1;
        var field2= listRows[i].Field2;
        var field3 = listRows[i].Field3;
        var firstColumn = jQuery("table.ms-listviewtable tbody tr:nth-child(" + (i+1) + ") td:first-child");

        if (field1!= undefined && field2!= undefined && field3!= undefined){        

            //in some cases this code will actually run before the DOM has finished loading the required elements
            //we will just set a delay to try again until the elements are available on the page
            if (firstColumn.length == 0){
                setTimeout(setProtectedIcon, 200);
                return;
            }

            if (field1.Label != undefined && field1.Label != "0 - No Controls"){

                firstColumn.prepend('<i class="fas fa-key xxx-protected" title="This document contains protected content"></i>');
            }
            else if (field2.Label != undefined && field2.Label != "U - Unclassified"){

                firstColumn.prepend('<i class="fas fa-key xxx-protected" title="This document contains protected content"></i>');
            }
            else if (field3.length > 0){

                //field3 is the only one that allows multiple values so we need to loop through them
                for (var j = 0; j < field3.length; j++){

                    if (field3[j] != undefined && field3[j].Label != "0 - No Controls"){

                        firstColumn.prepend('<i class="fas fa-key xxx-protected" title="This document contains protected content"></i>');
                        break;
                    }
                }
            }
        }
        else{

            firstColumn.prepend('<i class="fas fa-exclamation-triangle xxx-protected-unknown" title="Not enough info to determine if document contains protected content"></i>');
        }
    }
}

}

I changed some of the field names and values to protect the innocent, so please excuse any typos as I assure you this code runs without issue when there is only one page of results.

** I have a bit of an update here. It seems that after the page has fully finished loading the results the ctx.ListData.Row has the correct objects in it. It seems that just while the page is loading, all of the list items go through the ctx.ListData object one page at a time (which is very strange to me, but whatever...).

I guess the real question here, is how can I reliably detect when the list is fully rendered so I can run the function above? I know if it put a setTimeout of 900 before the function is called it works as I expect so this entire issue is just a timing issue. I don't want to have to leave this as a setTimeout solution though as it will never be 100% reliable

Your Answer

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

Browse other questions tagged or ask your own question.