rendered called before DOM completion - meteor blaze

2,564 views
Skip to first unread message

Aashu Aggarwal

unread,
Mar 28, 2014, 10:30:25 AM3/28/14
to meteo...@googlegroups.com
I am creating a dynamic form using json and trying to use jquery-validation plugin to add validation rules to input fields.

Json structure and helper method is :

    var fields = [{
        label: "Name",
        type: {name: 'STRING'},
        validationRules: {
            required: true,
            maxlength: 100,
            minlength: 3
        }
    },{
        label: "Currency",
        type: {name: 'CHECKBOX'},
        defaultValue: ['USD', 'INR'],
        validationRules: {
            required: true
        },
        sourceCollection: 'oNLFfi4L3zgNLhScv',
    }] ;

    Template.eventCreate.helpers({
        fields: function(){
            console.log("calling fields");
            fields.forEach(function(field, index){
                field.sourceCollectionData = StaticLists.find({_id: field.sourceCollection});
            });
            return fields;
        }
    });


Template looks like:

    <template name="eventCreate">
        <form id="newEventForm" class="form-horizontal">
            {{#each fields}}
                <div class="form-group">
                    <label class="col-xs-2 control-label">{{label}}</label>
                    <div class="col-xs-6">
                        {{#if equals type.name 'STRING'}}
                            <input name="{{label}}" id="{{label}}" class="form-control" placeholder="Enter {{label}}" value="{{defaultValue}}" />
                        {{/if}}
                        {{#if equals type.name 'CHECKBOX'}}
                            {{#each sourceCollectionData}}
                                {{#if isActive}}
                                    <div class="col-xs-2 checkbox">
                                        <label class="checkbox-inline">
                                            <input type="checkbox" name="{{../label}}" id="{{../label}}" value="{{name}}" {{checked ../defaultValue}}> {{name}}
                                        </label>
                                    </div> 
                                {{/if}}                               
                            {{/each}}                         
                        {{/if}}
                    </div>               
                </div>
            {{/each}}
        </form>
    </template>

Now I am trying to add validation rule in rendered method:

    Template.eventCreate.rendered = function(){
        $('#newEventForm').validate({
           ....
        });

        fields.forEach(function(field, index){
            if(field.validationRules){
                $('#'+field.label).rules('add', field.validationRules);
            }
        });
    }

It works in case of input text but throws exception for checkbox because checkbox DOM is still not laid out and there is no element with id "Currency".

I assume in meteor blaze rendered is called once only when DOM rendering is complete. Though it is called once here but before DOM rendering completes

Aashu Aggarwal

unread,
Mar 28, 2014, 10:32:18 AM3/28/14
to meteo...@googlegroups.com
Just to mention that same thing happens with other input fields like radio button and dropdowns which are rendered inside an each block. Those fields also get rendered after rendered callback.

Gadi Cohen

unread,
Mar 28, 2014, 12:26:36 PM3/28/14
to meteo...@googlegroups.com
Probably the template is first created before any data exists.  Don't forget, render() won't run ever again after it's been run once, even if new data were to be added to the cursor passed to {{#each}} in your example above.

Try this pattern here:

You'll have to adapt your code, but I think it's clear how... will be much faster too.

David Greenspan

unread,
Mar 28, 2014, 1:05:45 PM3/28/14
to meteo...@googlegroups.com
Yes, Gadi is right.

Basically, if you wrap the contents of the #each in a template, then you will get a callback that corresponds to when that particular item is rendered.  Meteor handles #each specially, for efficiency, and will add and remove individual items separately from the surrounding template.  The initial contents of the list are rendered inline, but when data hasn't loaded, the initial contents may be empty.

-- David



--
You received this message because you are subscribed to the Google Groups "meteor-talk" group.
To unsubscribe from this group and stop receiving emails from it, send an email to meteor-talk...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Aashu Aggarwal

unread,
Mar 28, 2014, 2:20:04 PM3/28/14
to meteo...@googlegroups.com
Ok let me put create a separate template for each block and see if that fixes the issue.

Gadi Cohen

unread,
Mar 29, 2014, 1:38:41 AM3/29/14
to meteo...@googlegroups.com
For performance, you might also consider checking specifically for the newly added <input> in your rules (since with it's own template, each one will call render() as it's added to the DOM), as opposed to the other way around - i.e. processing and reprocessing every input after every add.  You can also use this.$(), which searches just inside the template instance as opposed to through the entire DOM.  I think you'll notice a nice difference (depending on how big your form is).

Aashu Aggarwal

unread,
Mar 29, 2014, 9:44:39 AM3/29/14
to meteo...@googlegroups.com
Yesterday I created a separate template for DOM inside each block and rendered callback was invoked correctly. So does that mean with each block we should create a separate template and include it in parent template?

And I was wondering how shall i only process the elements of the current template. Though right now form is not too big but still this should help. Thanks.


--
You received this message because you are subscribed to a topic in the Google Groups "meteor-talk" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/meteor-talk/47Orrrz7kjg/unsubscribe.
To unsubscribe from this group and all its topics, send an email to meteor-talk...@googlegroups.com.

Gadi Cohen

unread,
Mar 30, 2014, 9:50:35 AM3/30/14
to meteo...@googlegroups.com
Hey, hope I understood correctly.  Umm, you don't have to always work like that, although it's often cleaner, and yes, it is required (for efficiency) if you want some code to run every time a new block (inside #each) is rendered.  That way, you can target the code to run only on the elements that have just been added to the DOM.  So, connected to your next question, yes, if you use this.$(), it will only search the DOM elements in that specific template instance, in this case, the most recent input element created.

So instead of your current code which searches the whole DOM for each 'field', you could do:

Template.eventCreateField.rendered = function() {
  if (this.validationRules)
   this.$('input').rules('add', this.validationRules);
}

Does that make sense?  `eventCreateField` would be your inner template.  There'll only be one `input` element inside each instance, and the validationRules are already in the data context for that template instance.  So, you're cutting away a lot of unnecessary work... this is the power of Meteor :)

Aashu Aggarwal

unread,
Mar 30, 2014, 10:49:44 PM3/30/14
to meteo...@googlegroups.com
Thats correct Gadi. Since I am still learning meteor, its a good point to keep in mind.

Many thanks.

Aashu Aggarwal

unread,
Apr 1, 2014, 1:07:10 PM4/1/14
to meteo...@googlegroups.com
Hi,

I have a different issue now with rendering. Earlier I was hard coding the JSON object in js file itself and on render callback adding validations to the input fields.

Now I am retrieving the object from Mongo, something like this:

Template.eventCreate.helpers({
    fields: function(){
        var template = EventTemplate.findOne({name: 'Job Template'});
        console.log("template", template);
        if(template){
            Session.set("template", template);
            template.fields.forEach(function(field, index){
               field.sourceCollectionData = StaticLists.find({_id: field.sourceCollection});
            });
            return template.fields;
        }        
    }
});

Template.eventCreate.rendered = function(){
    $('#newEventForm').validate({
        ...
        ...
    console.log("rendering main template");

   addValidationRules(Session.get('template'));
}

Now the console output is something like:

template
Object {_id"iFDndmjavtFN8AdGQ"name"Job Template"description"Job Template"fieldsArray[13]}

which shows that (and I tried it even with a break point in js script) that just when script loads template is undefined and no input field is rendered on front end but rendered callback is invoked. Now later when template gets populated with data, input fields are rendered but callback is not invoked again.

Am I doing anything wrong here?

Matt McClard

unread,
Apr 7, 2014, 9:54:35 PM4/7/14
to meteo...@googlegroups.com
I am having the exact same issue as above if anyone has any ideas on how to solve this that would be awesome!

Richard Fielding

unread,
Apr 19, 2014, 6:56:46 AM4/19/14
to meteo...@googlegroups.com
Also having the same problem here ...

Basically since upgrading to Blaze all my typeahead plugins are broken, since they all use data pulled from subscriptions. I'm using iron-router and I've verified that template.rendered is getting called before iron-router's waitOn, which is getting called before iron-router's render function... which is bizarre...

Template.myTemplate.rendered = function(){
    var possible_names = MyCollection.find().map(function(doc){
        return doc.name;
    });
    // possible_names is always empty
    $('input').typeahead({
        source : possible_names
    });

Aaron Curtis

unread,
May 19, 2014, 8:40:36 PM5/19/14
to meteo...@googlegroups.com
Me too :-(

Dan Dascalescu

unread,
Dec 9, 2014, 10:36:40 PM12/9/14
to meteo...@googlegroups.com
On Saturday, April 19, 2014 3:56:46 AM UTC-7, Richard Fielding wrote:
Basically since upgrading to Blaze all my typeahead plugins are broken

Native Meteor widgets are best. Instead of Typeahead, have you considered autocomplete
Reply all
Reply to author
Forward
0 new messages