Unexpected additional funtion calls in Meteor - meteor

Here is a template to demonstrate the issue. The randomArtist is a function that return a document from mongodb. I call the function 3 times, each time I use a different field from that document.
<template name="featuredArtist">
<p>{{randomArtist.artistName}}</p>
<p>{{randomArtist.description}}</p>
<p>{{randomArtist.randId}}</p>
</template>
Here is a template logic.
On the server start up I initialize the collection with some data, so there should be (I think) available for the View part from the beginning.
On the client I get a random document, then store its id (randId) in session to skip lottery during next function call. (PS. It's not the key, but any improvement suggestions are welcome, I'm the beginner in Meteor.)
Artists = new Mongo.Collection("artists");
if (Meteor.isServer) {
Meteor.startup(function () {
Artists.insert({ randId: 0, artistName: "Artis Name 1", image: "url", description: "Description of Artis Name 1"});
Artists.insert({ randId: 1, artistName: "Artis Name 2", image: "url", description: "Description of Artis Name 2" });
Artists.insert({ randId: 2, artistName: "Artis Name 3", image: "url", description: "Description of Artis Name 3" });
Artists.insert({ randId: 3, artistName: "Artis Name 4", image: "url", description: "Description of Artis Name 4" });
Artists.insert({ randId: 4, artistName: "Artis Name 5", image: "url", description: "Description of Artis Name 5" });
});
}
if (Meteor.isClient) {
Meteor.subscribe("artists");
Template.featuredArtist.helpers({
randomArtist: function(){
var randId = Session.get("artistRandId");
if(!randId){
var rand = (Artists.find().count() * Math.random()) | 0;
var artist = Artists.findOne({randId:{$lte:rand}}, {sort: {randId: -1}});
if (!artist) {
artist = Artists.findOne( { randId : { $gte : rand } } );
}
if (!artist) {
console.log('Mongo.Artists is empty');
}
else {
console.log(artist);
Session.set("artistRandId", artist.randId);
return artist;
}
}
else {
console.log('randId='+randId);
return Artists.findOne( { randId: randId } );
}
}
});
}
Working demo is on http://meteorpad.com/pad/MhwXS6MpXQoTYh4Lw/Artists
What I observe in logs is that the 'randomArtist' function is call 7(!) times instead of expected 3 times as it is invoked in the template:
3 times vs expected 0: console.log('Mongo.Artists is empty');
1 time as expected: console.log(artist);
3 times vs expected 2: console.log('randId='+randId);
Can any explain me where are these additional calls come from?
In my real application I can observe more number of calls - probably because I use more complicated templates, does mean that the featureArtist is embedded inside another ones.
It is also unclear how is it possible that in logs I see that the collection is empty as it seems to be populated at start up?

