Proper handling of computed property with vue-meteor-tracker

Hi, I recently discovered interesting challenge when implementing own user-profile-UI.

I use meteor-useraccounts/core and have an Vue component allowing the user to update his data. I use vue-meteor-tracker for the reactivity.

I was basically struggling with the necessity of having an input field updated by the tracked AND the user interaction. First I wanted to use a computed properties for values like profile.name

<template>
    ...
   <input type="text" v-model="name" >
    ...
</template>

<script>
    ...
    export default {
		name: 'UserProfile',
		data() {
		    updatedName: ''
		},
		computed: {
			name: {
				// getter
				get: function () {
					return this.userData && this.userData.profile && this.userData.profile.name ?
						this.userData.profile.name : '';
				},
                // setter
                set: function (updatedName) { 
			        this.updatedName = updatedName;
                }
            },
        meteor: {
			$subscribe: {
				'user-data': [],
			},
            userData(){
				return Meteor.user();
            }
        },
   }
</script>

There were several issues with that. updatedName is empty, till there is at least some interaction on the related input. And for some reason the first interaction on the input (e.g. deleting a letter) did’n no change the input value.

I ended up by using a watcher

		watch: {
			userData (newUserData, oldUserData, ) {
				this.name = newUserData && newUserData.profile && newUserData.profile.name ?
                    newUserData.profile.name : '';
            },
        },

which works fine, but it does feel like I am not using Vues data reactivity the right way. Has anyone a better solution ?

Hi! You can try this though I’m not sure what exactly you want to achieve:

<template>
  <div>
    <input type="text" v-model="name">
    <p>name: {{ name }}</p>
    <p>updatedName: {{ updatedName }}</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      updatedName: null
    };
  },
  meteor: {
    user() {
      return Meteor.user();
    }
  },
  computed: {
    profile() {
      return (this.user && this.user.profile) || {}
    },
    name: {
      // getter
      get: function () {
        if (this.updatedName === null) return this.profile.name || '';
        else return this.updatedName;
      },
      // setter
      set: function (updatedName) {
        this.updatedName = updatedName;
      }
    }
  }
}
</script>

Note that data() function must return an object. Also I removed a subscription because user’s profile is published automatically.

How are you updating the mongo database when the user changes their data? Do you have a ‘submit’ button or do you want the database to be updated after every keypress?

Generally I would do something like this:

<template>
  <div>
    <input type="text" v-model="newName">
    <button @click="submit" :disabled="newName==origName">Submit</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      origName: '',
      newName: '',
    };
  },
  meteor: {
    $subscribe: {
      'user-data': [],
    },
  },
  created() {
    this.$autorun(() => {
       // only query the profile.name field so that this doesn't re-run whenever some other user data changes
       const user = Meteor.users.findOne(Meteor.userId(), {fields: {"profile.name": 1}});
       this.newName = user && user.profile && user.profile.name || '';
       this.origName = this.newName;
    })
  },
  methods: {
    submit() {
      Meteor.call('changeName', this.newName);
      // if successful then the autorun above will re-execute and set origName to newName
    },
  },
}
</script>