Meteor reactivity in autorun - meteor

I'm having problems understanding Meteors reactivity.
The helper works perfectly, when a task is added it is rendered in the template.
However the autorun is not working, the tasks are only logged the first time.
Both the helper and the autorun are reactive computations so both should run when the Tasks collection changes?
import { Template } from 'meteor/templating';
import { Tasks } from '../../api/tasks';
import './day.html';
Meteor.subscribe('tasks');
Tracker.autorun(function() {
var tasks = Tasks.find({});
console.log(tasks);
});
Template.day.helpers({
tasks() {
return Tasks.find({});
}
});

According to Meteor docs:
Cursors are a reactive data source. On the client, the first time you retrieve a cursor’s documents with fetch, map, or forEach inside a reactive computation (eg, a template or autorun), Meteor will register a dependency on the underlying data.
With the helper you are iterating on the template so you are registering a dependency, however with Collection.find() you don't. If you try Tasks.find().fetch() or Tasks.find().count() you will see the result printed every time there is a change in the collection, because you now have a dependency, which will trigger a recomputation.

Related

SvelteKit: Pass Data From +Layout.svelte To +page.svelte SPA (static) application

I'm struggling with the latest version of SvelteKit, the docs available only works with SSR, and I'm developing SPA app (static page), so, what is the way to pass data from my +layout.svelte to +page.svelte?.
The documentation says that with load function from page.js (I've already set the SSR=false, and I understood that page.js is for SSR), but that doesn't work with SPA, and if I have the load function from the layout it's seems not work.
Aditionaly I want to trigger a function from my +page.svelte that is in the layout page.
Any ideas?
here my try
//+layout.svelte
<script>
export function load() {
return {
data: { title: 'default title' }
};
}
export let data;
</script>
//+page.svelte
<script>
export let data;
console.log(data.title); //undefined
</script>
the docs says that don't use: <script context="module">, and I don't want to use the store becouse I think that sholud be a better way.
Load functions belong in the accompanying files +layout.js/ts, not on the page. They also do not return a property data, everything returned is the data. See the docs.
If SSR is disabled, you can event return a store that could be modified from the page.
To get a store from the data so it can be used with $-syntax, the data property can be destructured on the page:
export let data;
$: ({ title } = data);
You could also create a store and set it as a context in the layout. Pages then can get said context and interact with it. This allows two-way interaction.
Using a store is necessary if the contents require reactivity (i.e. are changed) and the page or layout needs to update.

How can I change the subscriptions query parameters in react-komposer (meteor) from a child component?

I'm building an app with Meteor using the react-komposer package. It is very simple: There's a top-level component (App) containing a search form and a list of results. The list gets its entries through the props, provided by the komposer container (AppContainer). It works perfectly well, until I try to implement the search, to narrow down the results displayed in the list.
This is the code I've started with (AppContainer.jsx):
import { Meteor } from 'meteor/meteor';
import { composeWithTracker } from 'react-komposer';
import React, { Component } from 'react';
import Entries from '../api/entries.js';
import App from '../ui/App.jsx';
function composer(props, onData) {
if (Meteor.subscribe('entries').ready()) {
const entries = Entries.find({}).fetch();
onData(null, {entries});
};
};
export default composeWithTracker(composer)(App);
App simply renders out the whole list of entries.
What I'd like to achieve, is to pass query parameters to Entries.find({}).fetch(); with data coming from the App component (captured via a text input e.g.).
In other words: How can I feed a parameter into the AppContainer from the App (child) component, in order to search for specific entries and ultimately re-render the corresponding results?
To further clarify, here is the code for App.jsx:
import React, { Component } from 'react';
export default class App extends Component {
render() {
return (
<div>
<form>
<input type="text" placeholder="Search" />
</form>
<ul>
{this.props.entries.map((entry) => (
<li key={entry._id}>{entry.name}</li>
))}
</ul>
</div>
);
}
}
Thanks in advance!
I was going to write a comment for this to clarify on nupac's answer, but the amount of characters was too restrictive.
The sample code you're looking for is in the search tutorial link provided by nupac. Here is the composer function with the corresponding changes:
function composer(props, onData) {
if (Meteor.subscribe('entries', Session.get("searchValues")).ready()) {
const entries = Entries.find({}).fetch();
onData(null, {entries});
};
};
The solution is the session package. You may need to add it to your packages file and it should be available without having to import it. Otherwise try with import { Session } from 'meteor/session';
You just need to set the session when submitting the search form. Like this for instance:
Session.set("searchValues", {
key: value
});
The subscription will fetch the data automatically every time the specific session value changes.
Finally, you'll be able to access the values in the publish method on the server side:
Meteor.publish('entries', (query) => {
if (query) {
return Entries.find(query);
} else {
return Entries.find();
}
});
Hope this helps. If that's not the case, just let me know.
There are 2 approaches that you can take.
The Subscription way,
The Meteor.call way,
The Subscription way
It involves you setting a property that you fetch from the url. So you setup your routes to send a query property to you Component.Your component uses that property as a param to send to your publication and only subscribe to stuff that fits the search criteria. Then you put your query in your fetch statement and render the result.
The Meteor.call way
Forget subscription and do it the old way. Send your query to an endpoint, in this case a Meteor method, and render the results. I prefer this method for one reason, $text. Minimongo does not support $text so you cannot use $text to search for stuff on the client. Instead you can set up your server's mongo with text indexes and meteor method to handle the search and render the results.
See what suits your priorities. The meteor.call way requires you to do a bit more work to make a "Search result" shareable through url but you get richer search results. The subscription way is easier to implement.
Here is a link to a search tutorial for meteor and read about $text if you are interested

Render callback to all templates in meteor blaze

I am forced to assign rendered callbacks to all my templates.
Until 0.9.0 I used to do it like this:
_.each( Template, function( template, name ) {
//...
template.rendered = function() {
//...
};
});
But now, Template is a constructor and not an object, so this method won't work here. Is there any way to pass callback function to all templates or fire function when all templates were rendered using Blaze?
Here is a quick workaround I came up with, iterating over every Template property to find out if it corresponds to a template definition, and if it does, assign the onRendered callback.
// make sure this code is executed after all your templates have been defined
Meteor.startup(function(){
for(var property in Template){
// check if the property is actually a blaze template
if(Blaze.isTemplate(Template[property])){
var template=Template[property];
// assign the template an onRendered callback who simply prints the view name
template.onRendered(function(){
console.log(this.view.name);
});
}
}
});
I don't know what's your use case so there may be better solutions depending on it.
With Meteor 1.2.1 the Template object has an onRendered(hook) function to accomplish an 'all template' onRendered behaviour.
Template.onRendered(function(){
var template = this;
Deps.afterFlush(function() {
console.log("triggering Jquery mobile component creation for "+template.view.name);
$(template.firstNode.parentElement).trigger("create");
});
});
The postponed update via Deps.afterFlush(callback) is optional and subject to your application needs.

How to change the value of a variable on click in meteor

In my meteor app I need to load an array of items corresponding to the item clicked.As I'm new to meteor, I'm held up here.Here is my code.
Template.templatename.events({
'click .showdiv' : function()
{
Template.templatename.vname = function () {
return Db.find();
}
}
Can I set the variable vname dynamically by this code ? This is not working for me.
I think you're misunderstanding the notion of reactivity. A reactive data source will cause any functions which depend on it (including helpers) to rerun when its value is changed, which seems to be the behavior you're looking for here. Instead, you're rewriting the helper function itself every time an item is clicked, which kind of defeats the object of Meteor's reactive data model. Session variables could help:
Template.templatename.events({
'click .showdiv' : function() {
Session.set('vname', Db.find());
}
});
Template.templatename.vname = function () {
return Session.get('vname');
}
If you use an {{#each vname}} block in the templatename template, it will automatically update with the results of the Db.find() query when a .showdiv is clicked. If all you want to do is show the result of that query regardless of whether a click has been registered it would be as simple as:
Template.templatename.vname = function () {
return Db.find();
}
Note that it's still not clear exactly what data you're trying to populate here since the query will return a cursor (which is fine, but you need to loop through it using {{#each ...}} - use findOne if you only want one item), and its contents aren't going to depend on anything intrinsic to the click event (like which .showdiv you clicked). In the former example it will however fail to show anything until the first click (after which you would have to reset with Session.set('vname', null) to stop it showing anything again).

Meteor template.rendered - Why collection is empty?

Why in the following basic example returned collection inside rendered function is empty?
Autopublish is enabled. After the page loads calling command
Coll.find().fetch() inside javascript console returns correct set of entries
Here is the code
t.js
Coll = new Meteor.Collection("coll");
if (Meteor.isClient) {
Template.tpl.rendered = function(){
console.log(Coll.find().fetch()); // <-- This line prints empty array
};
}
if (Meteor.isServer) {
Meteor.startup(function () {
if (Coll.find().count() === 0) {
var f = ["foo","bar"];
for (var i = 0; i < f.length; i++)
Coll.insert({f: f[i]});
}
});
}
And t.html file
<head>
<title>test</title>
</head>
<body>
{{> tpl}}
</body>
<template name="tpl">
Test tpl
</template>
Meteor is built off of a data-on-the wire type structure. This means when the app initially loads the HTML & JS is sent down first and the data later.
You have to use reactivity to check for data changes or check when the subscription to a collection is complete (which entails removing the autopublish package). (You can check out how to move your app to a manual subscription at the docs: http://docs.meteor.com/#publishandsubscribe)
The subscription callback tells you when the data is returned:
Meteor.subscribe("coll", function() {
//Data subscription complete. All data is downloaded
});
A template can also be made reactive (like the way you are doing) but .rendered isnt being called because Meteor first checks to see if a template's html has changed & only if it is different will it change its HTML and call the rendered callback.
What you have as an option here is to 1) use Deps.autorun instead, or
2) I'm not sure why you are using this in your rendered callback but if it is necessary to put it there you need to ensure that the HTML of the template changes, by introducing something into the html from your collection that makes it change when the new data is introduced.

Resources