Bug: onRendered executes too early


#21

absolutely correct, however that subscription should be stored on the template instance so that it can be referenced there.

You’ll notice that I did NOT subscribe there, I only checked if the important sub was ready. In short, onRendered is responsible for handling “did it render” but has no care about how to actually subscribe.


#22

Hellstad is correct.

For example, we use Materialize in our project. Materialize uses many different functions to update via JS so you have to use it in onRendered functions.

As an easy to use solution, what I did was made a template called “materializeLoader”. This contains all the js required to update the page correctly.

Then, on any page using the elements which require JS, I can simply throw {{materializeLoader}} in to it, and bam, good to go! The loader will run AFTER everything else on your page.

In situations where your page is iterating through data, such as in #each, simply throw the loader inside the #each template, and you will have it updated each time the data is updated.


#23

I noticed your using Materailize and having issues, I’m about to leave the office now, but please try the solution I described, and if it does not work, let me know/send me a messsage and I will help you out more tomorrow!


#24

But my template don’t have sub.ready().


#25

You missed the critical part of the ensuing conversation.

You need to attach the critical subs (the one providing the data which your dom is based on) to the template instance. This means you need to subscribe at the template level (usually in onCreated) and that needs to be assigned to the template instance (this when in onRendered, onCreated, onDestroyed).

The point of that code is that templates which have conditional render (if/each) need the data present. If you are expecting the dom to exist before the data is there, you are going to be disappointed.

The entire point of this thread (since it’s beginning long ago) has been that people find onRendered executes before their critical piece of dom exists. There is no callback for re-render but there is tracker, so use the tools available.


#26

timeout is working however, that’s not a good solution, the data of dropdown is from database so it’s extendable… now if we are going to use the timeout we still need to calculate first how big is the data that needs to be rendered first before the materialize. Our basic number of dropdown fields on the screen are 84 then each group is extendable via add more buttons.


#27

Is this one of the solution? I tried it but I’ve got this error on browser console.

Exception from Tracker afterFlush function:
meteor.js?hash=e3f53db…:930 TypeError: Cannot read property ‘ready’ of undefined


#28

This was meant for testing purposes. The reason we use _.defer above has to do with letting the DOM render. If you are using a rendering engine, then depending on integration and who has thread priority your onRendered may be triggered BEFORE it is rendered (in our case, we call the appropriate Semantic libraries from _.defer in onRendered so we are not facing this, and at this point the DOM is properly rendered). I am just trying to help debug with a timeout - but not in production.

