Using Meteor.renderList inside a template throwing errors - meteor

i tried to use Meteor.renderList to render some sort of chat messages. I use the Template.foo.rendered callback method to add the domFragment to a list.
Template.foo.rendered = ->
list = this.find "ul"
list.appendChild fragmet
If I place the list inside <template name="foo"> Meteor throws errors in an endless loop/recursion.
Uncaught Error: LiveRange start and end must have a parent
I have to use another template thats not directly connected to foo. Appending the list from within the foo#rendered callback than works as expected.
Template.foo.rendered = ->
document.getElementById("foo").appendChild fragmet # element with id foo not part of template foo
I think, that there is a problem with the reactive contextes used by the template and renderList? Anyone knows if this is a bug or the expected behavior? I looked into the sources but was lost in there quite fast ;)
Thanks!

This makes sense because you are appending an element to the template when the template is rendered. So, every time the template is rendered, it appends an element to itself, causing it to re-render itself, ad infinitum.
Are you sure you want Meteor.renderList to render your list rather than using an {{each}} iterator in the template itself?

Related

What the proper way to avoid console warning, when meteor collection is not ready

Every time I refresh the page I receive the following console warning for every single helper that is returning something to template from collection. I know the reason is because the subscription is not ready yet, but what is the solution?
Exception in template helper: TypeError: Cannot read property 'x' of undefined.
I'm already using if(collection.find({}) !== undefined) , but this makes my codes so messy, there must be a way to fix this issue. then I tried guards and still not 100% solved.
In addition to Brendan's answer, using Blaze you can check if the subscriptions for the template is ready using
this.subscriptionsReady()
Which checks all the subscriptions scoped to the template with
this.subscribe()
in your onCreated or onRendered blocks
Meteor.subscribe returns a handle with a reactive method called .ready(). You can use that in your helper to only return the mongo cursor once it's ready.
Edit: docs

Using a date-time-picker in react and meteorjs

I am trying to place a date time picker in my form. However, I get an error that says Invariant Violation, Target is not in the DOM. I am very new to javascript as well as meteor and react. What does this error mean and how can I fix it?
Here is the code: https://gist.github.com/drwofle21/8cffed99312711ddfb3f
As the comment on the Gist said "document.getElementsByClassName("calendar") Will return an array, not single node."
That array contains a list of nodes that you can loop through. Best thing to do is console log it out and see what it's actually returning and how you can best handle it.

How can I make a reactive array from a Meteor collection?

