Retrieving data from custom useraccounts fields - meteor

I'm using the Atmosphere package meteor-useraccounts, but I cannot figure out how to retrieve data from custom made fields.
I made the field:
AccountsTemplates.addField({
_id: 'callsign',
type: 'text',
placeholder: {
signUp: "Callsign"
},
required: true,
});
and I would like to get the logged in user's callsign from the user's collection.
I tried to see if the registration pushes the data to the user's collection, but it seems not so. Is there a way to do this?

try hooking into the account creation, it should show you your values in options.profile. then you can save them to the user object or wherever you would like. put this code somewhere on the server:
Accounts.onCreateUser((options, user) => {
console.log('--------------------------------');
console.log('options:', options);
console.log('--------------------------------');
console.log('user:', user);
console.log('--------------------------------');
return user;
});
be sure to return the user object at the end.

Related

Meteor.user with Additional Fields on Client

In Meteor, one can add additional fields to the root-level of the new user document like so:
// See: https://guide.meteor.com/accounts.html#adding-fields-on-registration
Accounts.onCreateUser((options, user) =>
// Add custom field to user document...
user.customField = "custom data";
return user;
});
On the client, one can retrieve some data about the current user like so:
// { _id: "...", emails: [...] }
Meteor.user()
By default, the customField does not exist on the returned user. How can one retrieve that additional field via the Meteor.user() call such that we get { _id: "...", emails: [...], customField: "..." }? At present, the documentation on publishing custom data appears to suggest publishing an additional collection. This is undesired for reasons of overhead in code and traffic. Can one override the default fields for Meteor.user() calls to provide additional fields?
You have a couple of solutions that you can use to solve this.
Null Publication
Meteor.publish(null, function () {
if (this.userId !== null) {
return Meteor.users.find({ _id: this.userId }, { fields: { customField: 1 } });
} else {
return this.ready();
}
}, { is_auto: true });
This will give you the desired result but will also result in an additional database lookup.. While this is don't by _id and is extremely efficient, I still find this to be an unnecessary overhead.
2.Updating the fields the Meteor publishes for the user by default.
Accounts._defaultPublishFields.projection = { customField: 1, ...Accounts._defaultPublishFields.projection };
This has to be ran outside of any Meteor.startup blocks. If ran within one, this will not work. This method will not result in extra calls to your database and is my preferred method of accomplishing this.
You are actually misunderstanding the documentation. It is not suggesting to populate and publish a separate collection, just a separate publication. That's different. You can have multiple publications/subscriptions that all feed the same collection. So all you need to do is:
Server:
Meteor.publish('my-custom-user-data', function() {
return Meteor.users.find(this.userId, {fields: {customField: 1}});
});
Client:
Meteor.subscribe('my-custom-user-data');

How to normalise user data from varying structures in Meteor?

Users in my Meteor app can create accounts 'manually' or with the accounts-facebook package.
If they create an account manually then in the database their email is stored like this:
emails: [address: 'hi#gmail.com', verified: false]
But if they use the Facebook login then its stored like this:
services: {
facebook: {
email: "james#gmail.com"
}
}
I have an user account page where I need to display the users email and allow them to change it. How can I deal with the varying structure?
I made this React component to display the users email. When I just had the default Meteor user profiles it worked, but now Ive added Facebook login it errors as props.user.emails doenst exist.
<div className="form-primary__row">
<label>Email:</label>
{props.user.emails.map((item, i) => {
return (
<input defaultValue={item.address} key={i} name="email" />
);
})}
</div>
This is my method for a user to update their email. It also worked when I just had Meteors accounts but won't work with Facebook.
Meteor.methods({
'user.updateEmail'({ email }) {
Meteor.users.update(
{ _id: Meteor.userId() },
{
$set: {
'emails.0.address': email,
'emails.0.verified': false,
},
},
);
},
});
One approach is to use Accounts.onCreated()
The function should return the user document (either the one passed in
or a newly-created object) with whatever modifications are desired.
The returned document is inserted directly into the Meteor.users
collection.
Accounts.onCreateUser(function (options, user) {
// if the account is created using the manual approach,
// simply return the user object that will be inserted into
// the Users collection.
if (!user.services.facebook) {
return user;
}
// if user is created using fb's API,
// manually set the emails array, then return the user object
// which will be inserted into the Users collection.
user.username = user.services.facebook.name;
user.emails = [{address: user.services.facebook.email}];
return user;
});
The above ensures that the emails array always contains the email, whichever the login method the user chooses to use.

Server side meteor this.userprofile

