I want to load a spinner when my account page is loading, it is taking a bit time to load "myaccount" page so i want to load a spinner using spinner atmosphere package.
Here is my code,where do i need to add {> spinner} to load the spinner
this.route('myaccount', {path:'/myaccount/',
fastRender: true
});
for myaccount,it need to contact 3rd party api to load some data to page.
Suggest me which is the best place to load spinner
If you use NProgress preloader then you can achieve it like that:
this.route('myaccount', {
path:'/myaccount/',
action:function(){
self = this;
self.render('header', {to:'header'});
NProgress.start();
// load external data and onComplete (callback) it renders template and stops preloader
MyAccount.loadData(function(){
self.render(self.template);
NProgress.done();
})
}
});
Update :
Standard solution for using Spinner with Meteor subscription mechanism is using waitOn function:
<template name="loadingTemplate">
{{>spinner}}
</template>
<template name="layout">
<nav>
{{> yield region='header'}}
</nav>
{{> yield}}
<footer>
{{> yield region='footer'}}
</footer>
</template>
Router.configure({
loadingTemplate: 'loadingTemplate',
layoutTemplate: 'layoutTemplate',
yieldTemplates: {
'header': { to: 'header' },
'footer': { to: 'footer' }
}
});
//
this.route('myaccount', {
path: '/myaccount',
template:'myaccountTemplate',
layoutTemplate: 'layoutTemplate',
data: function() {
...
},
waitOn: function () {
return Meteor.subscribe('...');
}
});
Iron-router-progress is great!
https://github.com/Multiply/iron-router-progress
with this option, it's top
Router.configure
progressDelay : 100
Related
This Meteor client code is expected to show a progress bar animation when a button is clicked. The problem is that the progress element is null when the animation code tries to use it.
How can it be fixed? thx
Template.registerHelper('session', (value) => {
return Session.get(value);
})
Template.footer.events({
'click button[data-action=carSearch]': function (e) {
e.preventDefault();
clickInfo();
}
});
'clickInfo': function () {
Session.set('inProgress', true); // makes the element visiable
progressBarStart(); // start the animation
}
'progressBarStart': function () {
let bar = document.getElementById('bar'); //<-- bar is always null
if (bar.value < 70) {
setTimeout(lib.progressBarStart(), 1000); controls the speed
bar.value += 1;
}
}
<template name="nameSearch">
<div id="nameSearch">
<p id="result" data-id={{_id}}>
{{{display.result}}} <br>
{{#if (session 'inProgress')}}
<progress id="bar" value="0" max="70"></progress>
{{/if}}
</p>
</div>
</template>
You should give the render a time to re-render after calling Session.set('inProgress', true);, 50ms will be enough.
Next, you should declare clickInfo and progressBarStart correctly, not like you have them now.
function clickInfo() {
Session.set('inProgress', true);
Meteor.setTimeout(progressBarStart, 50);
}
function progressBarStart() {
...
}
I'd also recommend to use dedicated helper to obtain inProgress status:
Template.footer.helpers({
isInProgress: function() {
return Session.equal('inProgress', true);
}
});
And use it in your template:
{{#if isInProgress}
...
{{/if}}
You must not use Session variables for such a small reason. Meteor JS has given a facility to use Reactive Variables at template level manipulations.
You should give the render a time to re-render after calling Template.instance().inProgress.set(true);.
I'd also recommend to use dedicated helper to obtain inProgress status:
Template.footer.onCreated(function(){
this.inProgress = new ReactiveVar(false);
});
Template.footer.helpers({
isInProgress: function() {
return Template.instance().inProgress.get();
}
});
And use it in your template:
{{#if isInProgress}
...
{{/if}}
How to get attribute value from tag like width, color, value ...
<template>
{{#if body.width > 979px }}
{{> tmp1 }}
{{else}}
{{> tmp2 }}
{{/if}}
</template>
<template name="tmp1">...</template>
<template name="tmp2">...</template>
You can't access tag attributes directly from Spacebars template. You need to create a template helper for that.
Template.templateXY.helpers({
bigBody: function() {
return $("body").width() > 979;
}
});
then you use it like this:
<template name="templateXY">
{{#if bigBody}}
{{> tmp1}}
{{else}}
{{> tmp2}}
{{/if}}
</template>
UPDATE: for the helper to recompute on window resize event, you need to modify it a little bit. You can use a Dependency object for this.
Template.templateXY.onCreated(function() {
// create a dependency
this.resizeDep = new Dependency();
});
Template.templateXY.onRendered(function() {
let tmpl = this;
// invalidate the dependency on resize event (every 200ms)
$(window).on("resize.myEvent", _.throttle(function() {
tmpl.resizeDep.changed();
}, 200));
});
Template.templateXY.helpers({
bigBody: function() {
// recompute when the dependency changes
Template.instance().resizeDep.depend();
return $("body").width() > 979;
}
})
Template.templateXY.onDestroyed(function() {
$(window).unbind("resize.myEvent");
});
Other possible solution would be to store the window width to a ReactiveVar (which is a reactive data source itself) and use .on("resize", fn) to change the ReactiveVar
After some researches I found that there's no spacebars proper solution and the best option is to use js code.
So here's the code:
Session.set("width", $(window).innerWidth());
window.onresize = function () { Session.set("width", $(window).innerWidth()); };
if(Meteor.isClient) {
Template.body.helpers({ 'dynamicTemplateName': function () { return Session.get("width"); } });
}
I have chat app, and if you use Slack you know that when you enter to the room, you will automatically find yourself at the bottom of your chat room.
So I decided to do this and what I have
Template.room.onCreated(function() {
console.log($(document).height(),$(window).height());
$('html, body').scrollTop($(document).height()-$(window).height());
});
it output 437 , 437
BUT when I do this in console:
$('html, body').animate({ scrollTop: $(document).height()-$(window).height() + 64}, "fast");
it outputs 2000,437 , and that means that my messages is not loaded fully in my template. (If someone want more code, just ask.)
Any idea how to build this ?
EDIT:
This part of template.html
<div class="messages-wrap">
<div class="container-fluid">
<div class="row">
<div class="col-md-12">
{{#if haseMoreMessages}}
<div class="loadmore text-center" id="incLimit">Load More</div>
{{/if}}
{{#if Template.subscriptionsReady }}
{{#each messages}}
{{> message}}
{{/each}}
{{/if}}
</div>
</div>
</div>
</div>
And template.js (part of it)
Template.room.onRendered(function() {
Session.setDefault('messageLimit', 200);
});
Template.room.onCreated(function() {
var self = this;
self.autorun(function() {
if (self.subscriptionsReady()) {
Tracker.afterFlush(function () {
$('html, body').scrollTop($(document).height() - $(window).height());
});
}
});
});
Template.room.events({
'click #incLimit' : function(e,template){
Session.set('messageLimit',Session.get('messageLimit') + 100);
}
});
Template.room.helpers({
messages: function(){
return Messages.find({},{sort:{createdAt:1}});
},
haseMoreMessages:function(){
if (Session.get('messageLimit') > Messages.find().count()) return false;
return true;
},
});
This is one very frustrating aspect of Blaze. Try this, though:
Template.room.onRendered(function () {
var template = this;
this.autorun(function () {
if (template.subscriptionsReady()) {
Tracker.afterFlush(function () {
$('html, body').scrollTop($(document).height() - $(window).height());
});
}
});
});
This waits till all the template subscriptions are ready first, and then waits till any computations are fully complete (Tracker.afterFlush), and then executes the scroll. Tracker.afterFlush is usually necessary if your template has {{#if}} blocks that depend on other things before they get evaluated and rendered.
UPDATE:
Without seeing all your code and knowing why or when you want to scroll to the top, it's hard to say what you're aiming for. But see the Meteorpad link below for a working version of what you were trying to do with the message limits (I'm only incrementing the limit by 1 since there are 3 messages).
Meteorpad Link
A few things you should note:
Set default variables and subscribe to things in Template.x.onCreated, not Template.x.onRendered.
You forgot to actually subscribe to your collection.
Limit messages on the server side, in the Meteor.publish callback.
Different Approach via tracking of Template helpers:
I took advantage of the Template helper, as it already tracks reactively all changes (and new) messages. Hence, there you can place your scroll-down command.
Template.room.helpers({
messages: function(){
//put your scroll-up command here
//as this helper is keeping track on changes already
$('html, body').scrollTop($(document).height() - $(window).height());
return Messages.find({},{sort:{createdAt:1}});
},
haseMoreMessages:function(){
if (Session.get('messageLimit') > Messages.find().count()) return false;
return true;
},
});
Hence, you save the resources to have an additional tracker observing the collection (you would be double-tracking the same collection).
Most of the time, my search returns so fast that it's not worth flashing the loading template to the user...(in fact, it's distracting, as people are fine with a blank screen if the results are coming a split second later)...Is there any way to prevent the loading template from showing if the waitOn is only waiting for a short amount of time?
Here's my configuration
Router.route('/search', {
waitOn: function () {
return searchSubsManager.subscribe("search", Session.get('searchString'));
},
action: function () {
this.render('searchResults');
}
});
I saw that with this package:
https://github.com/Multiply/iron-router-progress
you can control whether it shows up for fast routes, but I don't need all that functionality, nor do I want the progress bar it provides... I'm just wondering if the basic iron router/ waitOn functionality can provide this ability.
There is not configuration to use on the waitOn function, but Why don't you create another layout template, and use it to show that fast routes?
<template name="noLoading">
{{> yield}}
</template>
Router.map(function () {
this.route('fastRoutes', {
path: '/someRoutes',
template: 'myHomeTemplate',
layoutTemplate: 'noLoading',
});
});
Update
or use the sacha:spin package and change the class name depending on the duration of the query.
if(queryDuration){
Meteor.Spinner.options = {
className: 'none'
}
}else{
Meteor.Spinner.options = {
className: 'spinner'
}
}
How do you get a Meteor app to start up on a particular page? If you don't control this, then the browser remembers where you were in the app when you last closed the app, and it tries to load that old location. Which is fine, if that's what you want, but if not, then what do you do?
Using iron-router in my app, this used to work:
Meteor.startup(function () {
Router.go('/');
});
Now this throws an error in Router.js, complaining that self._location is undefined. It's as if the call to Router.go() is happening too soon now. Is there a way to know when Router is ready for a go() call? Or a way to introduce a delay here?
_________________EDIT_________________
In an attempt to solve this, I have reduced everything down to the essentials. Here's all my code, 2 files in total:
newPL.js:
Router.configure({
layoutTemplate: 'layout'
});
Router.map(function () {
this.route('home', {
path: '/',
});
this.route('main', {
path: '/main',
});
});
if (Meteor.isClient) {
Meteor.startup(function () {
Router.go('home');
});
}
and newPL.html:
<head>
<title>newPL</title>
</head>
<template name='layout'>
<div>
{{> yield}}
</div>
</template>
<template name='home'>
<div>
Welcome to Planet Lingo!
<a href='/main'>GO</a>
</div>
</template>
<template name='main'>
<div>
<h1>Main</h1>
</div>
</template>
This looks like a bug in iron-router. Iron router does a Meteor.defer around router.start see: Iron-router source line 34
You can work around this by using Meteor.defer yourself
Meteor.startup(function () {
Meteor.defer(function () {Router.go('home');});
});
This works fine as your defer gets put below the one thats created by iron-router