Connor Bär
Connor Bär

Reputation: 25

Meteor: Profile update fails mysteriously

I'm building a Meteor application and want users to be able to update/set their name. I've got the code set up, and everything seems to be working except that it mysteriously fails on the update. Here's my code:

Template

<template name="_editName">
  {{#ionModal focusFirstInput=true customTemplate=true}}
    <form id="updateName">
      <div class="bar bar-header bar-stable">
        <button data-dismiss="modal" type="button" class="button button-clear button-assertive">Cancel</button>
        <h2 class="title">Update your name</h2>
        <button type="submit" class="button button-positive button-clear">Save</button>
      </div>
      <div class="content has-header overflow-scroll list">
        <label class="item item-input">
          <input type="text" id="firstName" placeholder="Given Name">
        </label>
        <label class="item item-input">
          <input type="text" id="lastName" placeholder="Surname">
        </label>
      </div>
    </form>
  {{/ionModal}}
</template>

Client-side code

Template._editName.events({
  'submit #updateName': function(event, template) {
    event.preventDefault();
    console.log('Submitted');
    var firstName = document.getElementById('firstName').value;
    var lastName = document.getElementById('lastName').value;

    Meteor.call('editName', firstName, lastName, function(error) {
      if (error) {      
        throw new Error(error.reason);
      } else {
        Router.go('profile');
      }
    });
    IonModal.close();
    IonKeyboard.close();
  }
});

Server-side code

Meteor.methods({
    'editName': function(firstName, lastName) {
        console.log(firstName); // returns expected value
        console.log(lastName); // returns expected value
        Meteor.users.update({_id: this.userId}, {$set: {
            'profile.firstName': firstName,
            'profile.lastName': lastName
        }});
        Meteor.users.findOne({_id: this.userId}, {fields: {
        'profile.firstName': 1, _id: 0}}); // returns undefined
    }
});

Everything works as it should: On submit, the function gets the field values, calls the method on the server, successfully passing the field values. The update seems to be working as no error is thrown. However, the 'profile.firstName' returns undefined and does not show up in my template.

I'm using the accounts-base package and aldeed:collection2. I have the following schema set up:

Schema.UserProfile = new SimpleSchema({
    firstName: {
        type: String,
        regEx: /^[a-zA-Z-]{2,25}$/,
        optional: true
    },
    lastName: {
        type: String,
        regEx: /^[a-zA-Z]{2,25}$/,
        optional: true
    }
});

I have absolutely no idea what's going wrong and would appreciate every help!

Upvotes: 0

Views: 240

Answers (2)

Meteorpoly
Meteorpoly

Reputation: 938

I've checked your code and I found multiple issues which need your attention.

Please get the fixed project from here and follow the instructions to get and run it.

The main issues were:

  1. In your collection's schema definition you first specified the UserSchema and referenced the UserProfile schema. Since the UserProfile didn't exist by the time you referenced it, the code was failing silently. Maybe you address this with the package owner for a better error and reference check handling.
  2. You should definitely read upon meteor's databinding since under the profile.html templates you did quite a few mistakes
  3. Initialize your profile schema on user creation:
Accounts.onCreateUser(function(options, user) {
  // We're enforcing at least a profile skeleton object to avoid needing to check
  // for its existence later.
  // this file should reside on Server (e.g.  [root]/server/accounts.js)
  profile = {
    firstName: '',
    lastName: '',
    birthday: '',
    sex: '',
    grade: '',
    country: '',
    hostel: '',
    house:''
  }

  user.profile = profile;

  return user;
});
  1. Expose the published fields for your custom profile
Meteor.publish('userProfile', function() {
  if(!this.userId) { 
    return null
  } else {
    return Meteor.users.find({_id: this.userId}, {
      fields: {
        username: 1,
        'profile.firstName': 1,
        'profile.birthday': 1,
        'profile.sex': 1,
        'profile.grade': 1,
        'profile.country': 1,
        'profile.hostel': 1,
        'profile.house': 1
      }
    });
  }
  1. General meteor / JS coding standard issues which you will want to review
  2. There are also other things you would like to check like, variable scoping, unit testing, performance etc. before you go to production with this.

As a general rule (from my experience at least) I would always start at a bare minimum and avoiding to use complex packages without a solid foundation of the framework at hand. This will save you a lot of debugging and frustration like this in the future :).

Please accept this answer as soon as you have verified it so I can delete it again from my github account.

Good luck.

Upvotes: 1

Meteorpoly
Meteorpoly

Reputation: 938

Please see my working example code below for you reference:

User detail template markup

      {{#with currUser}}
        <form class="edit-todo" id="edit-user">
          Username:
          <input type="text" name="username" value="{{profile.name}}" />
          Firstname:
          <input type="text" name="firstname" value="{{profile.firstname}}" />
          Lastname:
          <input type="text" name="lastname" value="{{profile.lastname}}" />
          <button type="submit" style="width: 100px;height:50px">Save user</button>
        </form>
      {{/with}}

User detail template code

Template.UsersDetail.events({
    'submit form#edit-user': function(e, tmpl) {
        e.preventDefault();

        var firstname = tmpl.find('input[name=firstname]').value;
        var lastname = tmpl.find('input[name=lastname]').value;
        var userId = Meteor.userId();

        Meteor.call('editName', userId, firstname, lastname, function(error) {
            if (error) {
                throw new Error(error.reason);
            } else {
                Router.go('users.detail', {
                    _id: userId
                });
            }
        });
    }
});
Template.UsersDetail.helpers({
    currUser: function() {
        return Meteor.users.findOne({
            _id: Meteor.userId()
        });
    }
});

Server method for name updates

/*****************************************************************************/
/* Server Only Methods */

Meteor.methods({
    'editName': function(userId, firstName, lastName) {
        Meteor.users.update({_id: userId}, {$set: {
            'profile.firstname': firstName,
            'profile.lastname': lastName
        }});
        console.log(Meteor.users.findOne({_id: this.userId}).profile.lastname);// returns the proper name
    }
});

Result

Upvotes: 0

Related Questions