I have the following code that renders the currentUsers' documents in a collection. However I want an admin belonging to the same organization to also be able to view and edit the collection. this.user.profile.organization does not work unfortunately. So, how would I allow the object to be available to admins from belonging to the same organization. EVery document that gets created gets the organization of the currentuser.
Meteor.publish('skills', function skillsPublication() {
return Skills.find({
owner: this.userId,
});
});
When you're on the server, you can always query the user document from the MongoDB database.
Meteor.publish('skills', function skillsPublication() {
const user = Meteor.users.findOne({ _id: this.userId })
// now you can do user.profile.organization
return Skills.find({
$or: [
{ owner: this.userId },
{ organization: user.profile.organization }
] // returns a document owned by the user or owned by the user's organization
})
})
Just a note, Meteor advises against using .profile field on your users collection, because that field is always published to the client. Meteor suggests that you use top-level keys on your user document instead.
For more info, read: https://guide.meteor.com/accounts.html#dont-use-profile

Meteor - template not updated after user sign in

I built an app with simple user login, using Accounts-unstyled. It all works fine, except that the template showing user data is not re-rendered with the user logs in. However if the user reloads the page, then his data is present.
My code doesn't really contain anything related to user, except for fetching the correct user's data. Do I need to manually trigger a refresh after the login?
The app is at www.barelinks.in. Test login: user=test; password=pwdpwd.
Upon first login nothing changes, but if you reload you will see the user's data.
Here is the code for the subscription:
if(Meteor.isServer) {
Meteor.publish('links', function linksPublication(userId) {
return Links.find({ user: userId });
});
}
And the helper fetching the right data (the Regex is for the search feature):
Template.body.helpers({
links: function() {
var nbSkip = linksPerPage*(Template.instance().page.get() - 1);
var queryRegex = new RegExp(Template.instance().query.get(), 'i');
return Links.find({ user: Meteor.userId(), title: queryRegex }, { sort: { createdAt: -1 }, limit: linksPerPage, skip: nbSkip });
},
});
Thanks!
I recognized that the content is updated when refreshing the page in a logged in state. Than you can switch forth and back and everything works as expected. I came usually across this behavior when the data subscription is not as it should be.
The server automatically updates the data according to a change of logged in state, you need to rely on the this.user property in the publish function.
if(Meteor.isServer) {
Meteor.publish('links', function linksPublication() {
return Links.find({ user: this.userId });
});
}

Meteor - Validating new user document serverside

I'm having trouble with this seemingly trivial stuff, harrrr!
I have this user document:
userData = {
account: {
type: 'free'
},
profile: {
name: 'Artem',
},
username: 'aaa#gmail.com',
password: '123'
};
Which I'm sending client-side: Accounts.createUser(userData);
Then server side I want to check if account type equals 'free'. If it doesn't - I want to abort new user creation (and hopefully throw error client side)
There are 2 functions which I've found in the docs that presumably can help me do it:
Accounts.validateNewUser
Problem: it receives 'trimmed-down' user object which doesn't contain properties other than profile, username, password, email. Thus I cannot validate account.type as it doesn't exist on user object being validated.
Accounts.onCreateUser
Problem: it is called after a generic user object is created and there is no way I can cancel inserting new document in Users collection. It absolutely requires to return a user document. If I return undefined it throws errors on server:
Exception while invoking method 'createUser' Error: insert requires an argument
It also doesn't allow to throw method errors (as it's not a method) -> thus I cannot log error client side.
You can use Accounts.validateNewUser with little change to your data structure:
userData = {
profile: {
name: 'Artem',
account : {
type : 'free'
}
},
username: 'aaa#gmail.com',
password: '123'
};
Then you should be able to access data you need.
As far as I remember there were some discussion on meteor forum about removing profile field, that's why I'm solving this kind of problems in different way. For me Meteor.users is collection which should not be changed for sake of peace in mind - it could be changed by future version of meteor. My approach require to write more code in the beginning, but later it pays off, because you have place to store data about user and Meteor.users collection has docs with minimal amount of data.
I would use jagi:astronomy#0.12.1 to create schema and custom methods. In general I would create new collection UserAccounts with schema:
UserAccount = new Astro.Class( {
name: 'UserAccount',
collection: 'UserAccounts',
fields: {
'userId' : {type: 'string'},
'type' : {type: 'string', default:'free'}
},
} )
and add schema to Meteor.users :
User = new Astro.Class( {
name: 'User',
collection: Meteor.users,
fields: {
'services' : {type: 'object'},
'emails' : {type: 'array'}
},
methods:{
account : function(){
return UserAccounts.findOne({userId:this._id})
}
}
} )
The usage looks like this:
var user = Meteor.users.findOne();
user.account().type
In summary:
Accounts.onCreateUser : always allow to create user account and always create UserAccount which corresponds to it ( with field userId)

Resources