Using a date-time-picker in react and meteorjs - meteor

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.

Related

Problem when retrieving uploaded file with Firebase Storage using Redux Saga

I'm using redux saga with firebase in an app. When I need to use the firebase storage and I need to get the download URL of a file I just upload:
this doesn't work
yield call (uploadTask.snapshot.ref.getDownloadURL);
but this work
yield call (() => uploadTask.snapshot.ref.getDownloadURL());
can anyone help me understand why the first option isn't working? I didn't understand the difference between these approaches :)
The difference between those is what value this has once getDownloadURL is running. The first version will have this equal to the window object (in non-strict mode) or undefined (in strict mode), while the latter will have this equal to uploadTask.snapshot.ref.
The call effect does have a couple of overloads which let you specify this. You can see them listed here, but one example is to pass in an array as the first argument, as in:
yield call([uploadTask.snapshot.ref, uploadTask.snapshot.ref.getDownloadURL])

How update an array, client side

I tried to update an array in a collection by doing
Configs.update({_id:this.parent._id, "cles.cle":this.context.cle},
{$set: {"cles.$.alias": $(e.target).val()}});
but I got this error
Uncaught Error: Not permitted. Untrusted code may only update documents by ID. [403]
How can I update data in an array, client side (minimongo) ?
OK, I think I finally understand the basis of your question. It looks like Meteor is treating your subselection of the array element in your selection criteria as an attempt to circumvent the policy of only allowing individual record updates. This feels like a bug in Meteor.
A possible work around:
var cles = Configs.findOne({_id:this.parent._id}).cles;
Modify the cles array as desired
Update the whole array in place Configs.update({_id:this.parent._id},{$set: {cles: cles});
Feels clumsy. Another solution would be to create a server method to do this on the server side. This would be better if your array can be large.
When you call collection.update on the client, the selector may point out docs by their _id field only. So you must do something like collection.update({_id: "your id"}, <updates>}) (or by using the alternative _id selector: collection.update("your id", <updates>})).

Meteor: The Loop of Death

I have noticed something really embarrassing with Meteor. When an error occurs inside a method on the server, this makes the entire code of the method to be rerun. This can lead to some unexpected results and some kind of trouble. For instance:
Meteor.methods
'someMethod': ->
# we insert an element in the database
Collection.insert({ record: "We want this record to be inserted only once." })
# We just made a mistake here, let's say that the object is not defined.
variable = object.property
return variable
What happened in my case is that Meteor throws an error saying that it cannot read the property of undefined (I can see it within the server logs) and then rerun the code of the method repeatedly throwing the same error again. I can see a lot of records inserted on my database when it should have one.
I don't know if there is a way to ask meteor to not rerun the code when an error occurs or some kind of thinking I need to learn while developing an application with it.

Conductor API has no attribute "xxxxxx"

I have been trying to add a new table to the database. Now, I have created
the table and I am going through conductor API to populate/update it. I have
defined some function to achieve the task. There function are defined in conductor
and db api, along with in conductor.manage and db.sqlalchemy.api. When I try
to run the system it says
AttributeError: 'ConductorAPI' object has no attribute 'xxxxxx'
while the function is properly defined in Conductor API as well as manager and
db.API. I back traced the flow of the code and it looks like the code reaches till
conductor.api. The function at the conductor API is defined as:
"""In class LocalAPI"""
def xxxxxx(self, context, node_id, values):
return self._manager.xxxxxx(context, node_id, values)
Now, after this it gives the error. Though the function is present in conductor.manager.
Please help me find out why am I getting this error.
So, I figured it out. You also need to add the interface to the conductor.rpcapi and now it's working fine!

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!

Resources