none
SharePoint Preview Pane customization RRS feed

  • Question

  • I have grouped link titles of a preview pane under categories since my title column is not unique. Looks like this and attaching the code. 

    I want to change the row names under the group by header to point to a different column (like Name). Ex.

    Group by CC:

    Name1

    Name2

    Name3

    Basically, the functionality of a preview pane should remain as it is but I just want the row names to point to a different column than group header.

    <style type="text/css">      
            .toggleButton {
                float: right;
            }
    
            .hiddenRow {
                display: none;
            }
        </style>
        <script type="text/javascript" src="https://code.jquery.com/jquery-1.12.4.js"></script>
        <script type="text/javascript">
            // Group table rows by first cell value. Assume first row is header row
            function groupByFirst(table) {
    
                // Add expand/collapse button
                function addButton(cell) {
                    var button = cell.appendChild(document.createElement('button'));
                    button.className = 'toggleButton';
                    //button.textContent = '+';
                    button.addEventListener('click', toggleHidden, false);
                    return button;
                }
    
                // Expand/collapse all rows below this one until next header reached
                function toggleHidden(evt) {
                    var target = evt.target || evt.srcElement;
                    if (target.textContent) {
                        if (target.textContent == '+') {
                            target.textContent = '-';
                        } else {
                            target.textContent = '+';
                        }
                    } else {
                        target.textContent = '+';
                    }
                    evt.preventDefault();
                    var row = this.parentNode.parentNode.nextSibling;
                    while (row && !row.classList.contains('groupHeader')) {
                        row.classList.toggle('hiddenRow');
                        row = row.nextSibling;
                    }
                }
    
                // Use tBody to avoid Safari bug (appends rows to table outside tbody)
                var tbody = table.tBodies[0];
    
                // Get rows as an array, exclude first row
                var rows = Array.from(tbody.rows);
    
                // Group rows in object using first cell value
                var groups = rows.reduce(function (groups, row) {
                    var val = $(row).find('.ms-vb').text();
                    if (!groups[val]) groups[val] = [];
    
                    groups[val].push(row);
                    return groups;
                }, Object.create(null));
    
                // Put rows in table with extra header row for each group
                Object.keys(groups).forEach(function (value, i) {
                    // Add header row
                    var row = tbody.insertRow();
                    row.className = 'groupHeader';
                    var cell = row.appendChild(document.createElement('td'));
                    cell.colSpan = groups[value][0].cells.length;
                    cell.appendChild(
                      document.createTextNode(
                        'Grouped by(' + value + ') ' + groups[value].length + ' hits'
                      )
                    );
                    var button = addButton(cell);
                    // Put the group's rows in tbody after header
                    groups[value].forEach(function (row) { tbody.appendChild(row) });
    
                    // Call listener to collapse group
                    button.click();
                });
            }
    
            $(document).ready(function () {            
                // Production steps of ECMA-262, Edition 6, 22.1.2.1
                if (!Array.from) {
                    Array.from = (function () {
                        var toStr = Object.prototype.toString;
                        var isCallable = function (fn) {
                            return typeof fn === 'function' || toStr.call(fn) === '[object Function]';
                        };
                        var toInteger = function (value) {
                            var number = Number(value);
                            if (isNaN(number)) { return 0; }
                            if (number === 0 || !isFinite(number)) { return number; }
                            return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number));
                        };
                        var maxSafeInteger = Math.pow(2, 53) - 1;
                        var toLength = function (value) {
                            var len = toInteger(value);
                            return Math.min(Math.max(len, 0), maxSafeInteger);
                        };
    
                        // The length property of the from method is 1.
                        return function from(arrayLike/*, mapFn, thisArg */) {
                            // 1. Let C be the this value.
                            var C = this;
    
                            // 2. Let items be ToObject(arrayLike).
                            var items = Object(arrayLike);
    
                            // 3. ReturnIfAbrupt(items).
                            if (arrayLike == null) {
                                throw new TypeError('Array.from requires an array-like object - not null or undefined');
                            }
    
                            // 4. If mapfn is undefined, then let mapping be false.
                            var mapFn = arguments.length > 1 ? arguments[1] : void undefined;
                            var T;
                            if (typeof mapFn !== 'undefined') {
                                // 5. else
                                // 5. a If IsCallable(mapfn) is false, throw a TypeError exception.
                                if (!isCallable(mapFn)) {
                                    throw new TypeError('Array.from: when provided, the second argument must be a function');
                                }
    
                                // 5. b. If thisArg was supplied, let T be thisArg; else let T be undefined.
                                if (arguments.length > 2) {
                                    T = arguments[2];
                                }
                            }
    
                            // 10. Let lenValue be Get(items, "length").
                            // 11. Let len be ToLength(lenValue).
                            var len = toLength(items.length);
    
                            // 13. If IsConstructor(C) is true, then
                            // 13. a. Let A be the result of calling the [[Construct]] internal method 
                            // of C with an argument list containing the single item len.
                            // 14. a. Else, Let A be ArrayCreate(len).
                            var A = isCallable(C) ? Object(new C(len)) : new Array(len);
    
                            // 16. Let k be 0.
                            var k = 0;
                            // 17. Repeat, while k < len… (also steps a - h)
                            var kValue;
                            while (k < len) {
                                kValue = items[k];
                                if (mapFn) {
                                    A[k] = typeof T === 'undefined' ? mapFn(kValue, k) : mapFn.call(T, kValue, k);
                                } else {
                                    A[k] = kValue;
                                }
                                k += 1;
                            }
                            // 18. Let putStatus be Put(A, "length", len, true).
                            A.length = len;
                            // 20. Return A.
                            return A;
                        };
                    }());
                }
                $('nobr:contains("Title"):eq(0)').closest('tr').hide();
    
                $("tr.ms-alternating>td").each(function (index, element) {
                    $(this).find('.ms-vb').html($(this).find('.ms-vb').find('a').text());
                });
    
                $("tr.ms-ppanerow>td").each(function (index, element) {
                    $(this).find('.ms-vb').html($(this).find('.ms-vb').find('a').text());
                });
                groupByFirst($('.ms-ppleft').find('table')[0]);
                
            });
        </script>



    • Edited by mikello Thursday, January 31, 2019 5:42 PM
    Thursday, January 31, 2019 4:35 PM

