Petran
Petran

Reputation: 8047

Vue Dynamic component is not been re rendered

I have a basic component for all my input fields that wraps a dynamic component.

<my-input v-model="book_id" as="select" :options="availableBooks"
            label="Books" :dirty="changed.includes('book_id')"/>

I use a computed property to feed that component.

availableBooks () {
  if(this.books.length === 0) {
    return [{id: '1', text: 'test1'}]
  } else {
    return [{id: '2', text: 'test2'}]
  }
}

Inside my-input component, I render dynamically a component /my-input.vue

<template>
  <div class="live-input" :class="cssClass">
   <component :is="inputType" :state="currentState" :value="value" :errors="errors"
           v-on="$listeners" v-bind="$attrs" class="d-inline-block"/>
  </div>
</template>

props: {
  as: { type: String, default: 'text' },
  label: { type: String },
  hint: { type: String },
  inline: { type: Boolean, default: false },
},

data() {
  return {
    inputType: this.as || TextInput,
    currentDirty: this.dirty,
    selfUpdate: false,
  }
}

Then I have a second component select select-input.vue

<template>
   <b-select :value="value" @input="$emit('input', $event)" 
              ref="input" :state="state" :options="optionsForSelect"/>
</template>

props: {
  options: { type: Array },
},

data() {
  const sampleOption = this.options[0]
  const valueMethod = tryProperties(sampleOption, ['value', 'id'])
  const textMethod = tryProperties(sampleOption, 
                       ['text', 'name', 'label', 'toS']
                     )

return {
  localOptions: this.options,
  optionsForSelect: this.options.map(option => {
    if (typeof option === 'string') {
      return { value: option, text: option }
    } else {
      return {value: option[valueMethod], text: option[textMethod]}
    }
  }),
}
},

First this.books.length equals to zero, then I updated async and it has some entries, When I trace the availableBooks object seems that is been updated correctly but the select dropdown is not been updated.

Also seems that prop options has been updated but still the component is not been rerendered.

watch: {
  options: function(newVal, oldVal) { // [{id: '2', text: 'test2'}]
    console.log('Prop changed: ', newVal, ' | was: ', oldVal)
  }
},

Upvotes: 0

Views: 2051

Answers (2)

Andres
Andres

Reputation: 400

If you want to get updates for the options prop in order to re-render your component when options changes, you should use a computed attribute for optionsForSelect instead of a data one. When using data the value gets copied in the component and will only get re-rendered when the data attribute is changed locally.

If you use a computed property, when the parent component changes the prop options value, the computed attribute will get re-evaluated and your component will render again.

computed: {
   optionsForSelect() {
       if (typeof option === 'string') {
           return { value: option, text: option }
       } else {
          return {value: option[valueMethod], text: option[textMethod]}
       }
   }
}

The computed property would be my preferred approach.

However, you can also accomplish this with the data attribute you have. You would have to add a watcher for the options prop and assign the new value each time the prop is updated to optionsForSelect data attribute.

watch: {
    options: function(newVal, oldVal) {
        // Update this.optionsForSelect value here
   }
}

Hope it helps.

Upvotes: 2

javimovi
javimovi

Reputation: 386

Once that have entered the WATCH function, you can update the select-input view with this.$forceUpdate().

watch: {
  options: function(newVal, oldVal) { // [{id: '2', text: 'test2'}]
    // console.log('Prop changed: ', newVal, ' | was: ', oldVal)
    this.$forceUpdate()
  }
}

Upvotes: 1

Related Questions