This project is read-only.

JS Function running before controls fully loaded

Jan 3, 2013 at 10:11 PM
Edited Jan 3, 2013 at 10:41 PM

Happy New Year!

I've been using your datagrid control for quite some time now so am fairly experienced with it, but have come across a problem I can't seem to resolve without the us of a setTimeout function.

I should stress that this problem only occurs on page load. It fires perfectly when a cell is changed.

I've added totals to the top of the grid and when the page loads I sum up all the items with a particular class and put that into the total span. The issue is that the SumAllTotals function seems to run before the Grid rows have been displayed/hidden, therefore it's duplicating the values (or if I select to only show visible rows then the totals are zero).

I have both a display and an edit template for each row and within each of these i have a class on the field i want to total. Notice the divimp class on the items below. The class is not present anywhere else.

DISPLAY TEMPLATE 

<input class="divtax" type="hidden" value="@Model.TaxAmount" />
<input type="hidden" class="divimp" value="@Model.ImputationAmount"/>

EDIT TEMPLATE

@Html.ValidationMessageFor(m => m.ImputationAmount)
    @Html.HiddenFor(model => Model.ImputationAmount)
    @Html.TextBoxFor(model => model.ImputationAmount, new { @readonly = "readonly", disabled = "disabled", @class = "divimp w80", id = @Html.PrefixedId(m => m.ImputationAmount) + "Hidden" })

When the grid initially loads it has both a Display template and an Edit template and they both have a style="display:none;" on them (see below). Then ( I'm guessing) your JS goes through and enables/disables the required template. 

<tr  id="CorporateActionAllocations___0___Item_Value_Edit_Container"  style="display: none;" >
<tr  id="CorporateActionAllocations___0___Item_Value_Display_Container"  style="display: none;" >

Because both templates are available the values are duplicated. I've tried locating my SumAllTotals function in each of the following places but it doesn't make a difference, it still sums too early. The only way it works is if I wait 100ms before running it.

1. Inside a document.ready function (we have multiple of these)
2. At the bottom of the view page in a javascript tag.
3. At the bottom of the view page in a 

$(window).load(function () {

4. At the bottom of the view page inside a window.load which is inside a document.ready

Do you have an event/flag which is raised once all the MVC COntrols Toolkit functions have finished firing on page load? I tried searching the documentation but couldn't find anything.

I should also point out that as part of the document.ready function I unbind the bind change event handlers to some of the cells within each row. 

BTW here are my javascript functions

function SetAllTotals() {
    SetDivTotal('divunits', 'divunitst');
    SetDivTotal('divdrp', 'divdrpt');
    SetDivTotal('divimp', 'divimpt');
}

function SetDivTotal(classname, totalspanId) {
    var divInternalTotal = 0;
    $('tr:visible .' + classname).each(function () {
        divInternalTotal = divInternalTotal + (1.0 * $(this).val());
    });
    $('#' + totalspanId).html(divInternalTotal);
}
And the code at the very bottom of my view page
<script type="text/javascript">
    SetAllTotals(); 
    $(window).load(function () {
        // this code will run after all other $(document).ready() scripts
        // have completely finished, AND all page elements are fully loaded.
        SetAllTotals(); 
    });
</script>

Coordinator
Jan 5, 2013 at 4:35 PM

The only solution is to use setTimeOut:

<script type="text/javascript">
    SetAllTotals(); 
    $(window).load(function () {
        // this code will run after all other $(document).ready() scripts
        // have completely finished, AND all page elements are fully loaded.
        setTimeOut(SetAllTotals); 
    });
</script>

Put this at the end of the page and you be sure your code will be executed after the grid is ready. The problem is that the grid must work properly also when returned by an ajax call, and in the ajax calls the ready event is not available, so the the javascript code itself must use setTimeout to be sure it is executed after the html has been loaded, so als you are forced to use setTimeOut to be sure your code is executed after the grid javascript code is executed.

No grid ready event is available because if yiu put some code here you are sure grid is ready, but not other controls are ready.

With the snippet above, instead, you are sure your code is executed after all Mvc Controls Toolkit controls are ready,

Jan 6, 2013 at 9:00 PM
Edited Jan 6, 2013 at 9:50 PM

Thank you for the timely response.

Little bit disappointed I have to use a setTimeout, but I understand the reasons why. In the end I've decided to design it a different way as using a setTimeout makes me feel like I'm violating something. 

I've decided to sum the totals on the server and then call a javascript function on the page to set the total span within the grid header template. The downside to this is that we have the code to total the allocations on both the server and within the javascript, but the code for that is relatively simple so I'm happy with that compromise.

 

Cheers

Coordinator
Jan 7, 2013 at 6:32 PM

No! setTimeOut is not a violation of anything! It is quite common to use it in javascript. It is quite tricky ONLY IF you put a time in it! However if you dont put any time in it, it you just says to javascript to execute your code AFTER the main thread is finished, that is, after the current javascript event has been processed. In other termis it just put a new event in the javascript event queue, to be sure the involved code is executed after the current evente has been processed. Putting something into an event queue is quite normal in an event based language like javascript.

Due to the way javascript events are processed in the various browsers, setTimeOut is the only solution some tasks can be accomplished. It is used in the implementation of the jQuery, and knockout.js libraries....so if you use them...you are probably using setTimeOut...without knowing it!

Obviously there is a performance penality associated to setTimeOut, so it can be used just in a limited way...and not, say on each element of an array of 1000 elements like:

for(var i=0; i< arr.length; i++) setTimeOut(function(){....arr[i]....});

May 14, 2013 at 4:19 AM
Hi There,

Just re-reading this whilst researching something else.
Thanks for the info re the setTimeout, I didn't realise it worked like that.