If you have many elements to render, then I am assuming you properly created a component / template partial for that type of element, and each onRendered will take care of its element’s rendering (this is precisely what we do and _.defer has always worked).

      {{#each field in fields}}
           {{> Dropdown field=field}}
      {{/each}}

#29

Hi,

I have my own constructor of fields to make it more easily to build up / design. I have no problem rendering the field on the UI page, the problem is that… after the template was rendered all the fields, the materialize version of dropdown fields are not displaying even I put this code ‘$(’.dropdown-button’).dropdown()’ on onRendered method. Here’s the dropdown link of materialize http://materializecss.com/dropdown.html. Maybe the materialize can’t cater these numbers of fields? Or What? But so far it works when I limit the timeout of dropdown converter to 5 second… and that’s funny… the system user will punch me for sure if they have to wait that long…

Here’s how I rendered my fields builder {{{ _theFields }}}

Below is how the fields are being set.

 _f.push({
            type: "group",
            gStart: '<div class="card tplcert-item" _ind="0"><div class="card-content">', //own html elements
            gEnd: "", //own html elements
            flds: []
        });
        
        _f.push({
            type: "group",
            gStart: '', //own html elements
            gEnd: "", //own html elements
            flds: [
                {
                   type: "hidden",
                   label: "Comment",
                   name: "templates.certificate.0._id"
                },
                 {
                   type: "select",
                   label: "Country",
                   name: "templates.certificate.0.country",
                   class: "country",
                   conClass: "s4",
                   option_attrb: {text: 'name', value: 'name'},
                   option_start: {text: '', value: ""},
                   options: countries
                },
                
                {
                   type: "select",
                   label: "State",
                   name: "templates.certificate.0.state",
                   class: "state",
                   conClass: "s4",
                   option_attrb: {text: 'name', value: 'name'},
                   option_start: {text: '', value: ""},
                   options: (_curr_data) ?  getStatesByCountry(_curr_data.templates.certificate[0]['country']) : []
                },
                
                {
                   type: "select",
                   label: "Amount Type",
                   name: "templates.certificate.0.amount_type",
                   conClass: "s4",
                   option_start: {text: '', value: ""},
                   options: ['Any Amount', 'Above', 'Below', 'Betweem']
                }
            ]
        });
        
        _f.push({
            type: "group",
            gStart: "", //own html elements
            gEnd: "", //own html elements
            flds: [
                 {
                   type: "select",
                   label: "Claim Type",
                   name: "templates.certificate.0.claim_type",
                   conClass: "s4",
                   option_start: {text: '', value: ""},
                   option_attrb: {text: 'name', value: 'name'},
                   options: claim_type_list,
                   required: true
                },
                
                {
                   type: "select",
                   label: "Incident Type",
                   name: "templates.certificate.0.incident_type",
                   conClass: "s4",
                   option_start: {text: '', value: ""},
                   option_attrb: {text: 'name', value: 'name'},
                   options: incident_list,
                   required: true
                },
                
                {
                   type: "select",
                   label: "Template File",
                   name: "templates.certificate.0.template",
                   conClass: "s4",
                   option_start: {text: '', value: ""},
                   options: ['10', '15'],
                   required: true
                }
            ]
        });
        
        _f.push({
            type: "div",
            text: '<a href="#" class="removeTplBtn" key_ind="certificate" _target=".tplcert-item" style="color:red;">- Remove</a>'
        });
        
        _f.push({
            type: "group",
            gStart: '', //own html elements
            gEnd: "</div></div>", //own html elements
            flds: []
        });
        
        //----------------BEGIN-------------------//
        if(_curr_data && _curr_data.templates.certificate.length > 1){
            for(var _i = 1; _i <= (_curr_data.templates.certificate.length - 1); _i++){
                _f.push({
                    type: "group",
                    gStart: '<div class="card tplcert-item" _ind="'+ _i +'"><div class="card-content">', //own html elements
                    gEnd: "", //own html elements
                    flds: []
                });
                
                _f.push({
                    type: "group",
                    gStart: '', //own html elements
                    gEnd: "", //own html elements
                    flds: [
                        {
                           type: "hidden",
                           label: "Comment",
                           name: "templates.certificate."+ _i +"._id"
                        },
                         {
                           type: "select",
                           label: "Country",
                           name: "templates.certificate."+ _i +".country",
                           class: "country",
                           conClass: "s4",
                           option_attrb: {text: 'name', value: 'name'},
                           option_start: {text: '', value: ""},
                           options: countries
                        },
                        
                        {
                           type: "select",
                           label: "State",
                           name: "templates.certificate."+ _i +".state",
                           class: "state",
                           conClass: "s4",
                           option_attrb: {text: 'name', value: 'name'},
                           option_start: {text: '', value: ""},
                           options: (_curr_data) ?  getStatesByCountry(_curr_data.templates.certificate[_i]['country']) : []
                        },
                        
                        {
                           type: "select",
                           label: "Amount Type",
                           name: "templates.certificate."+ _i +".amount_type",
                           conClass: "s4",
                           option_start: {text: '', value: ""},
                           options: ['Any Amount', 'Above', 'Below', 'Betweem']
                        }
                    ]
                });
                
                _f.push({
                    type: "group",
                    gStart: "", //own html elements
                    gEnd: "", //own html elements
                    flds: [
                         {
                           type: "select",
                           label: "Claim Type",
                           name: "templates.certificate."+ _i +".claim_type",
                           conClass: "s4",
                           option_start: {text: '', value: ""},
                           option_attrb: {text: 'name', value: 'name'},
                           options: claim_type_list,
                           required: true
                        },
                        
                        {
                           type: "select",
                           label: "Incident Type",
                           name: "templates.certificate."+ _i +".incident_type",
                           conClass: "s4",
                           option_start: {text: '', value: ""},
                           option_attrb: {text: 'name', value: 'name'},
                           options: incident_list,
                           required: true
                        },
                        
                        {
                           type: "select",
                           label: "Template File",
                           name: "templates.certificate."+ _i +".template",
                           conClass: "s4",
                           option_start: {text: '', value: ""},
                           options: ['10', '15'],
                           required: true
                        }
                    ]
                });
                
                _f.push({
                    type: "div",
                    text: '<a href="#" class="removeTplBtn" key_ind="certificate" _target=".tplcert-item" style="color:red;">- Remove</a>'
                });
                
                _f.push({
                    type: "group",
                    gStart: '', //own html elements
                    gEnd: "</div></div>", //own html elements
                    flds: []
                });
            }
        }
        //----------------END-------------------//
        
        _f.push({
            type: "div",
            text: '<a href="#" class="moreTplBtn" _target=".tplcert-item" style="float:right;">+ Add More</a>'
        });

Is there any possibility for the meteor to wait first all functions that is being executed before rendering the template? Because since materialize works at 5 seconds for at least 84 fields, maybe the issue is because of sometimes the meteor reloaded the helpers every time there’s a script that touches the collection. As you can see on the fields designer that’s all dropdown with options from collections i.e Country, State, and others. So I’m not sure what is the problem.

For some suggestion to put a timeout my client dosn’t what it… because you have to calculate first for each dropdown how many data needs to loop as options before you’ll get the real estimated time.

Any suggestion and possible fixes are welcome, I tried all suggestions above but no luck yet.

Thanks!


#30

Template.test.onRendered( () => {
$(Template).ready( () => {
// Excute your code
}
}
// Thanks


#31

I recently had to deal with this problem. The most strictly general and correct solution is to use the MutationObserver API. However, IMO this API is too low-level: the simple task of waiting for the key element to be added requires a lot of boilerplate code. I suppose this could/should abstracted into a module or class.

Instead, I took the lazy way out and used setInterval, checking every 10ms for the presence of the element, giving up after 5sec. IMO this is not too hard on performance. It’s not ideal, but it is a general solution that does not depend on how you populate the page.

Btw, none of this addresses the possibility of a jarring UX, because the page might scroll rather abruptly/oddly in the middle of the loading process.

Waiting for the state “subscriptions-are-ready” is not a general solution because, for example, the template might fetch data using remote methods, or in other non-subscription ways.