Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Merge Vue props with default values

I have an options prop in my Vue component that has a default value.

export default {
  props: {
    options: {
      required: false,
      type: Object,
      default: () => ({
        someOption: false,
        someOtherOption: {
          a: true,
          b: false,
        },
      }),
    },
  },
};

If the options object is passed as a prop to the component, the default value is replaced. For example, when passed { someOption: true }, now the options object contains only that value.

How can I pass a partial object and override the default values with the given values instead of replacing the whole object?

like image 866
Mikko Avatar asked Feb 03 '17 07:02

Mikko


2 Answers

I've encountered a similar problem recently and used Object.assign Here is the docs from mozilla https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/assign

A concrete usage of your case would be something like that:

props: {
 options: {
  required: false,
  type: Object,
  default: () => ({}),
 },
},
data(){
  mergedOptions:{},
  defaultOptions:{
    someOption: false,
    someOtherOption: {
      a: true,
      b: false,
    },
  }
},
mounted(){
  //you will have the combined options inside mergedOptions
  Object.assign(this.mergedOptions,this.defaultOptions,this.options)
}

By doing this, you will override only the properties that passed via props. Don't know if it's the most efficient way but it's very understandable and neat :)

So if you pass in as props :options={someOption:true} the merged options will be equivalent to:

{
 someOption: true,
 someOtherOption: {
  a: true,
  b: false,
 },
}

EDIT: If you need your data to be reactive, you might want to have a computed.

  computed: {
    mergedOptions(){
      return {
       ...this.defaultOptions,
       ...this.options
      }
    }
  }
like image 115
Cristi Jora Avatar answered Sep 22 '22 14:09

Cristi Jora


You will actually never want to modify props within components. If you do, you break one-way-dataflow of parent/child components and your code will be hard to reason about what is affecting what.

Lifted right from the Vue docs, the correct solution is to either (1) use an initial prop or (2) a computed value, so your app can be reactive and respect parent components, and you can rest easy and kick your feet up :)

Both solutions assume your template will use opts for options...

Solution 1: Use an initial prop (defaults and options):

props: ['options', 'defaults'],
data: function () {
  var opts = {}
  Object.assign(opts, this.defaults, this.options)
  return { 
    opts: opts
  }
}

Solution 2: Define a computed property so your component can react to prop changes:

props: ['options', 'defaults'],
computed: {
  opts: function () {
    let opts = {}
    Object.assign(opts, this.defaults, this.options)
    return opts
  }
}

A quick thought experiement will show, if a parent component changes your input props, your component can properly react.

like image 34
Nick Steele Avatar answered Sep 25 '22 14:09

Nick Steele