is there a Collection Subscription Inheritance passed onto child templates - meteor

Consider below hierarchy of templates,
'A' has 2 template 'B' and 'C'.
'B' has 2 templates 'D' and 'E'.
my question is whether the meteor collection subcription made in template 'A' is available in the helper for template 'E' since 'A' has 'B' and 'B' has 'E'?
NOTE: if YES/NO why?

will that data be available? probably.
if template A is setting up the sub in its onCreated(), and attached to "this", then that means the sub will go away when the template is destroyed.
and if the sub-template is set up like this:
<template name="A">
{{> B}}
</template>
... template A is not destroyed while B is rendered.
this means that template B should be able to do a find() in minimongo and see that data that A subscribed to.
personally, i prefer to pass down from A to B any data that A "owns" that B uses. but that works better in a non-reactive scenario. if B needs to find() data and expects it to be reactive, then i'll typically have B also subscribe to that data. imho, it's more clear in that case to have B also subscribe, because if it's taken out of the A context, it will stop working.

The data is available as a helper in the child templates if you pass the data to them.
You can do:
{{> childTemplate post=post}}
And then
<template name="childTemplate">
{{post.title}}
</template>

The subscription data is bounded to the scope you subscribe to.
You can Template.instance().subscribe('subscription_name'). The data will then be available to the Template instance.
Also also can Meteor.subscribe('subscription_name'). The data will then be scoped to the Meteor environment. In this case the will data be accessible from the child templates as well. This isn't recommendable for sensitive data, though.
Last alternative is, like in Afifs answer, to give the child template the data with a parameter, which is effective and neat.

Related

Meteor: Call template helper from other template

I have two templates and I've defined JavaScript helpers and events to go with each. When a button is clicked in template A, one of the things I want to do is call a helper function for template B which will change what's displayed on the screen. Is this possible?
If it's not possible, I'd instead like to reload template B.
How can I do either of these? Do I use Tracker.autorun? Reactive variables? Ideally I would do, inside an event function for template A,
B.helpers.call("helperFunctionFromTemplateB");
There are a lot of solutions to what I think you want to achieve, but the answer really depends on context.
If template A is a child template of B:
You can pass a reference to a ReactiveVar in the parent template down to the child template's data context and modify it using {{>childTemplate reactiveVar=reactiveVar}} where reactiveVar is a helper in the parent template that returns the reference to the reactive variable
If the thing you want to change is in the parent's data context, you can use Template.parentData(n) where nis the amount of levels you want to jump up. While modifying the parent's data may not immediately seem reactive, you can make the data prop reactive by accessing it via Template.currentData()
Use some kind of globally accessible state. The most common answer would probably be to use the Session package and use Session.get('var') and Session.set('var', val).
Use an event emitter. This approach gets +'s for decoupling and reusability, but it's also potentially heavy handed if you only need to modify this variable in one place from one source (i.e. your requirements are simple)
Meteor 1.3 - If you want to make references to your reactive data in multiple places but don't want to create a global like Session, use a ReactiveVar or Reactive Dict (closer to session), create your variable where it makes sense, export it, and import it in your templates/anywhere else to be used.
There's a lot of other solutions, these are just the first that come to mind. If you provide more specific context, I'll provide a code sample of what I think's best and explain why. :)

Adding Elements To Collections and Reactivity

