I have been recently making a Vue component library which has a dozen of props. There are two props called showCount
and showCountOnSearch
respectively. The props
property of my component looks like:
export default {
props: {
showCount: {
type: Boolean,
default: false,
},
showCountOnSearch: {
type: Boolean,
default: false,
},
// ...other props
},
// ...
}
I want the default value of showCountOnSearch
to be determined based on the value of showCount
. For example:
showCount
is not specified, set showCount
to the default value false
. If showCountOnSearch
is not specified either, set it to false
too.showCount
is specified and showCountOnSearch
is not, set showCountOnSearch
to the same value of showCount
.In other words, showCountOnSearch
always defaults to the value of showCount
.
Currently Vue allows default
property to be functions to generate default value dynamically. However, seems it's impossible to obtain another prop's value inside the default
function.
I hope the API to look like:
export default {
props: {
showCount: {
type: Boolean,
default: false,
},
showCountOnSearch: {
type: Boolean,
default(props) {
return props.showCount
},
},
// ...other props
},
// ...
}
@riophae, you can achieve same by using a computed prop like this:
export default {
props: {
showCount: {
type: Boolean,
default: false,
},
showCountOnSearch: {
type: Boolean,
},
// ...other props
},
computed: {
showCountOnSearchComputed () {
return this.showCountOnSearch || this.showCount
}
}
}
We cannot define props based on other props directly in the default value because the order in which props are resolved will decide that default value. However, because props
is an object, there's no order in keys. Thus, making props' value depend on each other can lead to unexpected behaviour.
What you can do instead is:
data
to hold the actual variable and change it in the created
hookOr combine both if necessary
Yeah, computed property solves the problem.
But actually it's possible to determine the order:
var propsWithDefaultFunction = []
var propsWithDefaultFunctionThatReliesOnOthers = []
var otherProps = []
for (var key in props) {
var prop = props[key]
if (typeof prop === 'object' && typeof prop.default === 'function') {
if (prop.default.arguments.length === 0) {
propsWithDefaultFunction.push(key)
} else {
propsWithDefaultFunctionThatReliesOnOthers.push(key)
}
} else {
otherProps.push(key)
}
}
// first resolve default values for `otherProps`
otherProps.forEach(...)
// then resolve default values for `propsWithDefaultFunction`
propsWithDefaultFunction.forEach(...)
// at last resolve default values for `propsWithDefaultFunctionThatReliesOnOthers`
propsWithDefaultFunctionThatReliesOnOthers.forEach(...)
@riophae, what if b
relies on a
and then c
relies on b
?
@nickmessing yeah... that's impossible.
@riophae, that's actually possible using Proxy
and calling everything with fake objects but it's not reasonable and limits browser support., computed properties approach works great in all scenarios.
I think computed is not an ideal solution because it introduces the need of making up a new name that makes the code harder to reason about (conceptually the name of the prop externally and the name of the computed property internally are identical). It also makes vue component props less flexible than javascript function parameter defaults (which can be based on one another).
Because of this we end up with multiple patterns to do the same thing throughout our codebase (prop: initialFoo, computed: Foo
or foo, computedFoo
, theFoo, foo
etc. many of which end up being of each other which makes references in the template (of the component and its parent) confusing. We can come up with a pattern and adopt it, but none are particularly ideal (bc they add conceptual overhead to a behaviour that is more straightforward than implied by the necessary implementation).
I appreciate this isn't really possible in Vue as-is, but I think the pattern is an important one and wonder if it could have support from the library either as an official recommendation (in the styleguide) or code support (something like a computedDefaultProps
block inside components (or inside the computed block itself to avoid polluting the top level of component scope) that creates computed functions that override the prop values while still remembering what they are without the need for new names... )
To follow up... our team has adopted the pattern of calling the prop foo
and the computed property _foo
. it matches the javascript private/public convention nicely. Reference _foo
inside the component, reference foo
externally.
I also saw somewhere someone reporting that it is possible to return a function based on other props to get a default value, and it does work sometimes.. but like @posva mentioned i think this will unpredictable. is it based on the order in which the props are passed into the component therefore really **ing dangerous!? 😲 .. might warrant an associated console warning ... 🤔
This would work if validation happens after the first pass (https://github.com/vuejs/vue/issues/9467)
@posva
Would be nice if this was in the docs here. Thank you!
https://vuejs.org/v2/guide/components-props.html
We cannot define props based on other props directly in the default value because the order in which props are resolved will decide that default value. However, because
props
is an object, there's no order in keys. Thus, making props' value depend on each other can lead to unexpected behaviour.What you can do instead is:
- Use a computed property
- Add a property in
data
to hold the actual variable and change it in thecreated
hookOr combine both if necessary
Even computed may work for the default values but I have the same issue for the validator. What I needed was to validate a prop based on some other props. How this can be achieved?
You would have to do that in a lifecycle hook in pretty sure. That’s the
best I could come up with anyway 😕
On Thu, Oct 22, 2020 at 19:29 btalebpour notifications@github.com wrote:
Even computed may work for the default values but I have the same issue
for the validator. What I needed was to validate a prop based on some other
props. How this can be achieved?—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
https://github.com/vuejs/vue/issues/6358#issuecomment-714816823, or
unsubscribe
https://github.com/notifications/unsubscribe-auth/AAKPYIY7U7OKIXIAH42TWZLSMC56DANCNFSM4DWWJASA
.>
Damon Muma
Yes, I understand and it was on my mind but the behavior won't be the same.
Thanks anyway.
On Fri, Oct 23, 2020 at 9:59 AM Damon Muma notifications@github.com wrote:
You would have to do that in a lifecycle hook in pretty sure. That’s the
best I could come up with anyway 😕On Thu, Oct 22, 2020 at 19:29 btalebpour notifications@github.com wrote:
Even computed may work for the default values but I have the same issue
for the validator. What I needed was to validate a prop based on some
other
props. How this can be achieved?—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
https://github.com/vuejs/vue/issues/6358#issuecomment-714816823, or
unsubscribe
<
https://github.com/notifications/unsubscribe-auth/AAKPYIY7U7OKIXIAH42TWZLSMC56DANCNFSM4DWWJASA.
>
Damon Muma
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
https://github.com/vuejs/vue/issues/6358#issuecomment-715460630, or
unsubscribe
https://github.com/notifications/unsubscribe-auth/AA6DI6SOM5ZZ4ERLOV2NNZ3SMGY7PANCNFSM4DWWJASA
.
--
Behzad
Most helpful comment
I think computed is not an ideal solution because it introduces the need of making up a new name that makes the code harder to reason about (conceptually the name of the prop externally and the name of the computed property internally are identical). It also makes vue component props less flexible than javascript function parameter defaults (which can be based on one another).
Because of this we end up with multiple patterns to do the same thing throughout our codebase (
prop: initialFoo, computed: Foo
orfoo, computedFoo
,theFoo, foo
etc. many of which end up being of each other which makes references in the template (of the component and its parent) confusing. We can come up with a pattern and adopt it, but none are particularly ideal (bc they add conceptual overhead to a behaviour that is more straightforward than implied by the necessary implementation).I appreciate this isn't really possible in Vue as-is, but I think the pattern is an important one and wonder if it could have support from the library either as an official recommendation (in the styleguide) or code support (something like a
computedDefaultProps
block inside components (or inside the computed block itself to avoid polluting the top level of component scope) that creates computed functions that override the prop values while still remembering what they are without the need for new names... )