Is there a post createUser hook in meteor when using accounts-ui package?
Asked Answered
S

6

25

Let's say I have a todo app, and I want to make sure that every user that registers has at least one todo to start with, something like "First todo to cross off!", how would I do that in meteor?

In general, the way I see it, I can do it when the user is created for the first time (ideal), or check to see whether they need a new todo every time they log in (less ideal). In the latter case, I can do a check for Todos.findOne(), and if the count is 0, add one. However, seems that whether I do this in my router when the page loads, or on some template's .rendered function, the collection I'm checking hasn't been loaded yet, so I always create a new todo, even if one really does exist. So it'd be great if someone could explain how to get around that.

But, what I'd ideally want is the ability to just create a new Todo when the user is created. There is a Accounts.onCreateUser method, but that is used to add additional info to user profile, not a post-create hook. There's also a method to programmatically create the user using Accounts.createNewUser with a callback, but I'm using the accounts-ui package so am not programmatically adding users. In a less ideal case, I could check for the Todo whenever the user logs in, but even in that case, there seems to be a federated Accounts.loginWithXService login, so not sure how to handle the callback when any user logs in, regardless of service type.

I think I must be missing something simple, so apologies if this is super obvious. Any help is appreciated.

Segovia answered 20/10, 2012 at 2:8 Comment(0)
J
10

I used the _.wrap method described above but wanted to include an additional suggestion. It's a good idea to call the original callback from your new custom callback. Meteor does some things on the callback that we don't want to miss.

Modified code that worked like a champ for me:

Accounts.createUser = _.wrap(Accounts.createUser, function(createUser) {

    // Store the original arguments
    var args = _.toArray(arguments).slice(1),
        user = args[0];
        origCallback = args[1];

    var newCallback = function(error) {
        // do my stuff

        origCallback.call(this, error);
    };

    createUser(user, newCallback);
});
Jem answered 20/11, 2012 at 22:18 Comment(1)
For other use cases it's important to remember that this will NOT work server side. @portforwardpodcast, et al, createUser can be used anywhere, however, currently the callback is ironically only for client side use: docs.meteor.com/#/full/accounts_createuser. Thus this couldn't be implemented when security is needed. A common example is adding roles to new users : #30483314Vermont
M
18

The Meteor API now has the hook onCreateUser:

Accounts.onCreateUser(function (options, user) {
  Todos.insert({
    owner: user._id,
    text: "First todo to cross off!",
  });

  // We still want the default hook's 'profile' behavior.
  if (options.profile)
    user.profile = options.profile;

  return user;
});
Margaretmargareta answered 4/3, 2014 at 20:3 Comment(7)
This function is actually a PRE Create User hook, I think what most people want and indeed OP is that you execute something after the user has been created.Antalya
@DanailGabenski It works just fine for the OP's purpose. I've updated my answer to demonstrate it.Margaretmargareta
This answer be on the top of this pageCalenture
If this is supposed to go on the server, how do you call it from a client-side event?Deposition
@Danail: indeed, the _id is included in the user object. Hope this PR gets accepted for the docs to mention this.Fishbowl
@DanDascalescu Not sure if that was the behavior till recently, i.e. the user account was not created or the _id was unavailable. Will have to try it with the latest version, but it's a good idea for that PR to be accepted.Antalya
This is not suitable as a post account creation hook, since the creation might still fail, for example when an email address already exists.Dam
J
10

I used the _.wrap method described above but wanted to include an additional suggestion. It's a good idea to call the original callback from your new custom callback. Meteor does some things on the callback that we don't want to miss.

Modified code that worked like a champ for me:

Accounts.createUser = _.wrap(Accounts.createUser, function(createUser) {

    // Store the original arguments
    var args = _.toArray(arguments).slice(1),
        user = args[0];
        origCallback = args[1];

    var newCallback = function(error) {
        // do my stuff

        origCallback.call(this, error);
    };

    createUser(user, newCallback);
});
Jem answered 20/11, 2012 at 22:18 Comment(1)
For other use cases it's important to remember that this will NOT work server side. @portforwardpodcast, et al, createUser can be used anywhere, however, currently the callback is ironically only for client side use: docs.meteor.com/#/full/accounts_createuser. Thus this couldn't be implemented when security is needed. A common example is adding roles to new users : #30483314Vermont
V
10

If you are using the UserAccounts package: postSignUpHook now exists.

Splendido just merged my pull request for exactly this issue.

