Tarasovych
Tarasovych

Reputation: 2398

Vue.js - data value does not set from prop

What do I have: two components, parent and child.

Parent

<UserName :name=user.name></UserName>
...
components: {UserName},
    data() {
      return {
        user: {
          name: '',
          ...
        }
      }
    },
    created() {
      this.fetchUser()
      console.log(this.user) //<- object as it is expected
    },
    methods: {
      fetchUser() {
        let that = this
        axios.get(//...)
          .then(response => {
            for (let key in response.data) {
              that.user[key] = response.data[key]
            }
          })
        console.log(that.user) //<- object as it is expected
      }
    }

Child

<h3 v-if="!editing" @click="edit">{{ usersName }}</h3>
<div v-if="editing">
    <div>
        <input type="text" v-model="usersName">
    </div>
</div>
...
props: {
  name: {
    type: String,
    default: ''
  },
},
data() {
  return {
    editing: false,
    usersName: this.name,
    ...
  }
},

Problem: even when name prop is set at child, usersName data value is empty. I've inspected Vue debug extension - same problem.

What have I tried so far (nothing helped):

1) props: ['name']

2)

props: {
  name: {
    type: String
  },
},

3) usersName: JSON.parse(JSON.stringify(this.name))

4) <UserName :name="this.user.name"></UserName>

P. S. when I pass static value from parent to child

<UserName :name="'just a string'"></UserName>

usersName is set correctly.


I've also tried to change name prop to some foobar. I guessed name might conflict with component name exactly. But it also didn't helped.

Upvotes: 2

Views: 6903

Answers (2)

Roy J
Roy J

Reputation: 43899

user.name is initially empty, and later gets a value from an axios call. usersName is initialized from the prop when it is created. The value it gets is the initial, empty value. When user.name changes, that doesn't affect the already-initialized data item in the child.

You might want to use the .sync modifier along with a settable computed, or you might want to put in a watch to propagate changes from the prop into the child. Which behavior you want is not clear.

Here's an example using .sync

new Vue({
  el: '#app',
  data: {
    user: {
      name: ''
    }
  },
  methods: {
    fetchUser() {
      setTimeout(() => {
        this.user.name = 'Slartibartfast'
      }, 800);
    }
  },
  created() {
    this.fetchUser();
  },
  components: {
    userName: {
      template: '#user-name-template',
      props: {
        name: {
          type: String,
          default: ''
        }
      },
      computed: {
        usersName: {
          get() { return this.name; },
          set(value) { this.$emit('update:name', value); }
        }
      },
      data() {
        return {
          editing: false
        }
      }
    }
  }
});
<script src="https://unpkg.com/vue@latest/dist/vue.js"></script>
<div id="app">
  {{user.name}}
  <user-name :name.sync=user.name></user-name>
</div>

<template id="user-name-template">
    <div>
        <input type="text" v-model="usersName">
    </div>
</template>

Upvotes: 3

monahajt
monahajt

Reputation: 61

should be passed like this... <UserName :name="user.name"></UserName>

if data property is still not being set, in mounted hook you could set the name property.

mounted() {
    this.usersName = this.name
}

if this doesn't work then your prop is not being passed correctly.

sidenote: I typically console.log within the mounted hook to test such things.

Upvotes: 2

Related Questions