1

I have the JSLink function below which I copied about 99.9%. But when I run it for my List, I get the following error. It doesn't seem to know the what the listItem is. Could someone please explain what is wrong. This should have bee a simple function to do.

TypeError: Unable to get property 'style' of undefined or null referenceTypeError

(function () {

    var overrideCtx = {};
    overrideCtx.Templates = {};
    overrideCtx.Templates.Fields = {
        'ContractdaysLeft': {
            'View' : HighlightRowOverride,
            'NewForm' : HighlightRowOverride,
            'EditForm' : HighlightRowOverride,
        }
    };

    SPClientTemplates.TemplateManager.RegisterTemplateOverrides(overrideCtx);

})();

function HighlightRowOverride(inCtx) {

    for (var i = 0; i < inCtx.ListData.Row.length; ++i)
    {   
        var listItem = inCtx.ListData.Row[i];

        var iid = GenerateIIDForListItem(inCtx, listItem);

        var row = document.getElementById(iid);

        if (listItem.ContractdaysLeft < 90)
        {
            row.style.backgroundColor = "#FFF1AD";          
        }
    }
}
  • Try adding a check if a row is null: if (row != null) { if (listItem.ContractdaysLeft < 90) { row.style.backgroundColor = FFF1AD"; } – Paul Strupeikis Feb 25 '16 at 14:30
3

Ok, there are several things happening here. First of all the error you are getting is "unable to get property 'style'". You are not trying to access a property style on listItem, you are trying to access the property style on row, so it is probably recognizing the listItem, what it can't find is the row.

Secondly, what you need to understand is that you have attached the override to a field, which means it is going to run once for every single list item, but then in the HighlightRowOverride function you again loop through all the rows/list items. Even though the data for all rows might be there in ctx.ListData, not all rows have necessarily been rendered yet, so there is no element with the ID you are looking for in document.getElementById.

For instance, let's say SharePoint is rendering the very first list item. It hits the ContractDaysLeft field, sees that there is a rendering override, and executes your code. First thing in your code, you loop through all the rows. (Remember at this point SharePoint is still rendering just the very first list item.) So first time through your loop, maybe it finds the row for the first list item. But second time through your loop, you are trying to get the row for the second list item, but SharePoint hasn't even finished rendering the first list item yet! So, of course it won't find the row.

I think what you want is for your function to run after all rows have been rendered, so they are there on the page to be found when you call document.GetElementById. So what you want to do is register your function with OnPostRender:

(function () {

    var overrideCtx = {};
    overrideCtx.Templates = {};
    overrideCtx.Templates.OnPostRender = HighlightRowOverride;

    SPClientTemplates.TemplateManager.RegisterTemplateOverrides(overrideCtx);

})();
1

+1 for Dylans answer, you have to put your code in the OnPostRender part

You CAN do it IN the FieldOverride, but:

  • you have to wait for the DOM element to exist (that is where you got the occasional error)
  • standard SharePoint _spYield function can be used to delay execution (setTimeout is used under the hood)
  • You have to understand inCtx is always a pointer to the global ctx (javascript always passes objects as pointer!)
  • So you can not use any ctx stuff inside the Yield (because x cycles later ctx already has other Item content)
  • the yield 20 is an educated guess on when the DOM should be ready; (those are not milliseconds!, it does 20 to 0 setTimeout loops)

        var iid = GenerateIID(inCtx);
        var daysleft = 80;
        _spYield(function(){
            var row = document.getElementById(iid);
            if (daysleft < 90){
                row.style.backgroundColor = "#FFF1AD";          
            }
        },20);
    

With the new iCSR library (https://iCSR.github.io) you don't have to write a function at all

If you declare the View as:

'ContractdaysLeft':{
  View : iCSR.DueDate({
                        ranges:'[msRed],0,#FFF1AD,90,[msGreen]'
                        ,rowcolor:true
                        })
}

You get this:

  • msRed and msGreen are iCSR tokens matching the colors from the new Microsoft Planner
  • iCSR adjusts the textcolor contrast for you (on each cell, you can't set a textcolor on a TR row)
  • and more

Note

  • iCSR does not use _spYield to wait for the TR DOM to exist. It uses a hidden image with an onload event. Because that is the failsave way of knowing a DOM element exists, and executes the moment it is available.

iCSR

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.