I've seen props being define like:
props: ['message', 'name', 'something']
and
props: {
imperfectNumber: {
type: Number,
required: true
}
}
Whats the difference and how can I define some props that require no validation and others that do all in the same block?
Once you go down the object definition route, you have to do that with all the props. You can't mix them.
You can find more information about the props here: https://v2.vuejs.org/v2/guide/components.html#Prop-Validation
Note:
Instead of defining the props as an array of strings, you can use an
object with validation requirements
I've highlighted the part in the manual that explains. This indicates you can do one or the other.
Related
I just found out that instead of defining the properties of a component like so:
const props = defineProps({
id: Number,
title: String,
name: String,
})
I can do this instead:
defineProps([
'id',
'title',
'name',
])
This doesn't seem to require type declaration but is there any downside to doing this? Does vue determine the type of each property by itself?
I'm using script setup.
Downside is of course less safety.
Does vue determine the type of each property by itself? No
When providing array of strings, Vue does not validate the type of passed props at all so in case of incorrect usage (which is much more likely to happen as other devs/future you are not able to tell what should be passed in without reading the rest of component's code) you end up with some runtime error somewhere in your component instead of clean error/warning about wrong value passed as prop (or a sensible error from your IDE)
You should use as much specific props definition as possible most of the time.
Well it's not just type declaration.
It is a prop validation feature.
The complete syntax is
const props = defineProps({
name: String,
id: [ Number, String ],
style: {
type: Object,
default: ()=>{
color: "red",
bg-color: "white"
},
validator: function (value) {
return ['red', 'blue', 'green'].includes(value.color)
}
},
})
So the downside of using just the named props is:
No type safety. But even in case of typed props it will only show console warning in development build.
Whereas the advantage of using prop definition is
Multiple types for single prop
Default value for a prop
Custom validator function
Slall question:
I have a 2 parent components nesting the same child component inside of them.
I use props so the parents can tell the child what title to show.
this child component is a photo gallery. It makes a query to the database, download photos and show them up. classic.
I need the parents to tell the child where to get the photos from:
Get the photos from All users, for the home page
or
get only the photos from a specific user for a user's page.
I'm wondering if I can pass this information through a prop.
Is that possible? Can we use the info from a prop as a varialble inside of the setup() function?
Is there a better way to do this?
Passing objects from one component to a child component is the purpose of props.
You can pass many items through props. VueJS has the following types built-in:
String
Number
Boolean
Array
Object
Function
Promise
In the V3 VueJS guide it gives the following example of a prop being passed into a component and then being logged to the console inside the setup() method.
export default {
props: {
title: String
},
setup(props) {
console.log(props.title)
}
}
However, when using props, you should always mark whether the is required, what its type is and what the default is if it is not required.
For example:
export default {
props: {
title: String, // This is the type
required: false, // If the prop is required
default: () => 'defaultText' // Default needed if required false
},
setup(props) {
console.log(props.title)
}
}
It's important to note that when using default, the value must be returned from a function. You cannot pass a default value directly.
I’m trying to figure out how to use the reactivity system economically. I understand that $options for a component are for non-reactive data and are typically set in a created() or mounted() method from within itself. But is there a way to pass in non-reactive props on init from the parent?
For a simple example:
let’s say I have a component that renders a value as currency.
99% of the time currency will be USD and
100% of the time currency will not change after instantiation.
But the value needs to be reactive.
(this is a dumbed down example just to demonstrate the concept)
props: {
value: {
type: Number,
required: true,
},
currency: {
type: String,
default: 'USD',
},
},
But I don’t want to spend a watcher on currency, since it will never change.
Can I somehow pass it in as an unwatched $option? Is there another way of doing this? Or are watchers so cheap I shouldn't worry about efficiency at scale?
Thank you for any help!
To get a non-reactive field, use a regular HTML attribute instead of a declared property, and access it via the component $attrs object.
I would go with the Vue property though. It is typesafe, validated, and the overhead is insignificant.
<my-element currency='12' />
const currency = parseFloat(this.$attrs.currency)
Why nested fields of literal objects bound to a component’s property do not get reactive and observed?
Example:
<my-comp :my-prop="{ my: { literal: { object: { with: { nested: { field: ‘not reactive’ }}}}}}"></my-prop>
When you do this inside my-comp:
created() {
console.log(this); // you can see in the Chrome console that nested fields of this.myProp do not get reactive (i.e. no getters)
}
I know that I can play around it, but I am just asking why not?
Is there a neat way to make it reactive?
Thank you!
This is because the way "HTML" templates are rendered.
When Vue "compiles" your html, it basicly creates the following "computed" function
render(createElement) { /* `createElement` is also known as `h` in the Vue world */
return createElement('my-comp', {
props: {
myProp: { my: { literal: { object: { with: { nested: { field: ‘not reactive’ }}}}}},
},
);
},
Since the full flow of the value of myProp never passes through the data function, nor through the return result of a normal computed function, it never gets marked for reactivity.
While it might sound weird that Vue marks the return result of all "computed" functions, expect the special render function reactive, it actually makes sense if looking at the design idea of Vue, namely props in, and events out. This not marking of reactivity gives us a performance boost here, compared to marking it reactive.
If you still require it to be reactive (and thus want to go against the core principles of Vue (and thus making your application harder to understand) ), you can do it, by storing the value inside the data section of your component, or by returning it from a computed property.
Still a little bit young in VueJS but I'm loving every bit of it. But now, fixated somewhere.
I want to initialize some values in data() using values passed via props. This is so that I can be able to mutate them later on, since it is not recommended to mutate props inside a component. In fact the official docs recommend this property initialization using prop values as shown below:
{
props: ['initialCounter'],
data: function () {
return { counter: this.initialCounter }
}
I have something like the one below:
<template>
<div class="well">
<!-- Use Prop value directly on the template: works (but of no help in initializing data) -->
Department: {{department.name}}
<!-- Use prop value but gotten via computed property: Works inside the template but not in the initialization -->
Department: {{fetchDepartment.name}}
<!-- Use the array I initialized with the prop value: Does not work -->
Department: {{this_department.name}}
</div>
</template>
<script>
export default {
name: 'test',
props: ['department'],
data() {
return {
this_department: this.department
// below does not work either
//this_department: this.fetchDepartment
}
},
created() {
// shows empty array
console.log(this.department)
},
mounted() {
// shows empty array
console.log(this.department)
},
computed: {
fetchDepartment() {
return this.department
}
}
}
</script>
As seen in the commented sections above, the initialization is not successful. Neither does the value of this.department appear either from the created() or the mounted() hooks. And note, I can see it is defined using the Chrome Vue Devtools. So my question is, how exactly should I initialize data() attributes using props values, or which is the best way of going around this issue?
I know my answer comes in late but it helps me and hopefully someone else coming here. When props' data are async:
// in the parent component
<template>
<child :foo="bar" v-if="bar" />
</template>
That way, you render the component when props are already available making it safer to follow the guide's recommended ways to initialize data value with props as so:
props: ['initialCounter'],
data: function () {
return {
counter: this.initialCounter
}
}
Happy coding!
You CAN modify a prop. Use the '.sync' modifier. I use it frequently because it is convenient and intuitive. This requires emitting an event to update the value on the parent. I am not really sure the warning of how it results in maintenance issues.
Another method I use if I want to modify a value and not update the parent is using Lodash clone. For example (assuming its available on mounted)
mounted(){
this_department = _.clone(this.department)
}
If you consistently want to mutate the prop and have it change with the parent, then use a computed property. However, in most cases you will want to depend on the state of that data within the component and change it using other functions and thus a computed property will not be what you need.
A computed property is the simplest way to provide a mutable version of a prop, but you might not want to lose data when the prop is updated. You could use an explicit watch.
Watchers
While computed properties are more appropriate in most cases, there
are times when a custom watcher is necessary. That’s why Vue provides
a more generic way to react to data changes through the watch option.
This is most useful when you want to perform asynchronous or expensive
operations in response to changing data.
This is most useful when you want to perform asynchronous or expensive
operations in response to changing data.