Making dc.js charts reactive in Meteor - meteor

I'm using dc.js and crossfilter to create some charts in Meteor. Everything is set, I have all the charts ready, but there is 1 question regarding making the charts reactive. I subscribe number of collections from mongoDB on client side in JavaScript files; here, I have to update the data that I pass to crossfilter every time the data in collections change.
Suppose, for each page on the app I'm subscribing to 8 different collections, each of the collections have about 30 data fields/columns. This data in every field keeps changing or updating for every 30 seconds or so. Now the question is, as the data changes charts in the page must be updated with new data and re-render all the charts.
I learned that there are a couple of methods in Meteor which makes the page reactive like deps.autorun( ) and tracker.autorun( ). The problem is, what code should I put in the above functions to make my charts reactive?
PS: please comment back if you need any clarification, the question is already getting lengthy.

Say you have a 'dcChart' template and you are calling that template from your main page as
{{> dcChart data=dcChartData}}
helper for data as:
'dcChartData': function () {
var data = SomeCollection.find(...);
return data;
}
Your code should look something like this:
Template.dcChart.rendered = function () {
var template = this;
buildChart(template.data);
template.autorun(function () {
var templateData = Template.currentData();
buildChart(templateData);
});
}
In the autorun section you rerender the chart by passing the reactive data(records from the collection). So every time the collection is updated the chart will be rendered again).

Related

how to make crossfilter.js recognize items being added or removed

I'm working on a meteor.js app, which makes use of d3.js and crossfilter.js to create an interactive multi-chart cross-filtered dashboard.
An important part of the desired functionality is to have the individual charts adjust in real time, when a record is either added to the underying MongoDB collection, or is removed from it.
The reactivity part is not an issue -
Template.chart.rendered = function () {
Tracker.autorun(function(){
yelp_data = Yelp.find().fetch();
console.log('autorun is called');
});
}
The issue is to make the crossfilter aware of the changes, which , I hope, will force the individual charts to re-adjust in response to crossfilter changes.
By simply adding the following line to the autorun:
Tracker.autorun(function(){
yelp_data = Yelp.find().fetch();
ndx = crossfilter(yelp_data);
console.log('autorun is called');
});
did not make any difference.
What else do I need to do to have those charts adjust?
You need to use crossfilter.add and crossfilter.remove to add and remove data from a Crossfilter that has already been created. Crossfilter.remove is annoying because it requires you to change your filters in place to remove specific records. It's on a very long todo-list to fix this pull request to allow removal of arbitrary records.
Once the Crossfilter is updated, you will need to trigger update of any charts that are based on the Crossfilter. If you are using dc.js, this is just a matter of calling dc.redrawAll for whatever chart groups you've defined.

How to use any JavaScript graphic libraries with MeteorJS data

