Farshad
Farshad

Reputation: 2000

How to use prop inside another prop as default value in vuejs

I am using this package from GitHub for minus plus input for Vue.js. In the example folder, there is a sample file named plusminusfield.vue. This is the component's data:

export default {
    props: {
        value: {
            default: 0,
            type: Number,
        },
        base_capacity: {
            type: Number,
            required: true,
        },
        min: {
            /**
             * Here I want to use a variable named `base_capacity`,
             * which is passed as a prop above. If I hardcode a value here,
             * like 5, for example, it works, but this is not what I want.
             */
            default: 5,
            type: Number,
        },
        max: {
            default: 22,
            type: Number,
        },
    },
    data() {
        return {
            newValue: this.base_capacity,
        };
    },
    // ...
};

Here are some methods for validating the input value, and a watcher for changing it:

export default {
    // ...
    methods: {
        getNotificationClass(notification) {
            return `alert alert-${notification.type}`;
        },
        mpminus: function () {
            if (this.newValue > this.min) {
                this.newValue = this.newValue - 1;
                this.$emit("input", this.newValue);
            }
        },
        mpplus: function () {
            if (this.max === undefined || this.newValue < this.max) {
                this.newValue = this.newValue + 1;
                this.$emit("input", this.newValue);
            }
        },
    },
    watch: {
        value: {
            handler: function (newVal, oldVal) {
                this.newValue = newVal;
            },
        },
    },
    // ...
};

If I define newValue somewhere else, and use it in the props, I get an error saying that props can't be mutated, and my app doesn't run. And if I make the minimum of the input a computed property, like below, the error mentioned in the comments pops up:

export default {
    computed: {
        // Maximum call stack size exceeded
        min() {
            return this.min ? this.min : this.base_capacity;
        },
        // `base_capacity` is undefined
        min: this.base_capacity,
    },
};

Is there any other way to pass the min value to that input?

Upvotes: 13

Views: 11925

Answers (3)

Joshua Clark
Joshua Clark

Reputation: 394

While the accepted answer will work for Vue 2.x, in Vue 3.x prop default factory functions no longer have access to this. Instead you can pass the component's props as an argument to the factory function:

props: {
  ...
  baseCapacity: {
    default: 0,
    type: Number
  },
  min: {
    default: (props) => props.baseCapacity,
    type: Number
  },
  ...
},

See the migration guide: https://v3-migration.vuejs.org/breaking-changes/props-default-this.html

Upvotes: 26

Varit J Patel
Varit J Patel

Reputation: 3520

Instead of using it directly, use a factory function and return the value.

moreover, HTML attributes are case-sensitive.

Example: If you set the attribute as: base-capacity, Vue will automatically convert it into the camel-case as baseCapacity to access it from the script.

Here is the official docs

Vue.component('plus-minus', {
  template: '#vplusminus',
  props: {
    value: {
      default: 0,
      type: Number
    },
    baseCapacity: {
      default: 0,
      type: Number
    },
    min: {
      default: function () {
        return this.baseCapacity 
      },
      type: Number
    },
    max: {
      default: undefined,
      type: Number
    }
  },
  data() {
    return {
      newValue: 0
    }
  },
  methods: {
    getNotificationClass(notification) {
      return `alert alert-${notification.type}`
    },
    mpplus: function() {
      if (this.max === undefined || (this.newValue < this.max)) {
        this.newValue = this.newValue + 1
        this.$emit('input', this.newValue)
      }
    },
    mpminus: function() {
      console.log(this.min); // Here it is coming as 12
      if ((this.newValue) > this.min) {
        this.newValue = this.newValue - 1
        this.$emit('input', this.newValue)
      }
    }
  },
  watch: {
    value: {
      handler: function(newVal, oldVal) {
        this.newValue = newVal
      }
    }
  },
  created: function() {
    this.newValue = this.value
  }
});

new Vue({
  el: '#app'
});
.minusplusnumber {
  border: 1px solid silver;
  border-radius: 5px;
  background-color: #FFF;
  margin: 0 5px 0 5px;
  display: inline-block;
  user-select: none;
}

.minusplusnumber div {
  display: inline-block;
}

.minusplusnumber #field_container input {
  width: 50px;
  text-align: center;
  font-size: 15px;
  padding: 3px;
  border: none;
}

.minusplusnumber .mpbtn {
  padding: 3px 10px 3px 10px;
  cursor: pointer;
  border-radius: 5px;
}

.minusplusnumber .mpbtn:hover {
  background-color: #DDD;
}

.minusplusnumber .mpbtn:active {
  background-color: #c5c5c5;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <plus-minus :base-capacity="12" :value="16"></plus-minus>
  <plus-minus></plus-minus>
</div>

<script type="template/text" id="vplusminus">
  <div class="minusplusnumber">
    <div class="mpbtn minus" v-on:click="mpminus()">
      -
    </div>
    <div id="field_container">
      <input type="number" v-model="newValue" disabled />
    </div>
    <div class="mpbtn plus" v-on:click="mpplus()">
      +
    </div>
  </div>
</script>

Upvotes: 10

Radu Diță
Radu Diță

Reputation: 14201

You shouldn't use another prop as the default value. Even if they are available you have no guarantee regarding the order in which the props are evaluated.

A better approach is to use a computed property that takes into account both props and use that one inside your component.

computed: {
   minComputed () {
     return this.min ? this.min : this.base_capacity
   }
}

Also, remove the default value from the definition of min

Upvotes: 3

Related Questions