Meteorjs loading message - meteor

I built the application and it is taking time to load the initial set of data from mongodb and I want to show the loadin gif till the data loading is done. Could you please help me in doing this?

Use Session inside the onReady() callback of the Meteor.subscribe() function, which is invoked when the subscription is completed.
Meteor.subscribe('subscribe_to_this', function onReady(){
// set a session key to true to indicate that the subscription is completed.
Session.set('subscription_completed', true);
});
Then make this session value to be returned from your template helper as:
Template.myTemplate.isSubscriptionComplete = function(){
return Session.get('subscription_completed');
}
Now in your html, it is easy to display a loader if data is not loaded or to render a template, if the data has completed loading.
<template name="myTemplate">
{{#if isSubscriptionComplete }}
<!-- Data loading is done, so render your template here -->
{{> yourFinalTemplate}}
{{else}}
<!-- Data loading still remaining, so display loader here -->
<img src="images/load.gif">
{{/if}}
</template>

This could be done with Session variables. This is just an example to get you started:
In your client code:
var yourData;
Meteor.startup(function() {
Session.set("dataLoaded", false);
});
...
// Load your initial data
yourData = SampleCollection.find(query).fetch();
Session.set("dataLoaded", true);
A sample template:
<template name="main">
{{#if dataLoaded}}
<h1>Welcome to my application!</h1>
Your data:
{{#each yourData}}
...
{{/each}}
{{else}
<div>Loading data...</div>
{{/if}}
</template>
The template helpers:
Template.main.dataLoaded = function() {
return Session.get("dataLoaded")
}
Template.main.data = function() {
return yourData;
}

Related

How to force Meteor to rerender immediately, or prioritize reactive update?

I'm experimenting with building my own loading spinner and the way I have it set up is with a helper that check if entry arrays are empty.
{{#if viewThisPage}}
{{#if noEntriesLoaded}}
<div class="loadingSpinner">...</div>
{{/if}}
{{#each entry}}
{{ > myTemplate}}
{{/each}}
{{/if}}
Initially viewThisPage is false. When viewThisPage is set to true I query mongoDB for entries. The issue that I'm seeing from benchmarking is that the query goes so fast that all the DOM update events are squished together so that the spinner never gets rendered, in other words, noEntriesLoaded is true for too short a time.
There is, however, around a 1200ms lag between triggering viewThisPage and the page actually rendering. I want the spinner to display during this period of lag.
Three ways I've thought of to fix this
Somehow force meteor to rerender the section immediately after setting viewThisPage while entries haven't loaded yet.
Somehow prioritize the reactive response to viewThisPage so that it updates before the entires are loaded.
Add fake lag using setTimeout() on the block that adds entries. This works and I get to see my pretty loading spinner but is the dumbest idea ever.
You can show spinner while your entries are being rendering.
This code will hide loadingSpinner only when all entries are rendered:
meteor add reactive-var
<template name="Main">
{{#if viewThisPage}}
{{#if entriesAreNotReady}}
<div class="loadingSpinner">...</div>
{{/if}}
{{> Entries entry=entry onRendered=onEntriesRendered}}
{{/if}}
</template>
<template name="Entries">
{{#each entry}}
{{> myTemplate}}
{{/each}}
</template>
Template.Main.onCreated(function() {
this.areEntriesRendered = new ReactiveVar(false);
});
Template.Main.helpers({
onEntriesRendered: function() {
var areEntriesRendered = Template.instance().areEntriesRendered;
return function() {
areEntriesRendered.set(true);
};
},
entriesAreNotReady: function() {
return ! Template.instance().areEntriesRendered.get();
}
});
Template.Entries.onRendered(function() {
this.data.onRendered();
});
P.S. Possibly you will need to check that entries are loaded before you render Entries template

How to deal with the situation that template is rendered but the data is not ready?

In client startup I subscribe to something:
Meteor.publish("Roles", function(){
return Roles.find();
});
Meteor.startup(function() {
if(Meteor.isClient) {
Meteor.subscribe('Roles');
}
});
And roles template:
Template.roles.helper(function() {
allRoles: function() {
return Roles.find().fetch();
}
})
<template name="roles">
<div>
{{#with allRoles}}
<label>{{> role }}</label>
</div>
</template>
The problem is sometime roles template is rendered before the Roles is ready.
How to deal with this situation?
You can do the subscribe on the template and then use the Template.subscriptionReady helper to create a conditional to show a loading panel whilst your subscription is being loaded as follows:
Template.roles.onCreated(function () {
this.subscribe("Roles");
});
Template.roles.helper(function() {
allRoles: function() {
return Roles.find().fetch();
}
})
<template name="roles">
<div>
{{#if Template.subscriptionsReady}}
{{#with allRoles}}
<label>{{> role }}</label>
{{else}}
Loading...
{{/if}}
</div>
</template>
This replaces your other subscription and these subscriptions can be added to each onCreated method for each template to have subscriptions per template.
There are some common ways of dealing with it. You can use a guard or make use of iron router's waitOn function. With a guard you only return data from the helper if you're getting any results:
allRoles: function() {
var roles = Roles.find();
//explicit version
if (roles.count()) return roles
//implicitly works as well here because you're returning null when there are no results
return roles
}
You don't need the fetch() in this case, because #with works with a cursor. If you run into a situation where you need to fetch first because you're returning partial data, check that there are results first and only then return them.
You can also use iron router's waitOn Option if you're using this as part of a route.

Content wrapped in currentUser re-rendering when user updated

I'm using Meteor and having an issue where my content is being re-rendered when I don't want it to.
I have my main content wrapped in a currentUser if statement which I feel is fairly standard.
{{#if currentUser}}
{{> content}}
{{/if}}
The problem with this is my content template is being re-rendered when I update my user object. Is there any way around this? I don't reference users anywhere inside the content template.
Thank you!
Here's a sample app to replicate my problem:
HTML
<head>
<title>Render Test</title>
</head>
<body>
{{loginButtons}}
{{> userUpdate}}
{{#if currentUser}}
{{> content}}
{{/if}}
</body>
<template name="userUpdate">
<p>
<input id="updateUser" type="button" value="Update User Value" />
User last update: <span id="lastUpdated">{{lastUpdated}}</span>
</p>
</template>
<template name="content">
<p>Render count: <span id="renderCount"></span></p>
</template>
JavaScript
if (Meteor.isClient) {
Meteor.startup(function() {
Session.set("contentRenderedCount", 0);
});
Template.content.rendered = function() {
var renderCount = Session.get("contentRenderedCount") + 1;
Session.set("contentRenderedCount", renderCount);
document.getElementById("renderCount").innerText = renderCount;
};
Template.userUpdate.events = {
"click #updateUser": function() {
Meteor.users.update({_id: Meteor.userId()}, {$set: {lastActive: new Date()}});
}
};
Template.userUpdate.lastUpdated = function() {
return Meteor.user().lastActive;
};
}
if (Meteor.isServer) {
Meteor.users.allow({
'update': function () {
return true;
}
});
}
Update:
I should've explained this example a little. After creating a user, clicking the Update User Value button, causes the render count to increment. This is because it's wrapped in a {{#if currentUser}}. If this is if is removed, you'll notice the render count remains at 1.
Also, you'll need to add the accounts-ui and accounts-password packages to your project.
Meteor will re-render any template containing reactive variables that are altered. In your case the {{currentUser}} is Meteor.user() which is an object containing the user's data. When you update the users profile, the object changes and it tells meteor to re-calculate everything reactive involving the object.
We could alter the reactivity a bit so it only reacts to changes in whether the user logs in/out and not anything within the object itself:
Meteor.autorun(function() {
Session.set("meteor_loggedin",!!Meteor.user());
});
Handlebars.registerHelper('session',function(input){
return Session.get(input);
});
Your html
{{#if session "meteor_loggedin"}}
{{> content}}
{{/if}}

returning findOne object to template

Having troubles understanding how to return and use an object from findOne().
my code is this:
Html:
<head>
<title>count</title>
</head>
<body>
{{> hello}}
</body>
<template name="hello">
{{showcount}}
</template>
Js:
var Database = new Meteor.Collection("counters");
if(Meteor.is_client) {
Template.hello.showcount = function () {
var c = Database.findOne();
return c;
};
}
if (Meteor.is_server) {
Meteor.startup(function () {
if(Database.find().count() === 0)
{
Database.insert({name: "counter", value: 0});
}
});
}
Now I'm wondering if there is any way I can access the data from my object.
changing from {{showcount}} to {{showcount.name}} doesn't seem to work at all.
This same issue got me a few times when I started out with Meteor...
When the Meteor client connects to the server the template is being rendered before the collections have finished being synchronised. i.e. The client collection is empty at the point you are calling findOne.
To see this in action stick a console.log(c) after your findOne call then try reloading the page. You will see two log entries; Once on initial page load and then again when the collection has finished being synchronised.
To fix this all you need to do is update your hello template to handle the fact the collection might not have been synchronised.
{{#if showcount}}
{{showcount.name}}
{{/if}}
I tested your code with the above change and it works.
The proper way to do this is with the #with tag.
<template name="hello">
{{#with showcount}}
{{name}}
{{/with}}
</template>
See the documentation below for more info on the #with tag
https://github.com/meteor/meteor/blob/devel/packages/spacebars/README.md

Cannot read property '_liveui' of null

I'm getting client side errors(console.log ones) but my app works(I can add users)
The error is the following:
Uncaught TypeError: Cannot read property '_liveui' of null
The project is in my repo:
https://github.com/thiagofm/statusfyit
What is happening?
Meteor has updated its API a bunch since this question was asked, so the original code no longer runs directly.
Using jQuery.html to insert the results of rendering a template is not the normal approach. It is better to use the handlebars template include functionality.
For example, replace:
$().ready(function(){
hello = Meteor.ui.render(function(){
return Template.hello();
});
$('body').html(hello);
});
With:
<body>
{{> hello}}
</body>
To render different things depending on the state of the application, use the 'Session' object to conditionalize includes. For example:
<template name="foo">
{{#if showNewUserDialog}}
{{> newUserDialog}}
{{else}}
other stuff
{{/if}}
</template>
<template name="newUserDialog">
some stuff
</template>
and
Template.foo.showNewUserDialog = function () {
return Session.get('showNewUserDialog');
};
Template.other.events({
'click #new_user': function () {
Session.set('showNewUserDialog', true);
}
});

Resources