Meteor routing with a session id - javascript

I'm trying to build a cart system without the user having to login.
I'm using ironrouter
Router.route('/cart', {
template: 'cart',
data: {
cart: function () {
return Carts.find({uid: Meteor.default_connection._lastSessionId}).fetch();
//return Carts.find({uid: "97gxA35vEAS63qsCR"}).fetch();
}
}
})
In my router I've got a cart method, which returns results based on the current session id... Well thats what Im trying to do anyways :)
It doesn't look like
Meteor.default_connection._lastSessionId
Is returning anything, can't think of a reason why though, it works in a client side file.
My routers are located within /lib/routers.js (typo correction)
If you need anymore information please do let me know, thanks in advanced!

I don't think you need to rely on undocumented features like Meteor.default_connection._lastSessionId for that. You can just use a session variable for that to which you assign a random id if it is not yet set:
Meteor.startup(function() {
if (!Session.get('id')) {
Session.set('id', new Mongo.ObjectID()._str);
}
});
Router.route('/cart', {
template: 'cart',
data: {
cart: function () {
if (Session.get('id')) {
return Carts.find({uid: Session.get('id')}).fetch();
}
}
}
});

Related

How to initiate a function using data(){} but not mounted(){} while using vue-resource

This works, but I need to use mounted(){} to initiate the function which I think can be avoided but not sure how.
<script>
export default {
data () {
return {
domains: [],
}
},
methods: {
fetchDomains() {
let _this = this;
api._get({url: 'api/domains'})
.then(function (response) {
_this.domains = response.data;
})
}
},
mounted() {
this.fetchDomains()
}
}
</script>
This code doesn't work, but I like to do something like this. Initiating the function in data(){} itself.
<script>
export default {
data () {
return {
domains: this.fetchDomains(),
}
},
methods: {
fetchDomains() {
let data = [];
api._get({url: 'api/domains'})
.then(function (response) {
data = response.data;
})
return data
}
}
}
</script>
Thanks in advance.
Your first code snippet is the correct way to do it.
You can't initialize domains with the data from the API response because it is an async operation which may or may not be resolved successfully at some point in the future, well after the component is mounted. You might also want to do other things like keeping track of the async operation with a loading property which you set to true for the duration of the request.
Your component will initially be in a loading state which does not yet have any domains data, and you need to account for this. Show a loading spinner or something during this time.
I agree with Decade Moon that your first approach is the better way to do it (though you could use created instead of mounted).
The reason your second approach doesn't work is that you return an array and then replace the local variable's value with a different array. What you need to do is populate the array you returned.
new Vue({
el: '#app',
data() {
return {item: this.getItem()}
},
methods: {
getItem() {
let val = [];
setTimeout(() => {
const result = ['first','second','third'];
val.push(...result);
}, 800);
return val;
}
}
});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.min.js"></script>
<div id="app">{{item}}</div>
I might be deviating slightly from the question (since it explicitly mentions the data property), but I think this might be helpful. Personally, if I want to provide some data with more complex logic I use the computed property. This is great in my opinion and you can read more about it in the docs. The problem in this case is that it doesn't work entirely as expected with asynchronous operations...
However, there is a lovely little module called vue-async-computed which can be found here. It solves this specific problem by providing an asyncComputed property and keeps the code really clean!

Firebase React Binding

I'm somewhat new to React, and using the re-base library to work with Firebase.
I'm currently trying to render a table, but because of the way my data is structured in firebase, I need to get a list of keys from two locations- the first one being a list of user keys that are a member of a team, and the second being the full user information.
The team node is structured like this: /teams/team_id/userkeys, and the user info is stored like this: /Users/userkey/{email, name, etc.}
My table consists of two react components: a table component and a row component.
My table component has props teamid passed to it, and I'm using re-base's bindToState functionality to get the associated user keys in componentWillMount(). Then, I use bindToState again to get the full user node, like so:
componentWillMount() {
this.ref = base.bindToState(`/teams/${this.props.data}/members`, {
context: this,
state: 'members',
asArray: true,
then() {
this.secondref = base.bindToState('/Users', {
context: this,
state: 'users',
asArray: true,
then() {
let membersKeys = this.state.members.map(function(item) {
return item.key;
});
let usersKeys = this.state.members.map(function(item) {
return item.key;
});
let onlyCorrectMembersKeys = intersection(membersKeys, usersKeys);
this.setState({
loading: false
});
}
});
}
});
}
As you can see, I create membersKeys and usersKeys and then use underscore.js's intersection function to get all the member keys that are in my users node (note: I do this because there are some cases where a user will be a member of a team, but not be under /Users).
The part I'm struggling with is adding an additional rebase call to create the full members array (ie. the user data from /Users for the keys in onlyCorrectMembersKeys.
Edit: I've tried
let allKeys = [];
onlyCorrectMembersKeys.forEach(function(element) {
base.fetch(`/Users/${element}`, {
asArray: true,
then(data) {
allKeys.prototype.concat(data);
}
});
});
But I'm receiving the error Error: REBASE: The options argument must contain a context property of type object. Instead, got undefined
I'm assuming that's because onlyCorrectMembersKeys hasn't been fully computed yet, but I'm struggling with how to figure out the best way to solve this..
For anyone dealing with this issue as well, I seemed to have found (somewhat) of a solution:
onlyCorrectMembersKeys.map(function(item) {
base.fetch(`/Users/${item}`, {
context: this,
asObject: true,
then(data) {
if (data) {
allKeyss.push({item,data});
this.setState({allKeys: allKeyss});
}
this.setState({loading: false});
},
onFailure(err) {
console.log(err);
this.setState({loading: false});
}
})
}, this);
}
This works fine, but when users and members state is updated, it doesn't update the allkeys state. I'm sure this is just due to my level of react knowledge, so when I figure that out I'll post the solution.
Edit: using listenTo instead of bindToState is the correct approach as bindToState's callback is only fired once.

Meteor, TypeError: Cannot read property '_id' of undefined. Getting the _id from from a post parameter

