Meteor - reactive template value not from database - meteor

I would like to have a template value {{abc}} that is reactive to changes triggered by other code in my application, but it is not a database field. I have seen the Session variable being used for this but is this the only way?

Please have a look at meteor documentation with examples.
From there:
var weather = "sunny";
var weatherDep = new Deps.Dependency;
var getWeather = function () {
weatherDep.depend()
return weather;
};
var setWeather = function (w) {
weather = w;
// (could add logic here to only call changed()
// if the new value is different from the old)
weatherDep.changed();
};
So everytime you call setWeather() the dependency in getWeather() will be flagged as changed and call any reactive functions to run again with the new value.

Related

Event Listeners and Global Variables that are defined = "Uncaught TypeError is not a function" when set as function before invoking

I ran into an interesting issue when defining a global variable (as null in this case), and having a function change that global variable to a callback that was passed to that function---then trying to invoke the global variable (which is now the function) from a click event listener. However if that global variable wasn't defined (var globalCallback; as opposed to var globalCallback = null;) then everything is okay. I was under the assumption that the updated variable reference is always accessible by event listeners regardless of the variable's initial value---this doesn't seem to be the case.
See code below:
TypeError
document.addEventListener("DOMContentLoaded", function(){
//...
theSettingFunction(function(){
//...
});
var globalCallback = null; //creates TypeError when invoked after assigned to function
//var globalCallback = function(){}; //tried this too to test
function theSettingFunction(callback)
{
//...
globalCallback = callback;
//...
}
/* This event listener doesn't need removing it's a core UI element
This event gets triggered only after theSettingFunction() has been invoked first */
document.querySelector('#myButtonDiv').addEventListener('click', function(){
//...
globalCallback(); //invoking sees globalCallback as null still = TypeError
});
//...
});
Everything Okay
document.addEventListener("DOMContentLoaded", function(){
//...
theSettingFunction(function(){
//...
});
var globalCallback;
function theSettingFunction(callback)
{
//...
globalCallback = callback;
//...
}
/* This event listener doesn't need removing it's a core UI element
This event gets triggered only after theSettingFunction() has been invoked first */
document.querySelector('#myButtonDiv').addEventListener('click', function(){
//...
globalCallback(); //invoking... hey I see you---you're a function! Invoked.
});
//...
});
Is this because of the way the JS engine optimizes? Why else are event listeners not getting the updated references to global variables when they're defined?
No, this is not an engine optimization. It's an effect of JavaScript's funny hoisting rules, and the order in which the various things appear in your code.
The JavaScript language specifies that certain declarations are "hoisted" to the beginning of the program. I guess the idea was to make programming easier by not forcing programmers to pay attention to the order of things -- except, as it turns out, automatic reordering only gets you so far...
For example, you can call a function before defining it. Let's look at an example:
func(); // No error, because "func" is defined later.
variable = 1; // No error, because "variable" is declared later.
function func() {}
var variable;
console.log(variable); // Will print "1".
Under the hood, this is implemented by the engine reordering things as follows:
function func() {}
var variable;
func();
variable = 1;
console.log(variable); // Will print "1".
(Again, this is not an optimization, it's a requirement of the JavaScript language.)
An additional detail is that a combined declaration+initialization of a variable is split into two steps, and only the declaration is hoisted. So this:
console.log(variable); // Prints "undefined".
var variable = 1;
after internal re-ordering becomes this:
var variable;
console.log(variable); // Prints "undefined".
variable = 1;
Now, if we simplify your code a bit, we can see that this is exactly what happens. By inlining theSettingFunction, and assuming that the body of the onclick-handler is invoked directly, we get:
// This is what `theSettingFunction` does:
globalCallback = function() { /*...*/ }
var globalCallback = null;
// This is what the click-handler does:
globalCallback();
which the engine is required to reorder to:
var globalCallback; // hoisted declaration
globalCallback = function() { /*...*/ }
globalCallback = null;
globalCallback(); // TypeError, of course!
So you can easily fix the problem by moving the declaration+initialization of globalCallback before its first assignment, i.e. the call site of theSettingFunction:
document.addEventListener("DOMContentLoaded", function(){
var globalCallback = null; // Declare and initialize...
//...
theSettingFunction(function(){ // ...before assigning.
//...
});
function theSettingFunction(callback)
{
//...
globalCallback = callback;
//...
}
document.querySelector('#myButtonDiv').addEventListener('click', function(){
//...
globalCallback();
});
//...
});
Arguably, this will also make your code easier to read, because it's easier for humans to understand in which order things are happening when they don't have to take any invisible reordering into account. For this reason, personally, I would also move the definition of theSettingFunction before its first call.

Issues DocumentMerge in Google AppMaker

As I would like to create documents by merging the entries in a list into a Google Docs template. I have therefore integrated the DocumentMerge method from my previous question into a printButton in a list widget.
Clicking on the printButton should produce a document that merges the contents of the current row into the document template. But when I click on the printButton the method fails due to a circular reference. How can I fix that? The print method goes like this ...
function printReview(widget) {
var review = app.models.Review.getRecord(widget.datasource.item._key);
var templateId = 'templateId';
var filename = 'Review for ...' + new Date();
var copyFile = DriveApp.getFileById(templateId).makeCopy(filename);
var copyDoc = DocumentApp.openById(copyFile.getId());
var copyBody = copyDoc.getBody();
var fields = app.metadata.models.Review.fields;
for (var i in fields) {
var text = '$$' + fields[i].name + '$$';
var data = review[fields[i].name];
copyBody.replaceText(text, data);
}
copyDoc.saveAndClose();
}
As Morfinismo noticed you are getting the error because you are trying to pass complex object from client to server and serializer fails to handle it. In order to fix that you need to adjust your code:
// onClick button's event handler (client script)
function onPrintClick(button) {
var reviewKey = button.datasource.item._key;
google.script.run
.withSuccessHandler(function() { /* TODO */ })
.withFailureHandler(function() { /* TODO */ })
.printReview(reviewKey);
}
// server script
function printReview(reviewKey) {
var review = app.models.Review.getRecord(reviewKey);
...
}

How to use parallel async in meteor

I am having three functions to fetch screenshots, yelp and google data of a website.The result of these three functions are pushed to an array of subdocuments which will get inserted to database.I need to increase the performance of this api.Is it possible to call these functions using parallel async in meteor without using npm module?
The line of code I used is shown below
Meteor.methods({
insertApart : function(apart){
var google_data = setGoogleData(apart);
var screen_captures_data = setScreenShots(apart);
var yelp_data = setYelpData(apart);
function setGoogleData(apart) {
// code to fetch google data
}
function setScreenShots(apart) {
// code to fetch screen shots
}
function setYelpData(apart) {
// code to fetch yelp data
}
var data=[];
data.google = google_data;// setting google data
data.screen_captures = screen_captures_data;// setting screen captures
data.yelp = yelp_data;// setting yelp data
var id = Apartments.insert(data);
return id;
}
});

Is there a way to attach callback what fires whenever a crossfilter dimension filter changes?

I have several charts built with dc.js. I can achieve the desired functionality by attaching a callback to each dc.js chart's .on("filterted", function(chart) {}) but this is annoying because I have to attach the same callback to each chart. And error prone because as new charts are added, someone has to remember to attach an event hander. I would prefer to just attach a callback to the underlying crossfilter. Is that possible?
Is there a way to optimize this...
var ndx = crossfilter(data);
var dimAlpha = ndx.dimension(function(d) {return d.alpha});
var dimBeta = ndx.dimension(function(d) {return d.beta});
var groupAlpha = dimAlpha.group().reduceSum(function(d) {return 1;});
var groupBeta = dimBeta.group().reduceSum(function(d) {return 1;});
dc.pieChart(myDomId1)
.dimension(dimAlpha)
.group(groupAlpha)
.on("filtered", function(chart) {
//do stuff
});
dc.pieChart(myDomId2)
.dimension(dimBeta)
.group(groupBeta)
.on("filtered", function(chart) {
//do stuff
});
into something like this...
var ndx = crossfilter(data);
var dimAlpha = ndx.dimension(function(d) {return d.alpha});
var dimBeta = ndx.dimension(function(d) {return d.beta});
var groupAlpha = dimAlpha.group().reduceSum(function(d) {return 1;});
var groupBeta = dimBeta.group().reduceSum(function(d) {return 1;});
dc.pieChart(myDomId1)
.dimension(dimAlpha)
.group(groupAlpha);
dc.pieChart(myDomId2)
.dimension(dimBeta)
.group(groupBeta);
ndx.on("filtered", function() {
//do stuff
})
If you've got a million charts and don't want to have to attach the event listener to each one manually, you could iterate through the chart registry and add them that way. Ex:
dc.chartRegistry.list().forEach(function(chart) {
chart.on('filtered', function() {
// your event listener code goes here.
});
});
Note that this code must go after the charts have instantiated to work.
In the absence of a way to attach the callback once globally, one thing you could do to mitigate the risk from duplicate code is to define the callback function once and pass in a reference instead of defining it inline on each chart.
function my_func() {
// do stuff
}
dc.pieChart(myDomId2)
.dimension(dimBeta)
.group(groupBeta)
.on("filtered", my_func);
chart and filter can also be passed to the filter function something like:
function my_func(chart,filter) {
// do stuff
}
dc.pieChart(myDomId2)
.dimension(dimBeta)
.group(groupBeta)
.on("filtered", my_func);

Can I pass a function closure (with my parameters) as Firebase's set() method 'oncomplete' argument?

I want to do further processing depending on the success or failure of the set() method, but I need the context of some objects at the time I call the set() method. Otherwise my objects will be out of scope when the oncomplete function is called unless I put them in global - which I don't really want to do.
Here is an example:
function oncomplete_AddTran(tran,client,appt,balance){
/* if named argument 'balance' exists it is safe to assume
Firebase has not 'stepped on' the arguments with it's single
Error object or null */
if(balance typeof object) console.log("my parameters made it here");
}
function addTran(tran, client, appt, balance) {
var TRANS_LOCATION = 'https://xxx.firebaseIO.com/testing/transactions';
var tranListRef = new Firebase(TRANS_LOCATION);
var oncomplete = function() {
oncomplete_AddTran(tran, client, appt, balance); };
var tranref = tranListRef.child(tran.name).set(tran.literal, oncomplete);
}
Yes, it is possible. I am too impatient waiting for the confirmation I was looking for and decided to test myself. Here is the code I used (that works for my purpose):
function oncomplete_AddTran(tran,client,appt,balance){
console.log("arguments passed:" + arguments.length);
// firebase original arguments :: arguments.callee.caller.arguments
var fbargs = arguments.callee.caller.arguments;
}
function addTran(tran, client, appt, balance) {
var TRANS_LOCATION = "https://xxx.firebaseIO.com/testing/transactions";
var tranListRef = new Firebase(TRANS_LOCATION);
var oncomplete = function() {
oncomplete_AddTran(tran, client, appt, balance); };
var tranref = tranListRef.child(tran.name).set(tran.literal, oncomplete);
}
function main() {
var tran = {}; tran.name = "test1"; tran.literal = { tran: "tran" };
var client = {}; client.literal = { client: "client" };
var appt = {}; appt.literal = { appt:"appt" };
var balance = {}; balance.literal = { balance:"balance" };
addTran(tran,client,appt,balance);
}
The arguments were passed as expected but I still don't know how Firebase's set() method will pass the error object to the callback (in the event of an error) because I haven't tried reproducing an error and don't really know if I can.
The default null, and another (undefined) that is supposed to be passed when there is no error is not found in arguments.callee.caller.arguments (see callback function in example above). I am not sure that what I am doing is good practice - seems a bit hacky to me so I won't accept this answer to the question (yet).

Resources