I'm not sure if this is a Meteor issue or a generic one but I have the following code in my app.
<template name='admin'>
<div class='admin container-fluid noPadding'>
{{#if isInRole 'Admin'}}
<h3 class="homepageText">Server Statistics</h3>
{{> serverFacts}}
{{else}}
You don't belong here!
{{/if}}
</div>
When the page renders I see "You don't belong here!", then "Server Statistics" replaces it a second or so later. I have the same problem other places in my app always with a Blaze {{#if ...}} involved. Is there a way to stop the page from displaying in the browser until the rendering has completed and settled down?
This is a generic issue with reactive applications - rendering often happens while the data is still being pushed to the client. The normal solution is to use a spinner (ex: sacha:spin) until the underlying subscription(s) are ready. Then in Blaze you end up with:
<template name='admin'>
{{#if loading}}
{{> spin}}
{{else}}
<div class='admin container-fluid noPadding'>
{{#if isInRole 'Admin'}}
<h3 class="homepageText">Server Statistics</h3>
{{> serverFacts}}
{{else}}
You don't belong here!
{{/if}}
{{/if}}
</div>
You'll need a helper to compute loading based on your subscriptions. In a more complicated layout backed by several subscriptions you might end up with more than one spinner spinning at a time.
Related
In my Stencil theme, I am including a few different size charts for products which I intend to include by just changing the path to the size chart document. I found dynamicComponent in the Stencil docs and I thought I understood the way it worked. In my higher level partial, I am binding the string to component in this way - (product.html)
<div class="container" itemscope itemtype="http://schema.org/Product">
{{> components/products/product-view schema=true sizeChart='components/products/size_charts/tshirt.html'}}
{{#if product.videos.list.length}}
{{> components/products/videos product.videos}}
{{/if}}
{{#if settings.show_product_reviews}}
{{> components/products/reviews reviews=product.reviews product=product urls=urls}}
{{/if}}
</div>
(product-view.html)
{{#if sizeChart}}
<div class="tab-content" id="tab-sizeChart">
{{dynamicComponent sizeChart}}
</div>
{{/if}}
Where all I wish to change is the variable sizeChart in future theme maintenance. When the page renders, the place where I wrote the dynamicComponent is blank.
I used if conditionals instead of dynamicComponent. It wasn't what I thought it was.
I am using iron-router, i have a route "models" which has a list of items. When the user clicks one of these items a new route "model" is used, the name of the selected item is passed as a param and used to load data about that model from the database.
I want to use slick carousel, using an array of images returned from the database (based on the param).
<div id="carousel">
{{#each images}}
<div class="item">
<img src="/images/{{this}}" alt="">
</div>
{{/each}}
</div>
Where should I call the slick init?
Generally speaking, you should initialize plugins in a template's onRendered callback. In your case, that won't work because onRendered will fire before any of the images are inserted into the DOM. With other carousel plugins I've seen, the following strategy works:
Move each item to its own template (carouselItem).
Add an onRendered callback to carouselItem so that the plugin will be initialized after each item is added to the DOM.
I haven't tried this with slick carousel, but it would look something like this:
<template name="carousel">
<div id="carousel">
{{#each images}}
{{> carouselItem}}
{{/each}}
</div>
</template>
<template name="carouselItem">
<div class="item">
<img src="/images/{{this}}">
</div>
</template>
Template.carouselItem.onRendered(function() {
$('#carousel').slick();
});
Assuming slick carousel can be initialized multiple times, this approach should work. Be aware that one possible downside is that the plugin will refresh whenever images is updated. One way to fix that is to use the {reactive: false} option in your find.
Usually you would call a plugin on an element in Template.myTemplate.onRendered:
Template.xxx.onRendered(function() {
$('#carousel').slick();
});`
I simply created a meteor project and copy/paste from the example 'basic' files (found at https://github.com/EventedMind/iron-router/tree/devel/examples/basic) replacing Meteor project .html and .js files.
On the HTML, the example failed to include any template inclusion so I added {{> Home}} and run Meteor between the 'body' block.
<body>
{{> Home}}
</body>
The complete HTML code is:
basic
<body>
{{> Home}}
</body>
<template name="Home">
<h1>Home</h1>
{{> Nav}}
<p>
Data Title: {{title}}
</p>
</template>
<template name="One">
<h1>Page One</h1>
{{> Nav}}
</template>
<template name="Two">
{{> Nav}}
<h1>Page Two</h1>
</template>
<template name="Nav">
<ul>
<li>
Home
</li>
<li>
Page One
</li>
<li>
Page Two
</li>
</ul>
</template>
The .js code is exactly as in the example.
When run, however, it failed to change or route to pages One and Two. It simply stays on Home.
I am learning to work with it but I don't seem to get it right even on the simplest of examples. What I am doing wrong?
Router.map is now deprecated and replaced by Router.route, however, there seem to be a few bugs they're still ironing out. First of all, I've found that their basic routing function rarely works at the moment, so don't use it for now, i.e.:
Router.route('/path', function() {
this.render('pathName');
});
Whether this is due to compatibility issues or something else, I have no idea. I have found a very standard formula that seems to work quite nicely however, and that is to replace the function with the built in "Route Specific Options", and specify exactly what you want, like so using basic JSON:
Router.route('/path', {
name: 'pathName',
path: '/path',
template: 'templateName',
data: function () {
title: 'My Title'
}
});
Notice the path: option. This should not be necessary, as the path is defined as the first parameter of your Router function, but they specifically state in the documentation that you may need to include a path: in your routes for backwards compatibility. I'm sure once they get everything operating smoothly you'll be able to delete this entirely.
Also, the template: option should not be necessary if the templateName is the same as your /path (and for that matter, the pathName: option should not be necessary if it is also the same as your /path), but I've found including them just makes life easier as sometimes they will not function properly otherwise.
In short, when the bugs are gone, simple templates like yours will be called with one line:
Router.route('/one');
The end result is that they have simplified how routes are called and defined, but unfortunately, it just doesn't seem to working as planned at the moment. I hope this helps you with understanding the difference between Router.map and Router.route now.
Plus, the main problem with your code before was that you inserted the partial {{> Home}} between your body tag, which isn't necessary and messes things up, as that partial will always remain even as meteor attempts to route between and load other templates. In essence, when Meteor went to load template "One" it had to do so on top of template "Home", meaning you had, among other things, two {{> Nav}} partials loading at once.
When using iron router package you must leave body tag as it is or don't include it at all, i'm using a layout template but its not necessary:
basic.html:
<head>
<title>basic</title>
</head>
<body>
</body>
<template name="layout">
{{> yield}}
</template>
<template name="Home">
{{> Nav}}
<h1>Home</h1>
<p>
Data Title: {{title}}
</p>
</template>
<template name="One">
{{> Nav}}
<h1>Page One</h1>
</template>
<template name="Two">
{{> Nav}}
<h1>Page Two</h1>
</template>
<template name="Nav">
<ul>
<li>
Home
</li>
<li>
Page One
</li>
<li>
Page Two
</li>
</ul>
</template>
basic.js:
Router.configure({
layoutTemplate: 'layout'
});
Router.map(function(){
this.route('Home', {path: '/', data: {title: 'My title'}});
this.route('One');
this.route('Two');
});
and now works properly.
I'm migrating some code to blaze and have hit a problem with the bootstrap carousel that I can't seem to get over.
I had the following pre blaze to set one of the carousel items active to kick the whole thing off
<div class="item {{#if active_sponsor}}active{{/if}}">
As documented, this no longer works with blaze, so I've tried modifying it to the only thing I can think of which is
{{#if active_sponsor}}
<div class="item {{#if active_sponsor}}active{{/if}}">
{{else}}
<div class="item">
{{/if}}
This all lives within an {{each sponsors}} block.
Sadly, this fails to run with an error saying unexpected {{else}} (or, if I remove the {{else}} unexpected {{/if}}
What's the correct way to do this. I'm using exactly the same pattern earlier to change a
From "Using Blaze" on github :
https://github.com/meteor/meteor/wiki/Using-Blaze#conditional-attributes-with-no-value-eg-checked-selected
So you should use this form instead, assuming that active_sponsor is the property to look for in the current data context.
Template.whatever.helpers({
isActive:function(){
return this.active_sponsor?"active":"";
}
});
<div class="item {{isActive}}">
</div>
I have a multiple select with a Handlebar template in Meteor.js. On first rendering, everything is fine ("Politics" and "People" are preselected as expected):
As soon as the template has to be rerendered (because a Session variable changes, e.g. Session.set("foo", "Hello World!")), the third option is not preselected anymore:
My setup:
<template name="select">
<select name="foo" multiple>
<option value="1">Tech</option>
<option value="2" selected>Politics</option>
<option value="3" selected>People</option>
</select>
</template>
<template name="test">
{{foo}}
{{> select}}
</template>
{{> test}}
Template.test.helpers(
foo: ->
Session.get("foo")
)
Do you have any idea why the options are preselected anymore after rerendering?
Solution is:
<template name="test">
{{#isolate}}
{{foo}}
{{/isolate}}
{{> select}}
</template>
Typically, the exact extent of re-rendering is not crucial, but if you
want more control, such as for performance reasons, you can use the
{{#isolate}}...{{/isolate}} helper. Data dependencies established
inside an #isolate block are localized to the block and will not in
themselves cause the parent template to be re-rendered. This block
helper essentially conveys the reactivity benefits you would get by
pulling the content out into a new sub-template.
If you put {{foo}} inside {{#isolate}} ... {{/isolate}} then parent template won't be rerendered, so also {{> select}} won't be affected.
So I am not sure why you are loosing the multiple select but I can recommend you put an {{#isolate}} tag around your{{> select}} template. That should keep the template from re-rendered. Though it will not help if your select template re-renders because its data changes. Hope that helps...