I want to take a list of item names from a collection as a simple array to use for things like autocompleting user input and checking for duplicates. I would like this list to be reactive so that changes in the data will be reflected in the array. I have tried the following based on the Meteor documentation:
setReactiveArray = (objName, Collection, field) ->
update = ->
context = new Meteor.deps.Context()
context.on_invalidate update
context.run ->
list = Collection.find({},{field: 1}).fetch()
myapp[objName] = _(list).pluck field
update()
Meteor.startup ->
if not app.items?
setReactiveArray('items', Items, 'name')
#set autocomplete using the array
Template.myForm.set_typeahead = ->
Meteor.defer ->
$('[name="item"]').typeahead {source: app.items}
This code seems to work, but it kills my app's load time (takes 5-10 seconds to load on dev/localhost vs. ~1 second without this code). Am I doing something wrong? Is there a better way to accomplish this?
You should be able to use Items.find({},{name: 1}).fetch(), which will return an array of items and is reactive, so it will re-run its enclosing function whenever the query results change, as long as it's called in a reactive context.
For the Template.myForm.set_typeahead helper, you might want to call that query inside the helper itself, store the result in a local variable, and then call Meteor.defer with a function that references that variable. Otherwise I'm not sure that the query will be inside a reactive context when it's called.
Edit: I have updated the code below both because it was fragile, and to put it in context so it's easier to test. I have also added a caution - in most cases, you will want to use #zorlak's or #englandpost's methods (see below).
First of all, kudos to #zorlak for digging up my old question that nobody answered. I have since solved this with a couple of insights gleaned from #David Wihl and will post my own solution. I will hold off on selecting the correct answer until others have a chance to weigh in.
#zorlak's answer solves the autocomplete issue for a single field, but as stated in the question, I was looking for an array that would update reactively, and the autocomplete was just one example of what it would be used for. The advantage of having this array is that it can be used anywhere (not just in template helpers) and that it can be used multiple times in the code without having to re-execute the query (and the _.pluck() that reduces the query to an array). In my case, this array ends up in multiple autocomplete fields as well as validation and other places. It's possible that the advantages I'm putting forth are not significant in most Meteor apps (please leave comments), but this seems like an advantage to me.
To make the array reactive, simply build it inside a Meteor.autorun() callback - it will re-execute any time the target collection changes (but only then, avoiding repetitive queries). This was the key insight I was looking for. Also, using the Template.rendered() callback is cleaner and less of a hack than the set_typeahead template helper I used in the question. The code below uses underscore.js's _.pluck() to extract the array from the collection and uses Twitter bootstrap's $.typeahead() to create the autocomplete.
Updated code: I have edited the code so you can try this with a stock meteor created test environment. Your html will need a line <input id="typeahead" /> in the 'hello' template. #Items has the # sign to make Items available as a global on the console (Meteor 0.6.0 added file-level variable scoping). That way you can enter new items in the console, such as Items.insert({name: "joe"}), but the # is not necessary for the code to work. The other necessary change for standalone use is that the typeahead function now sets the source to a function (->) so that it will query items when activated instead of being set at rendering, which allows it to take advantage of changes to items.
#Items = new Meteor.Collection("items")
items = {}
if Meteor.isClient
Meteor.startup ->
Meteor.autorun ->
items = _(Items.find().fetch()).pluck "name"
console.log items #first result will be empty - see caution below
Template.hello.rendered = ->
$('#typeahead').typeahead {source: -> _(Items.find().fetch()).pluck "name"}
Caution! The array we created is not itself a reactive data source. The reason that the typeahead source: needed to be set to a function -> that returned items is that when Meteor first starts, the code runs before Minimongo has gotten its data from the server, and items is set to an empty array. Minimongo then receives its data, and items is updated You can see this process if you run the above code with the console open: console.log items will log twice if you have any data stored.
Template.x.rendered() calls don't don't set a reactivity context and so won't retrigger due to changes in reactive elements (to check this, pause your code in the debugger and examine Deps.currentComputation -- if it's null, you are not in a reactive context and changes to reactive elements will be ignored). But you might be surprised to learn that your templates and helpers will also not react to items changing -- a template using #each to iterate over items will render empty and never rerender. You could make it act as a reactive source (the simplest way being to store the result with Session.set(), or you can do it yourself), but unless you are doing a very expensive calculation that should be run as seldom as possible, you are better off using #zorlak's or #englandpost's methods. It may seem expensive to have your app querying the database repetitively, but Minimongo is caching the data locally, avoiding the network, so it will be quite fast. Thus in most situations, it's better just to use
Template.hello.rendered = ->
$('#typeahead').typeahead {source: -> _(Items.find().fetch()).pluck "name"}
unless you find that your app is really bogging down.
here is my quick solution for bootstrap typeahead
On client side:
Template.items.rendered = ->
$("input#item").typeahead
source: (query, process) ->
subscription = Meteor.subscribe "autocompleteItems", query, ->
process _(Items.find().fetch()).pluck("name")
subscription.stop() # here may be a bit different logic,
# such as keeping all opened subsriptions until autocomplete
# will be successfully completed and so on
items: 5
On server side:
Meteor.publish "autocompleteItems", (query) ->
Items.find
name: new RegExp(query, "i"),
fields: { name: 1 },
limit: 5
I actually ended up approaching the autocompletion problem completely differently, using client-side code instead of querying servers. I think this is superior because Meteor's data model allows for fast multi-rule searching with custom rendered lists.
https://github.com/mizzao/meteor-autocomplete
Autocompleting users with #, where online users are shown in green:
In the same line, autocompleting something else with metadata and bootstrap icons:
Please fork, pull, and improve!

Flex 3: getting .length of custom component id within a repeater... works sometimes, but not all the time

I have a repeater that populates a component, called 'project'. The project components are given an ID of 'wholeProject'. In all of my functions up until now, I was able to determine how many project components were made by doing the following:
wholeProject.length;
I used this in for loops, for each loops, and for changing the item settings within a project, i.e. something like this:
wholeProject[i].studentName = "Billy Bob";
However, I'm creating a new function that does not seem to like this wholeProject.length reference. I'm using it within the same level as all the others (i.e. the parent level). So far, my function is simply this:
public function getStudentYears():void
{
Alert.show(String(wholeProject.length));
}
when the application loads, the alert message simply does not appear. If I change the alert to something like this:
Alert.show("This is just a test.");
it works just fine. But for some reason, the wholeProject.length doesn't work in this function whereas it does in all my other ones. Anybody have any ideas as to why this is happening?
Use your repeater's data provider length instead.

Visibility of elements by ID

how come that when I attach onchange by attribute and call it
onchange="validateDate(FPR_CURR_FROM);"
it works, but when I use a ASP .NET validator, and my attached function is called like :
function anonymous() {
ValidatorOnChange(event);
validateDate(FPR_CURR_FROM);
}
I get error: FPR_CURR_FROM is undefined.
First off: I know that using FPR_CURR_FROM to access element is BAD, and I should use getElementByID etc... And I will change it eventually. But as I bumped into that code, I'm curious what caused it - propably visibility of variables I guess.
I think it's a scoping issue, yes, it would take seeing more code and how anonymous is called, but that is what it looks like to me from what I see... One way around that is to attach the FPR_CURR_FROM variable to the window object, and access it via window.FPR_CURR_FROM...

Resources