AccountsTemplates.configure({
    /*...*/
    postSignUpHook: /*[callback with your actions post full user creation goes here]*/,
    /*...*/
}

Documentation (You'll need to scroll down it's the last hook):

func(userId, info) Called, server side only, just after a successfull user account creation, post submitting the pwdForm for sign-up: allows for custom actions on the data being submitted after we are sure a new user was successfully created. A common use might be applying roles to the user, as this is only possible after fully completing user creation in alanning:roles. The userId is available as the first parameter, so that user object may be retrieved. The password is not available as it's already encrypted, though the encrypted password may be found in info if of use.

Vermont answered 6/12, 2015 at 4:21 Comment(2)
This is the way to go, especially if you want to do something with the created user like add a role it it.Mohandis
This does not work for FB sign up: github.com/meteor-useraccounts/core/issues/669Deutzia
E
4

You can piggyback onto functions that are called by Meteor by wrapping them. I'm also using the accounts-ui and accounts-password packages and I use Underscore's _.wrap method to redefine the loginWithPassword function. Underscore is included in Meteor by default.

I use something like this for logging in:

Meteor.loginWithPassword = _.wrap(Meteor.loginWithPassword, function(login) {

  // Store the original arguments
  var args = _.toArray(arguments).slice(1),
      user = args[0],
      pass = args[1],
      origCallback = args[2];

  // Create a new callback function
  // Could also be defined elsewhere outside of this wrapped function
  var newCallback = function() { console.info('logged in'); }

  // Now call the original login function with
  // the original user, pass plus the new callback
  login(user, pass, newCallback);

});

In this specific case, the code above would go in your client code somewhere.

For Accounts.createUser, it might look something like this (also somewhere in client code):

Accounts.createUser = _.wrap(Accounts.createUser, function(createUser) {

  // Store the original arguments
  var args = _.toArray(arguments).slice(1),
      user = args[0],
      origCallback = args[1];

  // Create a new callback function
  // Could also be defined elsewhere outside of this wrapped function
  // This is called on the client
  var newCallback = function(err) {
    if (err) {
      console.error(err);
    } else {
      console.info('success');
    }
  };

  // Now call the original create user function with
  // the original user object plus the new callback
  createUser(user, newCallback);

});

Hope that's helpful.

Epsilon answered 7/11, 2012 at 19:37 Comment(0)
S
2

One of the Meteor devs answered this question in Meteor google group: https://groups.google.com/forum/?fromgroups=#!topic/meteor-talk/KSz7O-tt4w8

Basically, right now, there is no createUser hook when using accounts-ui, only when programmatically doing so via Accounts.createUser. Also, there are no hooks for login, unless using the lower-level login functions like loginWithFacebook, etc. I haven't figured out an ideal way around this yet, but a few ways of handling it:

  • if needing to enter a default value into a collection, in that collection's subscription, use the onComplete argument. In this callback, if there are no entries in collection, add one. This avoids the first problem I mentioned in my post about not knowing when a collection was loaded, though not ideal since collection could be empty because user already removed first default one:

    Meteor.subscribe 'todos', user: Meteor.userId(), () ->
      todo = Todos.findOne()
      unless todo
        Todos.insert user: Meteor.userId()
    
  • you can set up a login hook by using the Meteor.autorun reactive method to check for a change in Meteor.userId(). That'll only get called when the user logs in/reloads the page. This is more useful for non-collection stuff since the collection is not guaranteed to be loaded when Meteor.userId is set:

    Meteor.autorun () ->
      if Meteor.userId()
        console.log 'Do some post login hook'
    

So I think the efficient solution is still out there somewhere, but wanted to update this post with workarounds I had found in the meantime.

Segovia answered 25/10, 2012 at 2:39 Comment(2)
This is obsoleted by David's answer.Fishbowl
@DanDascalescu actually it is not, since David's answer doesn't provide a solution to hook into "user created and logged in"Outcast
H
-3

I think this answer this question better: How can I create users server side in Meteor?

in resume:

 Accounts.createUser({
                        username: username,
                        email : email,
                        password : password,
                        profile  : {
                            //publicly visible fields like firstname goes here
                        }

});

check the meteor docs for more: http://docs.meteor.com/#/full/accounts_createuser

Headcheese answered 2/2, 2015 at 16:27 Comment(1)
The OP specifically mentioned "There's also a method to programmatically create the user using Accounts.createNewUser with a callback, but I'm using the accounts-ui package so am not programmatically adding users."Fishbowl

© 2022 - 2024 — McMap. All rights reserved.