May 10, 2021 Meteor
Although it is rare to need to write your own code to track dependent variables, it is necessary to understand the workflow for dependent variables.
Imagine that we now need to track the number of Facebook posts on Microscope that current users' Facebook friends are "like". L
et's assume that we've solved the problem of Facebook user authentication, used the correct API calls, and parsed the data.
We now have an asynchronous client function that returns the number
getFacebookLikeCount(user, url, callback)
It is important to remember that this function is very non-responsive and non-real-time. I t initiates an HTTP request to Facebook, gets some data, and then returns it to our app as a callback function parameter. But if the like number changes and the function doesn't run again, we won't be able to get the latest data on our interface.
To solve this problem, we first
setInterval
to call this function every few seconds:
currentLikeCount = 0;
Meteor.setInterval(function() {
var postId;
if (Meteor.user() && postId = Session.get('currentPostId')) {
getFacebookLikeCount(Meteor.user(), Posts.find(postId).url,
function(err, count) {
if (!err) {
currentLikeCount = count;
}
});
}
}, 5 * 1000);
Any time we check
currentLikeCount
variable, we expect to get accurate data within 5 seconds. W
e are now using this variable in the help method.
The code is as follows:
Template.postItem.likeCount = function() {
return currentLikeCount;
}
However, we can't
currentLikeCount
time the currentLikeCount changes.
Although the variable itself can now be pseudo-real-time, it is
not responsive so
it cannot communicate correctly with the rest of the Meteor ecosystem.
Meteor's responsiveness is controlled by dependency, which is a data structure that tracks Computation.
As we've seen in the Responsive section before, a computation is a piece of code used to process responsive data. I
n our example, there is a computation implicitly built
postItem
as a template.
Each help method in this template has its own computation.
As you can imagine, this computation is a piece of code that focuses exclusively on responsive data.
When the data changes, the computation notifies
invalidate()
and it is the computation that determines what needs to be done.
Putting the
currentLikeCount
into a responsive data source, we need to track all the computations that depend on the variable. This needs to change it from a variable to a function (a function with a return value):
var _currentLikeCount = 0;
var _currentLikeCountListeners = new Tracker.Dependency();
currentLikeCount = function() {
_currentLikeCountListeners.depend();
return _currentLikeCount;
}
Meteor.setInterval(function() {
var postId;
if (Meteor.user() && postId = Session.get('currentPostId')) {
getFacebookLikeCount(Meteor.user(), Posts.find(postId),
function(err, count) {
if (!err && count !== _currentLikeCount) {
_currentLikeCount = count;
_currentLikeCountListeners.changed();
}
});
}
}, 5 * 1000);
We built a
_currentLikeCountListeners
which tracks all computations that use
currentLikeCount()
When
_currentLikeCount
changes, we notify all computations data of the change by calling the dependent
changed()
function.
These computations can continue to process the following data changes.
You might think it's like a lot of references on a responsive data source, and you're right, Meteor provides a lot of tools to make the job simple (you don't need to call computations directly, they run automatically). T
here's a package
reactive-var
and that's exactly what
currentLikeCount()
does.
Let's join this package:
meteor add reactive-var
Use it to simplify our code a bit:
var currentLikeCount = new ReactiveVar();
Meteor.setInterval(function() {
var postId;
if (Meteor.user() && postId = Session.get('currentPostId')) {
getFacebookLikeCount(Meteor.user(), Posts.find(postId),
function(err, count) {
if (!err) {
currentLikeCount.set(count);
}
});
}
}, 5 * 1000);
Now with this package, we call
currentLikeCount.get()
and it will work as before.
There is another useful
reactive-dict
which provides key-value storage
Session
Angular is a client-responsive library developed by Google guys. L et's compare how Meteor and Angular rely on tracking. They are implemented in very different ways.
We already know that Meteor uses some code called comptations to implement dependency tracking. T
hese computations are tracked by special "responsive" data sources (functions) that mark themselves as invalidate as the data changes. W
hen the
invalidate()
to be called, the responsive data source, displayed, notifies all dependencies.
Note that this is the general case when data changes, and data sources can trigger invalidation for other reasons.
In addition, although computations usually simply re-run when the data is invalidate, you can specify any behavior you want at this point. These give users high responsive control.
In Angular, responsiveness is
scope
by the scope object.
A scope can be seen as a normal js object with some special methods.
When your responsive data depends on a value in scope,
scope.$watch
to tell the expression what data you care about (for example, what data you care about in the scope) and a listener that runs every time the expression changes.
So you need to show what you have to do when the expression data changes.
Going back to the previous Example of Facebook, our code can be written as follows:
$rootScope.$watch('currentLikeCount', function(likeCount) {
console.log('Current like count is ' + likeCount);
});
Of course, just as you rarely need to set up computations in Meteor, in Angular you don't have to show that calling
$watch
ng-model
{{expressions}}
and then they handle re-showing when the data changes.
When the responsive data changes,
scope.$apply()
is called.
He recalculates all watchers in scope, and then calls only the listener method of the watcher where the expression value changes.
So
scope.$apply()
very similar to the
dependency.changed()
that it operates at the scope level, rather than giving you control to decide which listener needs to be re-evaluate.
In other words, less control allows Angular to decide which listeners need to be re-evaluated in a smart and efficient way.
In Angular, our
getFacebookLikeCount()
like this:
Meteor.setInterval(function() {
getFacebookLikeCount(Meteor.user(), Posts.find(postId),
function(err, count) {
if (!err) {
$rootScope.currentLikeCount = count;
$rootScope.$apply();
}
});
}, 5 * 1000);
It must be admitted that Meteor has done most of the heavy lifting of responsive work for us, but hopefully, learning from these patterns will help you with your in-depth research.