Troubleshooting a template

Hello,

I have installed the following packages
jasny:bootstrap
jesperwe:bootstrap-select

Everything works smooth, and well… I do get that bootstrap’s CSS layout for my page…

if I create a template named

<template name="tempName">

   <select class="selectpicker" data-live-search="true">
  <option>Hot Dog, Fries and a Soda</option>
  <option>Burger, Shake and a Smile</option>
  <option>Sugar, Spice and all things nice</option>
</select>

Add it to the body and add this to the main.js (client)

Template.tempName.onRendered(function() {
   this.$('.selectpicker').selectpicker();
});

Everything works flawlessly, however… If I add this to an existing template

<template name="triphtml">
{{#if currentUser}}
{{#if showAdd}}
  <form class="add-trip">
  <h2 class="title-text"> Add Trip </h2>

 <select class="selectpicker" data-live-search="true">
  <option>Hot Dog, Fries and a Soda</option>
  <option>Burger, Shake and a Smile</option>
  <option>Sugar, Spice and all things nice</option>
</select>

</form>
{{/if} {{/if}
</template>
Template.triphtml.onRendered(function() {
	console.log("Page Loaded");
  this.$('.selectpicker').selectpicker();
  
});

I see everything expect for the select I just added. If I change the class from selectpicker to anything else, I do get the default select shown.
What’s the issue?
PS. I do get Page loaded message too.

Update: In my existing template I have

{{#if currentUser}}

{{#if showAdd}}

> THINGS HERE

{//if} {{/if}}

If I add my select above {{#if currentUser}} - it gets shown!

The issue is that the onRendered() is being run before the {{#if}}'s are resolved in your template. In other words, the <select> html element isn’t there to be found by this.$('.selectpicker').selectpicker()

I haven’t tested this, but you might try using Tracker.afterFlush() in your onRendered. Maybe like this:

Template.triphtml.onRendered(function() {
  Tracker.afterFlush(function() {
    console.log("Maybe this will run after the #if's?");
    this.$('.selectpicker').selectpicker();
  });
});

The canonical solution is to use (sub)templates:

<template name="triphtml">
{{#if currentUser}}
  {{#if showAdd}}
    {{> showForm}}
  {{/if}
{{/if}
</template>

<template name="showForm">
  <form class="add-trip">
    <h2 class="title-text"> Add Trip </h2>

    <select class="selectpicker" data-live-search="true">
      <option>Hot Dog, Fries and a Soda</option>
      <option>Burger, Shake and a Smile</option>
      <option>Sugar, Spice and all things nice</option>
    </select>
  </form>
</template>

and use Template.showForm.onRendered. The onRendered can only fire when the template is instantiated and it can only be instantiated when the parent template’s conditions have been satisfied.

Sadly no, the afterFlush(function()) doesn’t run after #ifs.

@robfallows, this will be a bit too difficult because I have a huge template that contains even the navigation bar in it, and the overall structure looks like:

<body>
{{> triphtml}}
</body>

template name="triphtml">

 <nav class="navbar navbar-default">
  <div class="container-fluid">
    <!-- Brand and toggle get grouped for better mobile display -->
    <div class="navbar-header">
      <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
        <span class="sr-only">Toggle navigation</span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
      </button>
      <a class="navbar-brand" href="#">NAV MENU</a>
    </div>
   <!-- Collect the nav links, forms, and other content for toggling -->
    <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
      <ul class="nav navbar-nav">

       {{#if showHistory}}
//NAV for page history
{{/if}}

{{#if otherCondition}}
//NAV for other condition
{{/if}} 
{{#if otherCondition}}
//NAV for other condition
{{/if}} 
{{#if otherCondition}}
//NAV for other condition
{{/if}} 
{{#if otherCondition}}
//NAV for other condition
{{/if}} 

{{#if currentUser}}

{{#if showAdd}}
//code here
{{/if}}

{{#if otherCondition}}
//other code here
{{/if}}
{{#if otherCondition}}
//other code here
{{/if}}
{{#if otherCondition}}
//other code here
{{/if}}
{{#if otherCondition}}
//other code here
{{/if}}
{{#if otherCondition}}
//other code here
{{/if}}
{{/if}}
</template>

Kinda like this but a bit harder. Isn’t there a function available that loads after #ifs? :frowning:

Then you’re going to find it very difficult to do what you want - especially when trying to integrate non-reactive aware code like jQuery.

You could look at making your onRendered methods reactive and put code tests here (rather than spacebars tests in the html). This is an unpleasant way to handle what you’re trying to do, given the sub-template approach is so clean.

Occasionally, you may need to wait for data to become ready before it’s time to attach the plugin (although typically it’s a better idea to use a sub-component in this use case) my emphasis.

1 Like

I tried this, this works once page is loaded once… but if i go through pages stops.

function codeAddress() {
        setTimeout(function(){
        	 this.$('.selectpicker').selectpicker();
            console.log( this.$('.selectpicker'));
        },900);
           
        };

        window.onload = codeAddress;

However… This solved all my issues.

 <select class="selectpicker" data-live-search="true">
 <script type="text/javascript">
    $('.selectpicker').selectpicker();

 </script>

I’m sorry - I can’t respond positively to that. :neutral_face:

1 Like

Haha, nevermind… the database doesn’t load unless I go to a different page. :joy:

However, thank you for showing me to do it properly, in future I will be doing it the way you should me. :slight_smile: I just need to find a workaround right now, I really don’t want to redo everything… It will be too much re-doing in the main.js (client) as well. Because I have so many helpers, functions, events. :frowning: