Meteor: How to detect if users authenticated - javascript

In my app I use accounts-github. Works perfect, but I have one problem.
In one of my templates I do
Template.bar.rendered = function () {
if (Meteor.user()) {
// setup stuff
}
}
The problem is that if the user initially is not logged in this code is not executed (thats ok). But when the user authenticates this code is not executed again. So the question is how can I listen for this change inside a template (doesn't have to be in inside the rendered function!)?

You could use Deps.autorun. (http://docs.meteor.com/#deps_autorun)
Usually Deps.autorun would run for your whole Meteor app. If you want to make it so that it only runs per template you would need to create and stop it in the rendered and destroyed template callbacks
e.g
var loginRun;
Template.bar.rendered = function() {
loginRun = Deps.autorun(function() {
if(Meteor.user()) {
//Stuff to run when logged in
}
});
}
Template.bar.destroyed = function() {
loginRun.stop();
}
If you don't need it to run per template (need it to run just once for you app on any template, then you can use the Deps.autorun on its own, anywhere in your client side code.
Meteor.user() is reactive, it would ensure that the Deps.autorun callback runs again when it changes, so you could theoretically use it to do things when the user logs in or out.
Other alternatives is there is a package on atmosphere that provides login and logout hooks, though they basically would use the Deps.autorun like above to work anyway. See https://github.com/BenjaminRH/meteor-event-hooks

My solution for similar problem was to
Attach an event to template where the login happens
Re-render template if login is succesful so the Template.bar.rendered is called
E.g.
Template.bar.events({
'click .loginButton' : function() {
if( Meteor.call.Login( username, pw ) )
{
$('#bar').html( Meteor.render( Template.bar ));
//jQuery is optional
}
});

Related

How to wait for Meteor.user() to be defined in Template onRendered

I am working on a Meteor project and I need to access some information stored in a user's profile to make a map on the page. However, when I try to access Meteor.user(), I receive undefined because when the function is called, Meteor.user() has not been loaded.
Template.body.onRendered () ->
console.log Meteor.user()
address = Meteor.user()['profile']['address']
GoogleMaps.ready 'studyMap', (map) ->
# maps code that relies on address
Because Meteor.user() is not defined, I am not able to get the map to work. How can I wait for Meteor.user() to be defined?
I think your going to need an asynchronous callback to load the map only when address = Meteor.user()['profile']['address'] loads. As far as how to implement this, I'm not really sure.
One of my apps has to do something similar to wait on a subscription. I defined the waitOn() handler in my router.
Router.map(function(){
this.route('Brocator', {
path: '/',
template: 'brocator',
waitOn: function() {
return (Meteor.subscribe('people', this.params._gridId)
&& Meteor.subscribe('gridlog', this.params._gridId) );
},
data: function() {
var gridId = this.params._gridId;
return (People.find({gridId: gridId})
&& GridLog.find({gridId: gridId}));
},
fastRender: true
}),
... other routes deleted for clarity
})
You may decide to use something along the lines of
return Meteor.user() != 'undefined';
in your waitOn() handler.
Posted an answer for meteor/react here however the same can be done with blaze. I am not familiar with blaze but within your Template.body.onRendered function you can listen to make sure that the login services are configured before rendering anything.
Tracker.autorun(() => {
if (Accounts.loginServicesConfigured()) {
// accounts is ready go ahead and render
} else {
// still waiting show loading spinner
}
});
I would recommend extracting this logic out to a higher level so that you don't keep repeating the same code.

Reflux avoid hitting server every time, when data cached locally

I curious if there is any agreed upon pattern to check if data has been already loaded before hitting the server.
Say I have my action that looks like this:
Actions.loadRequest.preEmit = function () {
$.get('/store/', function (data) {
Actions.loadSuccess(data);
}.bind(this));
}
This is called from a component that is simply saying give me this data:
But I don't want to hit the server if that data is already in the store.
Should I store the logic of checking the store in the component:
render: function () {
var data = this.state.store.data;
if (!data) {
Actions.loadRequest();
}
Is there a better way to go about this?
In my project I use shouldEmit for this (see https://github.com/reflux/refluxjs#action-hooks). An example from my code:
var streamStore = Reflux.createStore({
[...]
});
actions.loadStream.shouldEmit = function(streamId) {
if(streamId in streamStore.data)
return false;
return true;
};
This lives in the same file as the store definition. I think this is conceptually the right approach because the store saves the data, so the store should be responsible for intercepting the request to load more data and saying not to, just as it's responsible for listening to the action saying more data is available and updating itself.
Unfortunately this won't work with your example because you bound the AJAX call to preEmit, which gets called before shouldEmit. I would suggest refactoring to make the API call in a normal listen call, like this:
Actions.loadRequest.listen(function () {
$.get('/store/', function (data) {
Actions.loadSuccess(data);
}.bind(this));
});
This saves preEmit for the rare case of needing to rewrite an action's arguments before emitting it. I do use this pattern in my code, for example when loading a second page of results, which relies on a next token that came with the first page and is thus in the store. But in the general simple case of "action triggered, so make a request", using listen makes more sense because then you can add preEmit and shouldEmit for more advanced behavior, like the caching you want.
Reflux also has a helper function, listenAndPromise, which further simplifies the common use case of "action fired, make AJAX call, then fire another action when it's done". Your example could become:
Actions.loadRequest.listenAndPromise(function () {
return $.get('/store/');
});
See this section of the docs for more info on how to set that up: https://github.com/reflux/refluxjs#asynchronous-actions

In marionette mvc pattern, where to put different get API calls

For example I have the following server routes set up for my user entity:
GET /users/ // gets collection of users
GET /users/:id // gets user :id
GET /users/me // gets the current user
At the beginning of my app I want to get the current user from the server and store it... Something along the lines of:
App.addInitializer(function () {
$.get('/users/me')
.done(function processCurrentUser (userJson) {
App.user = new User(userJson);
});
});
My question is where this API call should actually reside. Would it be better to have something along the lines of:
App.addInitializer(function () {
App.user = new User();
App.user.fetchMe(); // performs the api call above
});
Or should I be doing something inside of a controller?
Thanks for the help!
When doing a fetch, I always worry about how its asyn behavior is going to affect the components that depend on that data. If there are no downriver components that will need the data before it can be reasonably expected to return, then there's technically nothing wrong with your approach.
There is, however, another possible way of loading your globals. What I often do (and for a user's list, too, it so happens) is bootstrap the data to the initial load page. I generally load it on the window variable. So for your example, in your backend template,
<script>
window.globals = {};
window.globals.currentUser = #Html.Raw(Json.Encode(ViewBag.User))
</script>
Of course, you can replace #Html.Raw(Json.Encode(ViewBag.User)) (we use C#) with your favorite backend model.
Then in your app start you're guaranteed to have the models:
App.addInitializer(function () {
App.user = new User(window.globals.currentUser);
});

Meteor subscribe on event/render

is there a way to subscribe in meteor when an event triggers or a template is rendered? Im trying to get a message popup and subscribe to all usernames when this happens.
Tried:
Template.newMessage.rendered = function(){
Meteor.subscribe("allUsernames");
}
And:
Template.layout.events({
"click #new-message": function(e, t){
$("#styledModal").modal();
Meteor.subscribe("allUsernames");
}
});
Neither work though, any way to do this or do I have to use a different route? Im using iron router
Im trying to understand why you would wanna do subscribe is such a late state of the process?
I would recommand that u subscribe in the waitOn property in IronRouter.
If you use the waitOn property to subscribe to all users, within that route, you can just display them in the popup =)
1 of the 7 Meteor principles is:
Latency Compensation. On the client, use prefetching and model simulation to make it look like you have a zero-latency connection to the database.
source:
http://docs.meteor.com/#sevenprinciples
Hope this helps,
Alex
You could try to do it using Deps and Session (although your code should work too, maybe you have a problem with publications?).
In your main template rendered function put this:
Template.layout.rendered = function() {
Session.set('getAllUsers',false);
Deps.autorun(function () {
if(Session.get('getAllUsers' == true)
Meteor.subscribe('allUsernames');
})
}
Then in the template that opens the user list:
Template.newMessage.rendered = function(){
Session.set('getAllUsers',true); //this should trigger Deps.autorun and subscribe.
}

How should I update user.profile on subsequent login (something similar to Accounts.onCreateUser)?

I've been poking around in the Accounts packages, using a modified version of the ever-fabulous EventedMind Customizing Login screencast.
I modified it to use facebook instead of github, and I noticed something when trying to update user.profile information. Specifically, I'm looking for the right way/place to handle changes to user.profile.
Let's say, for example, that I authenticate as a FB user for the first time. When I do this, the CreateUser event will fire.
Using Accounts.onCreateUser(...), I can populate additional information from the FB graph into the profile, like so:
Accounts.onCreateUser(function(options,user){
var accessToken = user.services.facebook.accessToken,
result;
result = Meteor.http.get("https://graph.facebook.com/"+user.services.facebook.username, {
params: {
access_token:accessToken,
fields: ['picture', 'name','first_name','last_name','username','link','location','bio','relationship_status','email','timezone','locale']
}
});
if (result.error){
throw result.error;
}
user.profile = result.data; //lazily adding everything
return user;
});
This works just fine when the user is created. It's nice and clean.
But now let's say that some of the information changes. For example, let's say that the profile picture changes. If I log out and then back in to the meteor application, Accounts.onCreateUser(...) doesn't fire, because the user already exists. It's not being created again, it's being modified.
I need to update the user.profile on subsequent logins, or at least check for changes and then modify as needed. I'd ideally like to do this in similar fashion to .onCreateUser. Maybe with a .onModifyUser or something...
I can figure a couple of ways to do this using some checking and/or client-side code, but I'm wondering if there is an already-existing server hook that would be cleaner.
Any recommendations on the cleanest way to handle this situation?
Thanks in advance.
If you're manually calling the login functions you can pass a callback as the last parameter which will get called on the client after the login completes. See: http://docs.meteor.com/#meteor_loginwithpassword.
Meteor.loginWithFacebook({}, function (err) { /* make a Meteor method call here */ });
There are no documented server side callbacks at the moment.

Categories