I'd like to build a small MeteorJS application, which contains a bar chart, which needs to be updated any time that the underlying MongoDB data changes.
Usually, most of the graphic libraries rely on JSON array as their "local" data sources. If I have to use such an array, how can I associate it with a Mongo collection, and make the array values update any time that collection changes?
While looking at https://forums.meteor.com, I have found a complete working example, which illustrates the solution to my problem. Here is the specific thread: https://forums.meteor.com/t/solved-how-to-draw-bar-chart-using-highcharts-and-mongodb-data-reactively/5156/17 , in which user jhuenges pints to the following meteorpad example: http://meteorpad.com/pad/iNfXQwMW3RwrokTe8/Chart
The key elements of this solution:
1) The 'data' element for the Chart is defined as a variable, who's values come from Mongo cursor:
series: [{
name: 'No. of Patients',
data: data
}]
Template.agecolumn.onRendered(function() {
Tracker.autorun(function () {
var data = Data.find({symptoms: {$in: ["Vomiting"]}}).fetch();
This combination insures, that, at any time, when the server-based MongoDB is updated, the values within 'data' are refreshed, and the Chart is re-rendered.
I have tested this by connecting my RoboMango utility to the server-side instance of MongoDB, and changing/removing values directly from the database.
As soon as any such change was applied to the server-side Mongo, the front-end Chart was refreshed automatically to reflect the changes, which is exactly what I was looking for.
As Christian Fritz suggested in his reply to my initial question, Tracker.autorun was used to facilitate the desired result.

In Meteor, is there a way to detect when data is fully loaded to the browser?

This problem probably has been asked before, but I cannot seem to figure out an easy way to do it.
I have a Meteor page that shows messages posted by users in chronological order (newest message at the bottom). I want the page to:
#1) Subscribe to the messages (using publish/subscribe) based on a parameter supplied in the URL
#2) Render the page
#3) Display the messages on the page
#4) Scroll to the bottom.
There's no apparent way to know when 1, 2, and 3 are complete before initiating the scroll to bottom. Meteor does have an observe/added function to do an event when new messages are added to a subscription, however that's only when documents are insertted into Mongo, and it does not trigger when displaying results to the initial subscription.
I'll show code for the steps above:
#1: Subscribe to the messages using publish/subscribe: in /client/messages.js
Template.messages.created = function() {
this.autorun( function() {
this.subscription = Meteor.subscribe("messages", Router.current().params.category);
}.bind(this));
};
#2 Render the page, in /client/messages.html
<template name="messages">
{{#each messages}}
{{messageText}}<br><br>
{{/each}}
</template>
#3: Display the mssages on the page: /client/messages.js
Template.messages.helpers({
messages: function() {
var category = Router.current().params.category;
return Messages.find({category: category}, { sort: { messageDate: 1 } });
},
});
All this works, but does not automatically scroll to the bottom.
I cannot add a jquery scroll command to the Meteor onRendered section because it runs BEFORE the data is written to the DOM. I can do a Meteor.setTimeout to wait 1 second before scrolling to the bottom, but does not work if it takes longer than a second to fill the DOM with subscribed data.
Here's another thing complicating the matter. I am supplying the category variable in the URL. When the client selects another category, using Meteor/Blaze pathFor,
{{pathFor 'messages' channelid='new'}}
the browser does not reload/rerender the page, it simply updates the URL parameter which triggers the autorun to change what messages it has subscribed to. It simply writes the new data to the DOM. Because of this, I cannot to a jquery $(document).ready to detect whether the page is ready (because the page is always ready), and I cannot use some fancy handlebars thing like {{scrollToBottom}} at the end of my {{#each}} in messages.html because that it not re-run.
So, is there a simple way to detect when Meteor/Blaze completely finishes writing new data to the browser?
If I understand correctly, you really just want to know when all of your data is published from the server to the client (so you can then do something, in this case, scroll to the bottom).
All calls to Meteor.subscribe() return a subscription handle. This has a ready() method, that tells you all the data has been sent. This includes those done at a template level (which you might want to consider if appropriate for your use-case). More information here:
http://docs.meteor.com/#/full/Blaze-TemplateInstance-subscribe
There are several ways to use this - you need to decide what is appropriate for you. As ready() is reactive, you can just use this. However, you might find it easier to implement an onReady callback within your subscription (see documentation above).
I suspect you will need to implement an autorun within your template rendered. This is to avoid the reverse scenario where the data arrives before the rendering (and so the callback fires too early). Something like this should do the trick (noting you have already set your subscription handle in your template creation function to this.subscription):
Template.messages.onRendered(function() {
var self = this;
this.autorun(function(computation) {
if(self.subscription.ready()){
$(document).scrollTop($(document).height());
computation.stop()
}
});
});
This should ensure both that the function is only called after rendering and the data from the subscription is complete. The autorun is also stopped after executing the scroll to stop continued calling should new documents cause ready() to no longer be truthy (and then become ready again) - which might surprise people with the page re-scrolling to the bottom!

Angular-like client side data binding and reactivity with Meteor?

I'm trying to wrap my head around Meteor's way of dealing with reactivity and I want to make sure I've got some concepts right.
Take the follow reactivity example:
A user types something into a form field. The thing that he is typing is instantly displayed somewhere else on the page, as the user is typing, letter by letter. An instantaneous duplication.
From what I know about Angular, this is a very common example of reactivity. Angular binds the input directly to the output on the client side. There's nothing in between.
Correct me since I could be wrong, but Meteor can do this, but the input would first need to be captured and stored into a Mongo + MiniMongo DB (perhaps only as a collection in local storage), there would need to be a subscribe step, and those values would then be read and displayed on the page.
Is there a way to directly bind an event on the front end to another thing on the front end like Angular does?
Is this right? For Meteor to have the front-end-only reactivity of Angular it must first go through the intermediary of a collection, meaning extra code would be necessary to accomplish this compared to Angular?
The example in the Meteor Docs:
Deps.autorun(function () {
Meteor.subscribe("messages", Session.get("currentRoomId"));
});
So here, when the data of currentRoomId changes, the function is reactive to that data change and the function runs (in this case Meteor subscribes to messages).
Using Session variables is the only way I see of possibly binding two parts of a view together directly. Are there other ways?
Meteor's client-side reactivity system (Deps) is not coupled with its live MongoDB syncing. You can use it with any reactive data source which implements the right interface, including data sources which are entirely client-side. For example, you can use the built-in Session object. This is just a client-side key-value store with support for Meteor's reactivity, and you don't have to do any publish or subscribe to use it.
This standard way to do this sort of thing looks something like this:
<input id="field" value="{{fieldValue}}">
Template.form.fieldValue = function () {
return Session.get("fieldValue");
};
Template.form.events({
"input #field": function (evt) {
Session.set("fieldValue", $(evt.currentTarget).val());
}
});
Now the Session variable fieldValue is synced up to the form field. You can call Session.get("fieldValue") in some helper and that template will re-render when the user types in the form field. And if you call Session.set("fieldValue", "blah") then the form field will update itself.
As for your edit: You can make your own reactive data sources using Deps.Dependency, or you could meteor add reactive-dict although that's not documented. There may be packages on Atmosphere.

Meteor.deps.Context and Invalid Collection of Documents

What's the difference with the following two blocks of code? The top works as expect, but the bottom does not.
// Initially outputs 0, but eventually outputs the # of players.
Meteor.autorun(function() {
var players = Players.find();
console.info(players.count());
});
// Outputs 0 twice. Why does this not work like the block above?
var players = Players.find();
Meteor.autorun(function() {
console.info(players.count());
});
I'm testing this in the leaderboard example, within the Meteor.isClient block.
Thank you,
Andrew
While Meteor is reactive you need to make your query within a reactive context a.k.a the Meteor.autorun. The reactive contexts are: Template, Meteor.autorun, Meteor.render and Meteor.renderList.
In the second case var players = Players.find(); is run while Meteor is starting up, and contains the data it got while querying at that time, while starting up.
In the first you've placed the query in a reactive context. Which is recalled and rerun whenever there is a data update of a sort. In the second case it doesn't get a chance to rerun the query it it remains with the data contained while the browser just loaded the page up.
While Meteor is reactive you still need to re query the data within the reactive context.

Resources