Reactive behavior in Meteor template - meteor

Could someone clarify how Meteor (Handlebars) templates interact with reactive sources? There is already a lot in the documentation but a more systematic explanation would help.
For instance, it seems the following does not trigger a template re-draw when the Session variable is changed
Template.foo.rendered = function () {
var selectedItem = Session.get('item_selected');
... do stuff ...
}
I don't understand why the Template.rendered event does not react to reactive source changes. I would also like to understand if other events/methods exhibit this special behavior.

The template will re-render when you set the item_selected value with
Session.set("item_selected","value");
This Session hash, beside the name has a reactive dependency similar to Deps.depends. When you change this Session hash the current reactive context will be invalidated and everything will be redrawn/re-rendered (which is called via Session.set).
The .rendered method will then be run where you can use this new value like you do with var selectedItem
For a very detailed videocast on how exactly it works you can have a look at the videos on EventedMind which demonstrate how Session is built and how to make another variable reactive.
http://www.eventedmind.com/posts/meteor-the-reactive-session-object
http://www.eventedmind.com/posts/meteor-introducing-deps

Related

Meteor reactive-var package is missing the equals() method, is this a bug?

I'm learning about reactive programming in Meteor:
https://stephenwalther.com/archive/2014/12/05/dont-do-react-understanding-meteor-reactive-programming
I believe that the idea behind Session.equals(key, value) is to remember an association between the reactive variable and the desired value so that updates only propagate to the surrounding code if the equality changes. That way if we have hundreds of views that depend on the variable, only the old and new views get their update code triggered when the value changes.
Note that this would not be the case if we called Session.get(key) === value because every view's code would be called when the variable changes. This is discussed further under the Session.get versus Session.equals() section of the article.
But I found an inconsistency under the Using Reactive Variables section where it says:
Notice that a reactive variable, unlike the Session object, does not have an equals() method. Yes, that is a shame.
So reactive-var is missing equals() but reactive-dict has ReactiveDict.equals().
I can't really see a conceptual reason to exclude ReactiveVar.equals(). Maybe they had no context for storing the association, or maybe there is some scoping or other issue with Javascript that prevents this that I don't fully understand.
So my question is: is this a bug?
Should I just always use reactive-dict? In which case I would change everything from:
let myReactiveVar = new ReactiveVar();
...
if(myReactiveVar.get() === 'myValue')
To the more verbose (but performant):
let myReactiveDict = new ReactiveDict();
...
if(myReactiveDict.equals('myReactiveVar', 'myValue'))
Which would match the functionality provided by Session.equals().
Another option would be to extend the ReactiveVar prototype with my own equals() method or inherit it in a child class and provide a MyReactiveVar.equals() method. Kudos if someone can provide examples to do either of these workarounds that we could submit as a pull request to the Meteor maintainers.
Update: I forgot to mention that ReactiveVar does take an equalsFunc optional parameter in its constructor. It might be possible to hack that as a reactive code block to partially implement equals() functionality without extending the class. Also, here is a related issue on GitHub.
Update: to save time, here is the relevant source code for ReactiveVar and ReactiveDict.equals(). I believe that the value parameter gets converted to serializedValue and is then added as a dependency in ReactiveDict, but I still don't see why it wouldn't be possible to do something similar for ReactiveVar.
The reason there's no equals method for ReactiveVar is because set only invalidates the computations is the new value differs from the current value.
Sets the current value of the ReactiveVar, invalidating the Computations that called get if newValue is different from the old value.
const example = new ReactiveVar(0);
Tracker.autorun(() => {
console.log(example.get());
});
example.set(1); // logs 1
example.set(0); // logs 0
example.set(0); // doesn't log
This is similar behaviour to ReactiveDict's equals method.
Note that set on ReactiveDict does not behave this way. Calling set broadcasts that the value has changed. If you want to prevent the computation from invalidating, that is when you would use equals.
Set a value for a key in the ReactiveDict. Notify any listeners that the value has changed (eg: redraw templates, and rerun any Tracker.autorun computations, that called ReactiveDict.get on this key.)

Reactive Method call in meteor is not Reactive

we use ReactiveMethod calls inside helpers in meteor.But most of the time without refreshing the browser it show the previous data(if the passed parameters are not changed). What is the solution for this?
inside helpers I use below method
customerOutsTanding: function(){
return ReactiveMethod.call("outstanding",customerId);
}
outstanding will pay using boostrap model input text and it will close.But using reactive method calling it will not update.after refreshing the browser it will get update
ReactiveMethod.call is only called again when the parameters changes. In your case parameter iscustomerId which is not getting changed since its not a reactive data source.
You probably need to use Tracker.Dependency (https://docs.meteor.com/api/tracker.html#tracker_dependency) to reactively trigger this function again. Something like this
var outstandingDep = new Tracker.Dependency;
customerOutsTanding: function(){
outstandingDep.depend();
return ReactiveMethod.call("outstanding",customerId);
}
Then when you want to change the dependency which is not clear from the question you need to call outstandingDep.changed() may be you need to call when the input text changes.

Meteor - Making DOM Changes from Helpers

Is it a good practice to make DOM changes from Meteor helpers? I'm currently relying on a javascript function to run inside the Meteor helper which makes the function run every time a collection data change occurs.
I know there is Tracker.autorun() but as far as I know, Tracker.autorun() only works for Session variables and does not work for collection data changes.
My current ways so far has not failed me or caused any problems but I'm not 100% sure if this was how Meteor was meant to be used.
Code Example
Template.page_body.helpers({
orange: function() {
do_some_rand_function()
return this.name
}
})
This code will make sure that do_some_rand_function() is ran every time this.name changes (this.name is a variable gotten from a Mongo Collection, therefore it is reactive).
No. Helpers should not have side effects (such as manually updating your DOM, modifying the database, making an HTTP request, etc.).
Your description sounds like a good use case for adding a template autorun in the rendered callback. All autoruns are reactive computations so they will rerun if any reactive variable used within them changes (Session, Meteor.user, Collections, etc.).
Give something like this a try:
Template.myTemplate.onRendered(function() {
this.autorun(function() {
if (MyCollection.findOne()) {
do_some_rand_function();
}
});
});
Also note that template autoruns are automatically stopped when the template is destroyed.

In Meteor, how to find which session variable triggers a template rerun

This is a general question about designing meteor applications or debugging meteor applications.
When I write meteor applications, I usually update session variable values to trigger re-runing a template helper function and/or re-rendering a template. So my application has quite a few different session variables.
Sometimes I find that the helper function gets re-run multiple times, but I can't think of any reason why it re-runs so many times. It must be some session variable gets updated and causes the re-run. Is there a way to figure out which session variable causes it?
The general question is: in a reactive design, when I see a template gets re-rendered, how to find why it gets re-rendered?
You could use Deps.autorun to quickly figure out which it is, If you're looking to debug its a quick a gritty way to do
Drop in the code like
Deps.autorun(function() {
Session.get("something");
console.log("Session something has changed");
});
Deps.autorun(function() {
Meteor.user()
console.log("Meteor user has changed");
});
You can place blocks of code like this on your client side to see which is changing. Each one will run once, initially, then after for each time the reactive variable inside it changes.
You would have to do this for each variable you use in your template and it would help you find out which is changing, each Deps.autorun block will run independently only when the variable inside it changes.

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.

Resources