All replies

  • Hi,

    As preview panel display Title only, the workaround is getting list item data by JSOM/rest api and replace OOB Title.

    I just update the script to display the item ID, you need add the query logic for your requirement.

    https://docs.microsoft.com/en-us/previous-versions/office/sharepoint-visio/jj246019(v%3Doffice.15)

    <style type="text/css">      
        .toggleButton {
            float: right;
        }
    
        .hiddenRow {
            display: none;
        }
        </style>
        <script type="text/javascript" src="https://code.jquery.com/jquery-1.12.4.js"></script>
        <script type="text/javascript">
            // Group table rows by first cell value. Assume first row is header row
            function groupByFirst(table) {
    
                // Add expand/collapse button
                function addButton(cell) {
                    var button = cell.appendChild(document.createElement('button'));
                    button.className = 'toggleButton';
                    //button.textContent = '+';
                    button.addEventListener('click', toggleHidden, false);
                    return button;
                }
    
                // Expand/collapse all rows below this one until next header reached
                function toggleHidden(evt) {
                    var target = evt.target || evt.srcElement;
                    if (target.textContent) {
                        if (target.textContent == '+') {
                            target.textContent = '-';
                        } else {
                            target.textContent = '+';
                        }
                    } else {
                        target.textContent = '+';
                    }
                    evt.preventDefault();
                    var row = this.parentNode.parentNode.nextSibling;
                    while (row && !row.classList.contains('groupHeader')) {
                        row.classList.toggle('hiddenRow');
                        row = row.nextSibling;
                    }
                }
    
                // Use tBody to avoid Safari bug (appends rows to table outside tbody)
                var tbody = table.tBodies[0];
    
                // Get rows as an array, exclude first row
                var rows = Array.from(tbody.rows);
    
                // Group rows in object using first cell value
                var groups = rows.reduce(function (groups, row) {
                    var val = $(row).find('.ms-vb').text();
                    if (!groups[val]) groups[val] = [];
    
                    groups[val].push(row);
                    return groups;
                }, Object.create(null));
    
                // Put rows in table with extra header row for each group
                Object.keys(groups).forEach(function (value, i) {
                    // Add header row
                    var row = tbody.insertRow();
                    row.className = 'groupHeader';
                    var cell = row.appendChild(document.createElement('td'));
                    cell.colSpan = groups[value][0].cells.length;
                    cell.appendChild(
                      document.createTextNode(
                        'Grouped by(' + value + ') ' + groups[value].length + ' hits'
                      )
                    );
                    var button = addButton(cell);
                    // Put the group's rows in tbody after header
                    groups[value].forEach(function (row) { tbody.appendChild(row) });
    
                    // Call listener to collapse group
                    button.click();
                });
            }
    
        $(document).ready(function () {            
            // Production steps of ECMA-262, Edition 6, 22.1.2.1
            if (!Array.from) {
                Array.from = (function () {
                    var toStr = Object.prototype.toString;
                    var isCallable = function (fn) {
                        return typeof fn === 'function' || toStr.call(fn) === '[object Function]';
                    };
                    var toInteger = function (value) {
                        var number = Number(value);
                        if (isNaN(number)) { return 0; }
                        if (number === 0 || !isFinite(number)) { return number; }
                        return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number));
                    };
                    var maxSafeInteger = Math.pow(2, 53) - 1;
                    var toLength = function (value) {
                        var len = toInteger(value);
                        return Math.min(Math.max(len, 0), maxSafeInteger);
                    };
    
                    // The length property of the from method is 1.
                    return function from(arrayLike/*, mapFn, thisArg */) {
                        // 1. Let C be the this value.
                        var C = this;
    
                        // 2. Let items be ToObject(arrayLike).
                        var items = Object(arrayLike);
    
                        // 3. ReturnIfAbrupt(items).
                        if (arrayLike == null) {
                            throw new TypeError('Array.from requires an array-like object - not null or undefined');
                        }
    
                        // 4. If mapfn is undefined, then let mapping be false.
                        var mapFn = arguments.length > 1 ? arguments[1] : void undefined;
                        var T;
                        if (typeof mapFn !== 'undefined') {
                            // 5. else
                            // 5. a If IsCallable(mapfn) is false, throw a TypeError exception.
                            if (!isCallable(mapFn)) {
                                throw new TypeError('Array.from: when provided, the second argument must be a function');
                            }
    
                            // 5. b. If thisArg was supplied, let T be thisArg; else let T be undefined.
                            if (arguments.length > 2) {
                                T = arguments[2];
                            }
                        }
    
                        // 10. Let lenValue be Get(items, "length").
                        // 11. Let len be ToLength(lenValue).
                        var len = toLength(items.length);
    
                        // 13. If IsConstructor(C) is true, then
                        // 13. a. Let A be the result of calling the [[Construct]] internal method 
                        // of C with an argument list containing the single item len.
                        // 14. a. Else, Let A be ArrayCreate(len).
                        var A = isCallable(C) ? Object(new C(len)) : new Array(len);
    
                        // 16. Let k be 0.
                        var k = 0;
                        // 17. Repeat, while k < len… (also steps a - h)
                        var kValue;
                        while (k < len) {
                            kValue = items[k];
                            if (mapFn) {
                                A[k] = typeof T === 'undefined' ? mapFn(kValue, k) : mapFn.call(T, kValue, k);
                            } else {
                                A[k] = kValue;
                            }
                            k += 1;
                        }
                        // 18. Let putStatus be Put(A, "length", len, true).
                        A.length = len;
                        // 20. Return A.
                        return A;
                    };
                }());
            }
            $('nobr:contains("Title"):eq(0)').closest('tr').hide();
    
            $("tr.ms-alternating>td").each(function (index, element) {
                $(this).find('.ms-vb').html($(this).find('.ms-vb').find('a').text());
            });
    
            $("tr.ms-ppanerow>td").each(function (index, element) {
                $(this).find('.ms-vb').html($(this).find('.ms-vb').find('a').text());
            });
            groupByFirst($('.ms-ppleft').find('table')[0]);
            $("tr:not('[class^=groupHeader]')", 'div.ms-ppleft').each(function () {
                var itemID = $(this).find('div[field="LinkTitle"]').attr('id');
                $(this).find('div[field="LinkTitle"]').html(itemID);
                //to do, get list item data and replace OOB title
            })
                
        });
        </script>


    Best Regards,

    Lee


    Please remember to mark the replies as answers if they helped. If you have feedback for TechNet Subscriber Support, contact tnmff@microsoft.com.

    SharePoint Server 2019 has been released, you can click here to download it.
    Click here to learn new features. Visit the dedicated forum to share, explore and talk to experts about SharePoint Server 2019.

    Friday, February 1, 2019 9:10 AM
  • Hi, I am trying to replace with another field name like "Program_x0020_Name". It is not working with .attr('Program_x0020_Name');

    Could you please help me with this


    • Edited by mikello Monday, February 4, 2019 9:35 PM
    Monday, February 4, 2019 4:54 PM
  • Hi,

    Sample script for your reference.

    <style type="text/css">
            .toggleButton {
                float: right;
            }
    
            .hiddenRow {
                display: none;
            }
        </style>
        <script type="text/javascript" src="https://code.jquery.com/jquery-1.12.4.js"></script>
        <script type="text/javascript">
            // Group table rows by first cell value. Assume first row is header row
            function groupByFirst(table) {
    
                // Add expand/collapse button
                function addButton(cell) {
                    var button = cell.appendChild(document.createElement('button'));
                    button.className = 'toggleButton';
                    //button.textContent = '+';
                    button.addEventListener('click', toggleHidden, false);
                    return button;
                }
    
                // Expand/collapse all rows below this one until next header reached
                function toggleHidden(evt) {
                    var target = evt.target || evt.srcElement;
                    if (target.textContent) {
                        if (target.textContent == '+') {
                            target.textContent = '-';
                        } else {
                            target.textContent = '+';
                        }
                    } else {
                        target.textContent = '+';
                    }
                    evt.preventDefault();
                    var row = this.parentNode.parentNode.nextSibling;
                    while (row && !row.classList.contains('groupHeader')) {
                        row.classList.toggle('hiddenRow');
                        row = row.nextSibling;
                    }
                }
    
                // Use tBody to avoid Safari bug (appends rows to table outside tbody)
                var tbody = table.tBodies[0];
    
                // Get rows as an array, exclude first row
                var rows = Array.from(tbody.rows);
    
                // Group rows in object using first cell value
                var groups = rows.reduce(function (groups, row) {
                    var val = $(row).find('.ms-vb').text();
                    if (!groups[val]) groups[val] = [];
    
                    groups[val].push(row);
                    return groups;
                }, Object.create(null));
    
                // Put rows in table with extra header row for each group
                Object.keys(groups).forEach(function (value, i) {
                    // Add header row
                    var row = tbody.insertRow();
                    row.className = 'groupHeader';
                    var cell = row.appendChild(document.createElement('td'));
                    cell.colSpan = groups[value][0].cells.length;
                    cell.appendChild(
                      document.createTextNode(
                        'Grouped by(' + value + ') ' + groups[value].length + ' hits'
                      )
                    );
                    var button = addButton(cell);
                    // Put the group's rows in tbody after header
                    groups[value].forEach(function (row) { tbody.appendChild(row) });
    
                    // Call listener to collapse group
                    button.click();
                });
            }
    
            ExecuteOrDelayUntilScriptLoaded(custom, "sp.js");
            function custom() {
                // Production steps of ECMA-262, Edition 6, 22.1.2.1
                if (!Array.from) {
                    Array.from = (function () {
                        var toStr = Object.prototype.toString;
                        var isCallable = function (fn) {
                            return typeof fn === 'function' || toStr.call(fn) === '[object Function]';
                        };
                        var toInteger = function (value) {
                            var number = Number(value);
                            if (isNaN(number)) { return 0; }
                            if (number === 0 || !isFinite(number)) { return number; }
                            return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number));
                        };
                        var maxSafeInteger = Math.pow(2, 53) - 1;
                        var toLength = function (value) {
                            var len = toInteger(value);
                            return Math.min(Math.max(len, 0), maxSafeInteger);
                        };
    
                        // The length property of the from method is 1.
                        return function from(arrayLike/*, mapFn, thisArg */) {
                            // 1. Let C be the this value.
                            var C = this;
    
                            // 2. Let items be ToObject(arrayLike).
                            var items = Object(arrayLike);
    
                            // 3. ReturnIfAbrupt(items).
                            if (arrayLike == null) {
                                throw new TypeError('Array.from requires an array-like object - not null or undefined');
                            }
    
                            // 4. If mapfn is undefined, then let mapping be false.
                            var mapFn = arguments.length > 1 ? arguments[1] : void undefined;
                            var T;
                            if (typeof mapFn !== 'undefined') {
                                // 5. else
                                // 5. a If IsCallable(mapfn) is false, throw a TypeError exception.
                                if (!isCallable(mapFn)) {
                                    throw new TypeError('Array.from: when provided, the second argument must be a function');
                                }
    
                                // 5. b. If thisArg was supplied, let T be thisArg; else let T be undefined.
                                if (arguments.length > 2) {
                                    T = arguments[2];
                                }
                            }
    
                            // 10. Let lenValue be Get(items, "length").
                            // 11. Let len be ToLength(lenValue).
                            var len = toLength(items.length);
    
                            // 13. If IsConstructor(C) is true, then
                            // 13. a. Let A be the result of calling the [[Construct]] internal method
                            // of C with an argument list containing the single item len.
                            // 14. a. Else, Let A be ArrayCreate(len).
                            var A = isCallable(C) ? Object(new C(len)) : new Array(len);
    
                            // 16. Let k be 0.
                            var k = 0;
                            // 17. Repeat, while k < len… (also steps a - h)
                            var kValue;
                            while (k < len) {
                                kValue = items[k];
                                if (mapFn) {
                                    A[k] = typeof T === 'undefined' ? mapFn(kValue, k) : mapFn.call(T, kValue, k);
                                } else {
                                    A[k] = kValue;
                                }
                                k += 1;
                            }
                            // 18. Let putStatus be Put(A, "length", len, true).
                            A.length = len;
                            // 20. Return A.
                            return A;
                        };
                    }());
                }
                $('nobr:contains("Title"):eq(0)').closest('tr').hide();
    
                $("tr.ms-alternating>td").each(function (index, element) {
                    $(this).find('.ms-vb').html($(this).find('.ms-vb').find('a').text());
                });
    
                $("tr.ms-ppanerow>td").each(function (index, element) {
                    $(this).find('.ms-vb').html($(this).find('.ms-vb').find('a').text());
                });
                groupByFirst($('.ms-ppleft').find('table')[0]);
    
                var ctx = new SP.ClientContext.get_current();
                var oList = ctx.get_web().get_lists().getById(_spPageContextInfo.pageListId);
                var items = [];
                $("tr:not('[class^=groupHeader]')", 'div.ms-ppleft').each(function () {
                    var itemID = parseInt($(this).find('div[field="LinkTitle"]').attr('id'));
                    var listItem = oList.getItemById(itemID);
                    ctx.load(listItem);
                    items.push(listItem);
                })
                ctx.executeQueryAsync(
                function () {
                    $("tr:not('[class^=groupHeader]')", 'div.ms-ppleft').each(function () {
                        var itemID = parseInt($(this).find('div[field="LinkTitle"]').attr('id'));
                        for (var i = 0; i < items.length; i++) {
                            if (items[i].get_item("ID") == itemID)
                                $(this).find('div[field="LinkTitle"]').html(items[i].get_item("Program_x0020_Name"));
                        }
                    })
                },
                function () {
                    //error
                }
               );
            }
        </script>

    Best Regards,

    Lee


    Please remember to mark the replies as answers if they helped. If you have feedback for TechNet Subscriber Support, contact tnmff@microsoft.com.

    SharePoint Server 2019 has been released, you can click here to download it.
    Click here to learn new features. Visit the dedicated forum to share, explore and talk to experts about SharePoint Server 2019.

    Tuesday, February 5, 2019 2:53 AM
  • I tried this code and it is not working. It still shows only Titles under group instead of Program Name. Just to be clear, the name of my column is "Program Name" but the xsl field name is "Program_x0020_Name". It is a Single line text field. 

    Your code was working with ID but it is not working with field name....




    • Edited by mikello Tuesday, February 5, 2019 4:27 PM
    Tuesday, February 5, 2019 4:08 PM
  • Hi,

    You could use developer tool to debug the script if any error, as I had tested the script in my local.

    Best Regards,

    Lee


    Please remember to mark the replies as answers if they helped. If you have feedback for TechNet Subscriber Support, contact tnmff@microsoft.com.

    SharePoint Server 2019 has been released, you can click here to download it.
    Click here to learn new features. Visit the dedicated forum to share, explore and talk to experts about SharePoint Server 2019.

    Friday, February 8, 2019 7:32 AM
  • Since I wasn't able to make it work, I achieved it through this post

    Another way to achieve this

    More manual than I would've liked but it works...


    • Edited by mikello Monday, February 11, 2019 6:06 PM
    Monday, February 11, 2019 6:06 PM
  • Hi,

    You could mark working solution as answer it so it would help other community members find the helpful information if they searched this thread.

    It would be better for you to learn debugging JavaScript by developer tool, it’s a powerful tool to solve JavaScript/jQuery issue.

    https://msdn.microsoft.com/en-us/library/hh968260%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396

    Best Regards,

    Lee


    Please remember to mark the replies as answers if they helped. If you have feedback for TechNet Subscriber Support, contact tnmff@microsoft.com.

    SharePoint Server 2019 has been released, you can click here to download it.
    Click here to learn new features. Visit the dedicated forum to share, explore and talk to experts about SharePoint Server 2019.

    Tuesday, February 12, 2019 1:05 AM