What is the intended reactive behaviour when adding a document to a published collection? More specific: does adding a document to a collection invalidate all subscriptions to that collection (even those that are not matching) and result in a re-rendering of all dependent ui elements?
That's what I'm experiencing right now, at least.
To be even more specific: I have forms, with some "normal" fields but also lists. Those lists contain subforms which contain fields as well. Both types of forms are stored in a single generic Data collection. The view on this Data collection is managed with fine-grained subscriptions.
When I add a new list element, a new subform to the base form, the whole base form is re-rendered. Even none related base forms are re-rendered.
I have a github repo showing this, a little obfuscated though. It's a movie database. You can add a movie, give it a name, a tagline and add actors to the movie. An actor has a name and a home. When adding an actor to a movie every movie is re-rendered.
https://github.com/Crenshinibon/fields3/tree/462a9291bfc400a2731c21d2debdd4071be764ed
I'm aware of this question: Understanding Meteor Publish / Subscribe and think this part is actually missing. (I just don't have enough reputation points to ask in the comments) I understand the Pub/Sub mechanism of Meteor pretty well, I think. Just the resulting reactive behaviour is a little bit unclear.
I'm also aware of this question: Reactivity, isolation, and lists. But it's for Spark and Blaze changed a lot (especially, it made #isolate obsolete.)
And before I rebuild my app (again) to put all kinds of forms (or even properties) in different collections to avoid the "whole page" of being reloaded, I thought I might ask and maybe there is something I'm missing.
I'm on 0.9.0-rc10.
You should not put the subscribe calls in Meteor.autorun.
Meteor.autorun is a reactive context, meaning everything inside will get re-run if a reactive data source changes inside. We’re storing the channel we’re in inside the Session under "current_channel". If that session value changes, then the subscription is renewed and we have access to different messages!
src
Instead do something like this in your javascript:
Template.viewContent.movies = function () {
var subscription = Meteor.subscribe('movies');
return {
data: Movies.find(),
ready: subscription.ready
};
}
Then your template will look like this:
{{#each movies}}
{{#if ready}}
{{#each data}}
#DOSOMETHING WITH THE MOVIES#
{{/each}}
{{else}}
!!!Loading indicator here!!!
{{/if}}
{{/each}}

Render a meteor reactive variable from a database field

If I have a field in my DB that contains a string like "Hello {{currentUser}}," is there a way to allow that value to retain reactivity when rendered into a template? I am also looking to see if I can somehow inject my own variable into the output by running it through a helper and handling string replacement.
Thoughts?
One solution I've come up with thus far:
The message stored in the db is something like: "Hello, [user], how are you?"
I then render the message from the DB as usual with {{#each}} and a predefined template.
When the message is actually rendered, I pass it through a helper. The helper replaces all []'s with <span class="$1"></span> so that I can target each item directly.
Once the message template's rendered() is called, I know that the message body contains the cleaned and prepped content (with the spans), so I use this.$('.user').each() and loop over each instance of the spans.
I've also created a special template in my page called 'placeholderUser' that only contains a call to {{user}}. I've added Template.placeholderUser.user = function(){} to the code to pull through a value and maintain reactivity.
Whew! Now that I have the structure set up, when looping through in the "each," I can call:
UI.insert(UI.render(Template.placeholderUser), el), which will render the template in the given span and maintain all reactivity.
It's super hacky, but it works. Any other, better, solutions out there?

Collection.findOne() does not return any results in Template.created

I have a template which is a simple edit form. The _id of the document to be edited comes in a session variable (set by mini-pages from the URL: http://example.com/items/4zt4z3t3t). In the Template.editForm.createdfunction I try to get the correponding document from the collection using ItemCollection.findOne({_id:_id}). The _id is set correctly in all cases.
When I navigate to http://example.com/4zt4z3t3t and debug the created function, ItemCollection.findOne()returns undefined, although there are items in the collection. Therefore I can never find my item by _id. Also, when I move the item find procedure to the routing stage, there is also no result for the find. Later on, the colleciton works as expected.
Any pointers?
Meteor uses a Data on the wire principle. This means when your HTML has loaded your data isn't sent along with it, at least initially.
You can't therefore access data in a .created function, unless you expect that template to be loaded after the data has been downloaded. This is why it returns undefined initially but if you check later its there.
You can either, wait for a callback from the subscription on when the data is completed, then load the template.
OR
Use reactivity in your template and let it load empty, and automatically fill it up with data when the data has come in (really the easiest). Access your data in the template and use a handlebars helper to fill in the data and use the .rendered callback to do any changes after.

Accessing other templates' instances

You can access the current template's instance by doing Template.instance(). But you often run into situations where you have to access other templates' instances as well. For example, if you use ReactiveVar, then you would want to get or set variables that are attached to other template instances.
I came across How to get the parent template instance (of the current template) but this is not complete.
Q1. How can we access any template's instance, not just the current template's
Q2. Is it against the Meteor way if I need to access other templates' instances?
you can try to set your template variable directly at the template level instead of inside the instance.
Template.example.myVariable = new ReactiveVar();
instead of
Template.example.onCreated(function (){
this.myVariable = new ReactiveVar();
});
The closest I got was to target the template by one of its elements (assume the template contains a form)
Blaze.getView($('form')[0]).templateInstance().someReactiveVar.set('changed')
If your target templates are in the same file, you can just define the reactive variable outside the template functions, at the beginning of the file. All templates in the file will access it.
If your target template is the parent template, (or any further parent template) you can access its data context using Template.parentData() the argument being the rank of the parent (default is 1). It seems that you know that already.
If you need to access a DOM element within a different template in the same page, you can use jQuery selectors.
I don't know any other way to reach another template instance (afaik, there is no Blaze.getTemplate(name) function.) The answer you are referring to seems to be the better you can get.
I think this is purely subjective, since in Meteor there are so many different ways of doing things, but I actually think Session is perfectly suited for sharing variables across several templates. People argue that Session is bad since it's global and can pollute the namespace. I would argue that it's up to the developer to keep their environment clean in any way that works for them. So for instance, this is bad:
Session.set('count', 23);
Session.set('last', new Date());
But this is better:
Session.set('notifications/count', 23);
Session.set('notificatinos/last', new Date());

Resources