Embed User Name in Url with Iron Router


#1

Hello!

I understand how to get a unique post id and use it for the url like so

Router.route(':_id', { name: 'postPage', data: function() { return Posts.findOne(this.params._id); } });

I’d like to include the author of the posts user name in the link as well.
Example: appName.com/tyler/postID

Would the code look something like this?

Router.route(':userName/:_id', { name: 'postPage', data: function() { return Posts.findOne(this.params._id); } });

What the equivalent to :_id? I’m able to access the user’s userName on my app with

Meteor.user().profile.userName


#2

Yes, you can use multiple params. You don’t need to use the :userName param in this case unless you need to specify additional data to use. Instead it can be a dynamic part of the URL.

Edit: The :userName param would be used to prevent the post showing under a username the post does not belong to.


#3

Thanks for your post! I figured it out!! Now I’m try to display a single page like so

example.com/userName

Router.map(function() {
    this.route('profile',{
       path:'/:userName',
        data:function(){
         return Meteor.users.findOne(this.params._id);
       }
    });
  });

``

Not working though :frowning:


#4

This will conflict with other routes so you could either do /:userName/profile, /p/:userName, /profile/:userName or try defining that route after all your other routes (not sure if that will work though)


#5

yes, it would be available as this.params.userName
you can also serialize and pass various arguments in link like example.com/fred/123?option1=something&option2=somethingelse

this.params.query.option1 == 'something’
this.params.query.option2 == ‘somethingelse’


#6

I don’t have any routes that use just ‘:_id’ yet. I don’t see why it wouldn’t work


#7

Try this:

Router.map(function() {
    this.route('profile',{
       path:'/:userName',
        data:function(){
         return Meteor.users.findOne({username: this.params.userName});
       }
     });
});

#8

Hmm, didn’t work.

I’m assuming I need to make sure a collection is created, with subscription and published enabled? In addition, on user creation, create one profile per user?

Accounts.onCreateUser(function() {
       Meteor.call('profileInsert');
});

Meteor.methods({
  profileInsert: function(profileAttributes) {
  check(Meteor.userId(), String);
  check(profileAttributes, {
  bio: String,
  location: String,
  profilePic: String
});
var user = Meteor.user();
var profile = _.extend(profileAttributes, {
  userId: user._id, 
  userName: user.profile.userName,
  fullName: user.profile.firstName + " " + user.profile.lastName, 
  submitted: new Date()
});
var profiletId = Profile.insert(profile);
return {
  _id: profileId
   };
  }

});
``

What do you think?


#9

Yes you will need to publish and subscribe to the user data if you don’t have autopublish on.

Also when you call your profileInsert method you are not passing any data.

And the onCreateUser function should return a user document as stated in the docs. In your profileInsert method you will not have access to Meteor.user() since it will not be in the database yet. So you could set a random id in your create user function. Example of how I would do it:

Accounts.onCreateUser(function(options, user) {
    user._id = Random.id();
    if(options.profile)
       Meteor.call('profileInsert', options.profile, user);

    return user;
});

Meteor.methods({
  profileInsert: function(profileAttributes, user) {
      //check attributes etc
      var profile = _.extend(profileAttributes, {
          userId: user._id, 
          userName: user.profile.userName,
          fullName: user.profile.firstName + " " + user.profile.lastName, 
          submitted: new Date()
      });
      var profiletId = Profile.insert(profile);
      return {
      _id: profileId
       };
    }
});

You could also just store the profile object in the user document, which is usually better and easier.


#10

This looks great!

What about having a place to store the string for profile pic, cover photo, mini bio, and location?


#11

Well you will have to handle image uploads then, but just put them in the profile object and you will have access to them.

As stated above though it’s much easier to just store the users profile directly in the user document, since you will have an extra query every time.

e.g like this

Accounts.onCreateUser(function(options, user) {
  // anything you want to change about user or profile here
  if (options.profile)
    user.profile = options.profile;
  return user;
});

#12

Get some weird error

I20150613-06:11:22.490(-7)? Exception while invoking method ‘ATCreateUserServer’ TypeError: Cannot set property 'id’ of null
I20150613-06:11:22.490(-7)? at Meteor.methods.profileInsert.
.extend.userId (app/lib/collections/profiles.js:6:14)

``

Accounts.onCreateUser(function() {
var user = Meteor.user();
user._id = Random.id();    //line 6
if(options.profile)
   Meteor.call('profileInsert', options.profile, user);

return user;

});

So I think line 6 is giving me an error. Why would I want to assign a Random id to the a new user? Doesn’t my accounts package already do that for me?

Oh and I added the user variable because another error was saying it was not defined.


#13

Update:

Meteor.methods({
   profileInsert: function(profileAttributes) {
 
  //check attributes etc
  var profile = _.extend(profileAttributes, {
      userId: user._id,             //line 17
      userName: user.profile.userName,
      fullName: user.profile.firstName + " " + user.profile.lastName, 
      submitted: new Date()
  });
  var profiletId = Profile.insert(profile);
  return {
  _id: profileId
   };
 }
});

The error:

ReferenceError: user is not defined
 I20150613-06:29:37.055(-7)?     at [object Object].Meteor.methods.profileInsert (app/lib/collections/profiles.js:17:19)

Something going on with line 17. Which I don’t understand because that is the same code I use when I need a user to create a new post and add it to the database.


#14

You are not taking in the user in your profileInsert method and in onCreateUser you should take in options and user. Look at the code I provided again.

var user = Meteor.user() will not work since there is no user yet. onCreateUser gets called BEFORE the user is put into the database


#15

this seems as new neverending thread, everything he copy without reading any docs and understanding how it works or how passing arguments in functions/events work…
and with Iron router subject, when working with methods and creating users atm


#16

When I copy your code I get this error the browser console

 Uncaught TypeError: Accounts.onCreateUser is not a function

On the server I get this error

 Exception while invoking method 'ATCreateUserServer' TypeError: Cannot read property 'profile' of null
 I20150613-06:50:46.067(-7)?     at [object Object].Meteor.methods.profileInsert (app/lib/collections/profiles.js:18:25)

#17

Sorry I’m new to Meteor. Second week.


#18

Accounts.onCreateUser is only called on the server, not the client.


#19

Thanks! Next error

W20150613-06:53:37.625(-7)? (STDERR) Error: Can only call onCreateUser once
W20150613-06:53:37.626(-7)? (STDERR)     at Object.Accounts.onCreateUser (packages/accounts-   base/accounts_server.js:963:1)

#20

You probably defined it twice somewhere.