I can't say why your helper is called exactly 7 times, but you can probably get rid of it by using the #with block helper like this:
<template name="featuredArtist">
{{#with randomArtist}}
<p>{{artistName}}</p>
<p>{{description}}</p>
<p>{{randId}}</p>
{{else}}
<p>Got no random artist :(</p>
{{/with}}
</template>
and remove the artistRandId session from your helper. Your helper will probably get called 2 times now, since the template will probably be rendered before the client receives the artist from the artist subscription (the second times occur when the client has received the artists).

Related

Filters on clustered Marker [HERE maps JS API]

I created a map with many Clustered markers with getClusterPresentation and getNoisePresentation.
my data is set like that :
{
address: "Store 1 address",
brands:["BRAND1", "BRAND4"],
lat: "40.82346",
lng: "5.2345",
name: "Store 1 Name"
},
{
address: "Store 2 address",
brands:["BRAND2", "BRAND4"],
lat: "40.82346",
lng: "5.2345",
name: "Store 2 Name"
},
I need to create a filter by brands to show / hide my markers. I don't know how to do it. How can I access a specific clustered marker with a specific brand (ex : 'BRAND2') and control his visibility.
Thanks for your help
When you create new H.clustering.DataPoint, you can specify optional parameter opt_data which will be associated with the given DataPoint. For more information about DataPoint see: DataPoint class apireference.
Then your noisePoint in getNoisePresentation callback will contain this data and you can access it by calling getData() method.
// assume data is stored in "data" array, create new DataPoints array:
data.forEach(function(obj) {
points.push(new H.clustering.DataPoint(obj.lat, obj.lng, undefined, obj));
})
.
.
.
// now we can work with this data in getNoisePresentation callback:
getNoisePresentation: function(noisePoint) {
let data = noisePoint.getData(),
visibility = data['brands'].includes('BRAND2');
return new H.map.Marker(noisePoint.getPosition(), {
min: noisePoint.getMinZoom(),
visibility
});
}
Here you can find full working example on jsfiddle which hides one noise point (Store 1) if map is zoomed in.

How to create AutoForm field in Meteor to select a record from another collection and save it as a subdocument?

I've been struggling with this for a couple of hours and I can't find a good solution so maybe someone can shed a light on this.
I have a simple schema like this:
var groupschema = new SimpleSchema({
name: {
label: 'Name',
type: String
},
description: {
label: 'Description',
type: String
}
}
And I have another one:
var itemschema = new SimpleSchema({
name: {
label: 'Type:',
type: String
},
details: {
label: 'Details',
type: String
},
group: [groupschema] // <--- this is my main interest
}
If I use the above code sample AutoForm will generate an "inner form" which is quite cool actually for some puporse (e.g. for a contact to have an array of adresses or phone numbers) but for me I would like a drop-down select with the name of the group and when I click on the Insert/Update button (depending on the current form) I would like to add the whole group document to the inserted/updated item document as a subdocument.
Something like this will be inserted to the mongodb:
{
_id: the generated mongoid,
name: "This is a type",
details: "There are some thing to know about this type",
group:{
name: "Cool group",
description: "This is a really cool group"
}
}
The actual documents are far more complicated the above example is just an oversimplified version.
I've stopped writing this question yesterday and tried to do my own version.
My - half baked - solution is:
introducing a new field in the schema named groupselect (type string, autoform type: select)
populate it's contents with a Collection.find().map() lookup
groupselect: {
type: String,
label: 'Group',
optional: true,
blackbox: true,
autoform:{
type: "select",
options : function() {
return Issuegroup.find().map(function (c) {
return {label: c.name , value: c._id};
});
}
}
},
using autoform hooks before insert I assign the subdocument to the real fiel group = Group.find({_id:doc.groupselect}) and I remove the helper field from the doc
using the same technique in the before update hook also for an update form
The problem I seem to be unable to solve in a clean way is to set the default value of the helper field 'groupselect' when the update form displays. I've tried the docToForm hooks but no luck.
Isn't this somehow a very common problem? I imagine there has to be a proper solution for this so I bet that I am missing something very obvious and hopefully someone will point it out for me.
Thanks

I'd like to create a reactive filter for textbox input - best practice?

I have a list of company names I'm populating from a collection
the helper function I have is:
Template.companyList.helpers({
companies: function () {
return Companies.find({owner: Meteor.userId()}, {sort: {a: 1}, name:1, createdAt:1});
}
});
It's looped through using a
{{#each companies}}
which outputs
<LI> Company Name </LI>
Above this I have a text box, and would like to filter the list of companies by what I type in the textbox - I'd prefer to have a "containing" filter as opposed to "starting with" filter, but i'll take either one - is there an established way of doing this in Meteor? If not, is there a plugin that someone wrote that does this?
Also, whatever answer you give, please consider the fact that I've been using Meteor for, oh, 5 days now, and i'm still learning it, so, a Newbie style answer would be great.
Thanks for Reading!
edit
This is the updated answer I came up with - combining David's answer with my previous companies helper:
Template.companyList.helpers({
companies: function () {
var query = Session.get('query');
selector = {owner: Meteor.userId()};
options = {sort: {a: 1}, companyName:1, createdAt:1};
if (query && query.length) {
var re = new RegExp(query, 'i');
selector.companyName = re;
}
return Companies.find(selector, options);
}
});
Here is the outline for a simple search-as-you-type interface:
Template.myTemplate.helpers({
companies: function() {
// build a regular expression based on the current search
var search = Session.get('search');
var re = new RegExp(search, 'i');
selector = {owner: Meteor.userId()};
// add a search filter only if we are searching
if (search && search.length)
selector.name = re;
options = {sort: {createdAt: -1}};
return Companies.find(selector, options);
}
});
Template.myTemplate.events({
'keyup #search': function() {
// save the current search query in a session variable as the user types
return Session.set('search', $('#search').val());
}
});
This assumes:
You are trying to search Companies by name.
You have an input with an id of search.
Please modify as needed for your use case. Let me know if you have any questions.

Publishing/subscribing multiple field selection of the same collection

i have a post collection that contains an embedded array of comments.
in each post view page, i need to publish 2 cursors: one witch contains all posts without their comments field (performance) to show in some widget, and another witch contains the selected post with it's comments. server side code looks like:
Meteor.publish('allPosts', function() {
return Posts.find({}, {fields: {'comments':0}})
})
Meteor.publish('singlePost', function(slug) {
return Posts.find({slug: slug})
})
in post view template, i subscribe for both of these, but when i use
Posts.findOne({slug: slug})
how can i know witch publication cursor is used? how can i choose one?
When you query Posts on the client, you are querying the union of all active subscriptions on that collection. In this case, if you have subscribed to singlePost with that particular slug, you will get the full document. If you have not, you will not get the comments.
Another example:
Meteor.publish("allSummaries", function () {
return Posts.find({}, {fields: {title: 1, date: 1}});
});
Meteor.publish("myPosts", function () {
return Posts.find({creator: this.userId}, {fields: {title: 1, rating: 1}});
});
Meteor.publish("singlePost", function (_id) {
return Posts.find({_id: _id}, {fields: {title: 1, body: 1});
});
Suppose a client with userId "me" is subscribed to allSummaries, myPosts and singlePost (with _id of "3"). The collection contains these documents:
{_id: "1", title: "Post 1", date: "yesterday", creator: "someone else", rating: 3}
{_id: "2", title: "Post 2", date: "today", creator: "me", rating: 4}
{_id: "3", title: "Post 3", date: "5 days ago", creator: "someone else", rating: 2}
For Post 1, the client will only see title and date (and _id) - published from allSummaries - but nothing else. For post 2, they will see title, date, and rating - the date comes from allSummaries, the title from both allSummaries and myPosts, and rating from myPosts. For post 3, they will see title, date and body, but not rating.

Save a value in Collections and find it with Meteor

Values = new Meteor.Collection("values");
if (Meteor.isServer) {
Meteor.startup(function () {
Meteor.setInterval(function() {
Values.insert({id: Math.random(), name: "value"});
console.log(Values.find({name: "value"}).id);
}, 1000);
});
}
I have that code and I'm trying to add a value to Values every second and find all the values I have and print them every second. However, it is not finding the values I add and is outputting:
I2043-14:21:56.895(0)? undefined
find returns a cursor, which is an object that contains the results of your search (somewhat like an array of results). It does it this way since find can get more than just one result, depending on the selector you passed.
It has a forEach similar to JS that accepts a function and receives the document, the index, and the cursor as parameters.
Values.find({name: "value"}).forEach(function(doc,index,cursor){
console.log(doc.id);
});
Visually, a result of find in your case looks something like:
[
{id: SOME_RANDOM_NUMBER, name: "value"},
{id: SOME_RANDOM_NUMBER, name: "value"},
{id: SOME_RANDOM_NUMBER, name: "value"},
{id: SOME_RANDOM_NUMBER, name: "value"},
]

Resources