Meteor has a Session that provides a global object on the client that you can use to store an arbitrary set of key-value pairs. Use it to store things like the currently selected item in a list.
It supports Session.set, Session.get and Session.equals.
How do I delete a Session name, value pair? I can't find a Session.delete(name) ?
[note: this answer is for Meteor 0.6.6.2 through at least 1.1.0.2]
[edit: updated to also explain how to do this while not breaking reactivity. Thanks to #DeanRadcliffe, #AdnanY, #TomWijsman, and #MikeGraf !]
The data is stored inside Session.keys, which is simply an object, so you can manually delete keys:
Session.set('foo', 'bar')
delete Session.keys['foo']
console.log(Session.get('foo')) // will be `undefined`
To delete all the keys, you can simply assign an empty object to Session.keys:
Session.set('foo', 'bar')
Session.set('baz', 'ooka!')
Session.keys = {}
console.log(Session.get('foo')) // will be `undefined`
console.log(Session.get('baz')) // will be `undefined`
That's the simplest way. If you want to make sure that any reactive dependencies are processed correctly, make sure you also do something like what #dean-radcliffe suggests in the first comment. Use Session.set() to set keys to undefined first, then manually delete them. Like this:
// Reset one value
Session.set('foo', undefined)
delete Session.keys.foo
// Clear all keys
Object.keys(Session.keys).forEach(function(key){ Session.set(key, undefined); })
Session.keys = {}
There will still be some remnants of the thing in Session.keyDeps.foo and Session.keyValueDeps.foo, but that shouldn't get in the way.
Session.set('name', undefined) or Session.set('name', null) should work.
The disadvantage with using delete Session.keys['foo'] is that your template will not hot reload if the session key holds an array. For instance, if you are doing
Template.mytempl.helpers({
categories: function() {
return Session.get('srch-categories')
}
})
and in your template
{{#if categories}}
{{#each categories}}
{{this}}
{{/each}}
{{/if}}
And categories is an array, if you delete the session key, your template will continue to display the last value of categories.
Related
In DynamoDb, is it possible to do a conditional put and return the old item if there already was a matching item?
I would like something like the following to create the row if the user does not already exist, and if it does exist, I want it to return the old item (with whatever name was there before). I.e., insert item if new, else just read existing item.
await documentClient.put({
TableName: 'table',
Item: {
userId: 'user0',
name: 'smith',
},
ConditionExpression: 'attribute_not_exists(userId)',
});
Is this possible to do in one call?
Sort of.
If you use UpdateItem it will create or update the item. It only updates the fields that have changed however, so if your fields have not changed, then no update will be made. It will also create the item if you do not have it.
Using Conditional Items for this will return an ErrorCode if the condition fails ConditionalCheckFailedException - which would require an additional operation.
(I don't use the javascript SDK that much, more in python, so I'm not sure this is the correct version/page but here is some documentation:
https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB.html#updateItem-property )
I tried the following code to pass data to a template and receive it in onCreated() but I cannot access the data.
deviceInfo.js:
BlazeLayout.render('layout',{main:'deviceInfo',stats:'paramstats',attr:"SOME_DATA"});
deviceInfo.html:
{{>Template.dynamic template=stats data=attr}}
paramstats.js:
Template.paramstats.onCreated( () => {
console.log("onCreated");
console.log("Data is:",this.data.attr);
});
But I get TypeError: Cannot read property 'attr' of undefined.
where am I going wrong?
You need to use the normal function syntax for onCreated callback. Arrow function will bind the context of your function to the outer scope automatically, it is the cause of your problem. Try this:
Template.paramstats.onCreated(function() {
console.log("onCreated");
console.log("Data is:",this.data.attr);
});
I am using Meteor 1.4.# and I was able to retrieve the parameters like so:
BlazeLayout.render("page", {
params: ['fullscreen', 'route']
});
// page.js
Template.page.onCreated(function() {
let params = this.data.params();
console.log(params);
}
Not quite sure why you're using two levels of indirection. BlazeLayout.render() is giving you one level and then you're using a dynamic template within that? Why not directly render the template you ultimately want using BlazeLayout.render()?
In any case, you're dereferencing your data context indirectly.
In the BlazeLayout.render() call you're setting the attr variable to some value.
Then in your dynamic template you're using data=attr but this means that inside your template helpers that this is going be have the value of attr. There will be no data subkey added automatically.
You don't show the value that you're setting for attr so it's not even clear that you have an attr subkey in your attr variable, that would also be confusing to anyone else who ever tries to debug your code.
#khang is correct about not using the arrow function syntax in onCreated(), try:
Template.paramstats.onCreated(function(){
console.log("onCreated");
console.log("Data is:",this);
});
this should have whatever value you stuffed into attr in your BlazeLayout.render()
I have a collection called "Articles". Each article has a category. I would like to have a global variable being an array with each distinct category value in my Articles collection.
I tried to do it this way:
/models/article.coffee:
#Articles = new Meteor.Collection "articles"
Articles.categories = ->
Meteor.call "articleCategories", (e, r) ->
unless e
return r
/server/article_server.coffee:
Meteor.methods
articleCategories: ->
categories = _.uniq(Articles.find({}, {sort: {category: 1}, fields:
{category: true}}).fetch().map (x) ->
x.category
, true)
return categories
This doesn't work. The result is "undefined" when I call Articles.categories() from the console.
What am I doing wrong?
EDIT:
I want to do this because I want my article categories to be available everywhere in the website.
As Articles collection will not be published on every pages, I tought, I could just generate an array server side and pass it over to the client.
But maybe it's not a good idea...
A Meteor.method will always return undefined on the client (unless a simulation/stub exists and it's called within another parent method) so this behavior is expected.
I'm not sure why you'd need a Meteor.method in this particular use case though, can't you just copy your method code inside your class method ?
EDIT :
To accomplish what you want to do, I'd suggest changing your model to create a Categories collection filled with every possible categories and just publish the entire content to the client.
Then just use a foreign key in your Articles collection.
An added benefit will be that your categories access client side will be reactive, contrary to using a Meteor.method.
Whether it's Telescope or even Wordpress I think this schema is very popular.
Take a look at this package:
https://github.com/dburles/meteor-collection-helpers
And add something like this (I wrote in javascript) in your model:
Articles.helpers({
categories: function(){
return _.uniq(
_.pluck(Articles.find({}, {sort: {category: 1}, fields:
{category: true}}).fetch(), 'category'),
true
);
}
});
I have an app where you can choose (or add if they don't exist!) a superhero/villain character from a certain universe on the first page; then outfit him with weapons, clothes, and gadgets on the second page (build).
I have this route defined:
Router.route('/build/:character', {
name: 'build'
waitOn: Meteor.subscribe('characters', {name: this.params.character})
//and a few other subscriptions and sessions as well for the items
//and stuff, but those don't matter here.
}
The link from the specific character, though, passes along a query as well:
<a href="{{pathFor 'build' query=this.universe}}">
So the final link could look something like this:
/build/Aquaman?DCComics
Now the page you are on will display a list of weapons and gadgets where you could also add other stuff if you so wish. Then you are supposed to drag the items you want to include onto your version of this hero.
Problem is, at this point the app doesn't know you even want to create your own hero. Maybe the user is just looking through them for fun. There's a button that the user has to click first to initialize the creating process, and that's when the actual _id is created, something like this:
Meteor.methods({
buildHero: function(heroCharacterName, heroUniverse) {
var heroToAdd = {}
heroToAdd['characterName'] = heroCharacterName
heroToAdd['universe'] = heroUniverse
heroToAdd['_createdAt'] = new Date()
CreatedHeroes.insert(heroToAdd, function() {
if (! error)
//Update the subscription somehow...
})
}
})
So, the _id that is created here in the new Collection must be passed along to a subscription somehow, because I don't want the user to see other personal heroes that have been created, only his own newly created one.
The solution I have in mind is adding the _id onto the URL in form of a hastag, and use this.params.hash in the subscription like so:
Router.route('/build/:character', {
name: 'build'
waitOn: [Meteor.subscribe('characters', {name: this.params.character}),
Meteor.subscribe('createdheroes', this.params.hash)]
}
First of all, is this a valid approach? If so, how do I accomplish it; how do I actually update the URL to include this hash?
If not, what would be a better approach?
I think you have to handle this logic in the data context or in a template helper and not in the way of subscribing/publishing.
If I was you I would besure that the newly created item is being published and subscribed by the client and modify your search query just that it only adds the newly created item.
I am not sure if I understand your question well but what I got, you will know the last _id which was used on your insert.
Instead of letting done this automatically by meteor, just use the meteor method to create / get that _id value >> see Meteor Documentation
var new_id = new Mongo.ObjectID()
col1.insert({ _id: new_id, ... });
col2.insert({ ..., ref_col1_id: new_id, ... });
Suppose I write:
new Meteor.Collection("foos");
new Meteor.Collection("bars");
Is there an API for accessing those collections by name? Something like Meteor.Collection.get(name), where name is "foos" or "bars"? I know I could write something like
var MyCollections = {
foos: new Meteor.Collection("foos");
bars: new Meteor.Collection("bars");
}
and then use MyCollections[name], but I'd prefer to use an existing API if one exists.
Based on Shane Donelley's mongoinspector
https://github.com/shanedonnelly1/mongoinspector
getCollection = function (string) {
for (var globalObject in window) {
if (window[globalObject] instanceof Meteor.Collection) {
if (globalObject === string) {
return (window[globalObject]);
break;
};
}
}
return undefined; // if none of the collections match
};
I've just found that package : https://github.com/dburles/mongo-collection-instances/
It allow you to
Foo1 = new Mongo.Collection('foo'); // local
Foo2 = new Mongo.Collection('foo', { connection: connection });
Mongo.Collection.get('foo') // returns instance of Foo1
Mongo.Collection.get('foo', { connection: connection });
// returns instance of Foo2
Hope it will help
This feature was added to Meteor in Feb 2016: "Provide a way to access collections from stores on the client"
It works like this:
Meteor.connection._stores['tasks']._getCollection();
And I was using it as follows to test inserts using the javascript console:
Meteor.connection._stores['tasks']._getCollection().insert({text:'test'});
For the insert it required the insecure package to still be installed otherwise got an access denied message.
As far as I can see in the collection.js source there currently is no way in the api to get an existing Collection by name, once it has already been initialized on the server. It probably wouldn't be hard to add that feature.
So, why not fork Meteor and submit a patch or create a smart package and share it I'm sure there are others out there who'd like the same feature.
With https://github.com/dburles/mongo-collection-instances you can use Mongo.Collection.get('collectionname')
Note that the parameter you're inserting is the same one you use when creating the collection. So if you're using const Products = new Mongo.Collection('products') then you should use get('products') (lowercase).
Note that they have a return value, so you can just do
var Patterns = new Meteor.Collection("patterns");
and use Patterns everywhere.
And when you need to subscribe to server updates, provide "patterns" to Meteor.subscribe().
If you have the same code for multiple collections, the chance is high that you're doing something wrong from a software engineering viewpoint; why not use a single collection with a type field (or something else that differentiates the documents) and use that instead of using multiple collections?
Rather than looking, I've just been doing:
Foos = new Meteor.Collection("foos");
or possibly put it inside another object. I haven't really been making a Collections collection object.
It seems there is no way to get at the wrapped Meteor.Collection object without saving it at creation time, as others have mentioned.
But there is at least a way to list all created collections, and actually access the corresponding Mongo LocalCollection object. They are available from any Meteor Collection object, so to keep it generalistic you can create a dummy collection just for this. Use a method as such (CoffeeScript):
dummy = new Meteor.Collection 'dummy'
getCollection = (name) ->
dummy._driver.collections[name]
These objects do have all the find, findOne, update et al methods, and even some that Meteor doesn't seem to expose, like pauseObservers and resumeObservers which seem interesting. But I haven't tried fiddling with this mongo LocalCollection reference directly to knowif it will update the server collection accordingly.
var bars = new Meteor.Collection("foos");
Judging by what the collection.js does, the line we use to instantiate the collection object opens a connection to the database and looks for the collection matching the name we give. So in this case a connection is made and the collection 'foos' is bound to the Meteor.Collection object 'bars'. See collection.js AND remote_collection_driver.js within the mongo-livedata package.
As is the way with MongoDB, whilst you can, you don't have to explicitly create collections. As stated in the MongoDB documentation:
A collection is created when the first document is inserted.
So, I think what you're after is what you already have - unless I've totally misunderstood what you're intentions are.
You can always roll your own automatic collection getter.
Say you have a couple of collections called "Businesses" and "Clients". Put a reference each into some "collections" object and register a Handlebars helper to access those "collections" by collections["name"].
i.e. put something like this on the client-side main.js:
collections = collections || {};
collections.Businesses = Businesses;
collections.Clients = Clients;
Handlebars.registerHelper("getCollection", function(coll) {
return collections[coll].find();
});
Then in your HTML, just refer to the collection by name:
{{#each getCollection 'Businesses'}}
<div> Business: {{_id}} </div>
{{/each}}
{{#each getCollection 'Clients'}}
<div> Client: {{_id}} </div>
{{/each}}
Look ma, no more generic "list all records" boilerplate js required!