I've just started to have a play with meteor and I'm slowly moving a
NodeJS/Express app over to meteor. However on my blog section, I can't seem to get a single post _id into the helper. With NodeJS alone, I could use req.params._id.
Using the Differential boilerplate as a starting poing, (still using insecure and autopublish in dev) I have this:
//both/controllers/post.js
PostController = AppController.extend({
waitOn: function() {
return this.subscribe('posts');
},
data: function () {
return Posts.findOne({_id:this.params._id});
}
});
And in the router file
//both/router/router.js
Router.route('/posts/:_id', {
name: 'post'
});
The links on the main blog listing page, to the single posts are formated as follows.
//client/templates/blog/blog.html
<h3>{{ title }}</h3>
Then the helper file which seems to be the issue?
// /client/templates/post/post.js
Template.post.rendered = function() {
};
Template.post.helpers({
data: function () {
return Posts.findOne({_id:this.params._id});
}
});
Template.post.helpers({
createdAtFormatted: function () {
return moment(this.createdAt).fromNow();
}
});
I've tried this.params._id, request.params.id, req.params._id,
And other than setting an on click event and setting the _id in session (I don't like this idea, if it will even work efficiently/effectively). I'm stuck on ideas.

Meteor collection always returns undefined on client

Im new to meteor and have been trying to learn the framework via the discover meteor book. Im having a few issue understanding what exactly is going on in my application (found here https://github.com/Themitchell/Sound-wav.es).
Essentially, my understanding is that on my server side I allow publications for certain collections which take arguments from my client side subscribe calls. For this part on my server i have this in my server/publications.js file:
Meteor.publish('studios', function (options) {
return Studios.find({
userId: this.userId
}, options);
});
Meteor.publish('studio', function (id) {
return id && Studios.find({
userId: this.userId,
_id: id
});
});
Next we would need a controller to handle the routing and deal with waiting for any subscriptions required, then, once the subscriptions are ready (hence the waitOn) go and render the template providing the data function as a reactive data source for the templates.
So I then set up my 2 routes for indexing studios and showing one studio using iron router and 'Controllers' as follows:
StudiosIndexController = RouteController.extend({
template: 'studiosIndex',
increment: 20,
limit: function () {
return parseInt(this.params.studiosLimit) || this.increment;
},
findOptions: function () {
return {
sort: this.sort,
limit: this.limit()
};
},
waitOn: function () {
return Meteor.subscribe('studios', this.findOptions());
},
studios: function () {
return Studios.find({}, this.findOptions());
},
data: function () {
return {
studios: this.studios()
};
}
});
ShowStudioController = RouteController.extend({
template: 'showStudio',
waitOn: function () {
return Meteor.subscribe('studio', this.params._id);
},
studio: function () {
return Studios.findOne(this.params._id);
},
data: function () {
return {
studio: this.studio()
};
}
});
Router.map(function () {
this.route('studiosIndex', {
path: '/',
controller: StudiosIndexController
});
this.route('showStudio', {
path: '/studios/:_id',
controller: ShowStudioController
});
});
Now this is great and works fine when displaying my index page. I get a list of collections which is reactive and the as i introduce new studios to the collection i see them get created on the server and on the client respectively. However in my show view when the view is rendered the data always seems to be empty. On dropping into my show controller's data function with a debugger I tried querying the Studios and this always returns undefined even when i try to fetch everything possible. Oddly I know that my publications are logging the correct id for a studio (using console.log). It seems that i get all the correct data up until the routing on the client side. The parameter ids are all correct but a find() call on studios always returns nothing. Am I missing something obvious here.
Its also worth noting i deleted my helpers for 'studios' and 'studio' in views/studios/index.js and views/studios/show.js respectively as my understanding is that this is what im doing with studios and studio in the controller. These helpers are now defined at the controller level and passsed to the reactive 'data' function. IS this correct?
TL;DR
With the code above my index action works however my show action fails where the data function always returns undefined and in fact on the show page i cannot get access to any of my studio information (Studios.find({}).count() always returns 0). I'm unsure how the 2 routes differ. Can anyone spot the issue?
Edit: Its also worth noting having looked through some of the iron router issues on github i have tried using this.ready(). The first time the route is run i hit data and this is false but then even wrapping my data helpers to wait for this.ready() gets an undefined return value when this.ready() returns true.
Extra Notes
Im running meteor 0.8.0 with latest 0.7.0 release of iron router and collection2 with simple schema, just in case you are interested / require this info.
EDIT: Possible solution
So having fiddled around it seems like my helpers are the issue. By using the 'studio' section and then calling this.studio() inside my data function this seems to break. If I change the code below:
ShowStudioController = RouteController.extend({
template: 'showStudio',
waitOn: function () {
return Meteor.subscribe('studio', this.params._id);
},
studio: function () {
return Studios.findOne(this.params._id);
},
data: function () {
return {
studio: this.studio()
};
}
});
to this
ShowStudioController = RouteController.extend({
template: 'showStudio',
waitOn: function () {
return Meteor.subscribe('studio', this.params._id);
},
data: function () {
return Studios.findOne(this.params._id);
}
});
My view renders correctly again. Im unsure where i saw this pattern however I had assumed the function assigned to 'studio' was essentially the same as doing
Template.showStudio.helpers({
studio: function () {
return Studios.findOne(this.params._id)
}
});
but it seems not!

Iron-Router randomly returning undefined in Meteor

For some odd reason, iron-router randomly returns undefined.
this.route('pollyShow', {
path: '/polly/:_id',
template: 'polly_show',
notFoundTemplate: 'notFound',
before: function () {
var id = this.params._id;
var poll = Polls.findOne({_id: id});
console.log(poll);
var ip_array = poll.already_voted;
$.getJSON("http://smart-ip.net/geoip-json?callback=?", function(data){
ip_voted = ip_array.indexOf(data.host);
if (ip_voted > -1) {
Router.go('pollyResults', {_id: id});
}
});
},
data: function() {
return Polls.findOne({_id: this.params._id});
}
});
Sometimes it is returning normally while other times it just returns undefined.
Is there any reason behind this?
The problem occurs because the Polly collection is sometimes populated and at other times unpopulated when the route executes.
This problem can be prevented by explicitly waiting on a subscription using waitOn option in the route configuration.
From the docs:
By default, a new Meteor app includes the autopublish and insecure packages, which together mimic the effect of each client having full read/write access to the server's database. These are useful prototyping tools, but typically not appropriate for production applications. When you're ready, just remove the packages.
To remove the packages, call meteor remove <package-name>.
Then you need to explicitly publish records which you want to see on the client on the server:
server/publications.js:
Meteor.publish('all_of_polly', function () { return Polls.find({}); });
And subscribe to it on the client:
this.route('pollyShow', {
path: '/polly/:_id',
template: 'polly_show',
notFoundTemplate: 'notFound',
waitOn: function () { return Meteor.subscribe('all_of_polly'); }
// ...
});

Categories