Как изменить props vue

Vue.js - Прогрессивный JavaScript-фреймворк

# Входные параметры

Подразумевается, что вы уже изучили и разобрались с разделом Основы компонентов. Если нет — прочитайте его сначала.

# Указание типа входных параметров

До сих пор мы видели только входные параметры, перечисленные в виде массива строк:

props: ['title', 'likes', 'isPublished', 'commentIds', 'author']

1

Однако лучше, когда каждый входной параметр будет определённого типа. В этих случаях вы можете перечислить входные параметры объектом, где свойство и значение будут содержать информацию об имени входного параметра и его типе, соответственно:

props: {
  title: String,
  likes: Number,
  isPublished: Boolean,
  commentIds: Array,
  author: Object,
  callback: Function,
  contactsPromise: Promise // или любой другой конструктор
}

1
2
3
4
5
6
7
8
9

Это не только документирует ваш компонент, но также предупреждает пользователей в консоли JavaScript браузера, если они передают неправильный тип. Вы узнаете гораздо больше о проверках типов и других валидациях входных параметров дальше на этой странице.

# Передача статических и динамических входных параметров

До сих пор вы встречали, что во входные параметры передавались статические значения, например:

<blog-post title="My journey with Vue"></blog-post>

1

Вы также встречали входные параметры, присваивающие динамическое значение с помощью v-bind или сокращения :, например:

<!-- Динамически присваиваем значение переменной -->
<blog-post :title="post.title"></blog-post>

<!-- Динамически присваиваем значение комплексного выражения -->
<blog-post :title="post.title + ' by ' + post.author.name"></blog-post>

1
2
3
4
5

В этих двух примерах мы передаём строковые значения, но могут передаваться значения любого типа во входной параметр.

# Передача чисел

<!-- Несмотря на то, что `42` статическое значение, нам нужен v-bind, -->
<!-- чтобы сообщить Vue, что это выражение JavaScript, а не строка.   -->
<blog-post :likes="42"></blog-post>

<!-- Динамическое присвоение значения переменной. -->
<blog-post :likes="post.likes"></blog-post>

1
2
3
4
5
6

# Передача булевых значений

<!-- Указание входного параметра без значения будет означать `true`. -->
<blog-post is-published></blog-post>

<!-- Несмотря на то, что `false` статическое значение, нам нужен v-bind -->
<!-- чтобы сообщить Vue, что это выражение JavaScript, а не строка.     -->
<blog-post :is-published="false"></blog-post>

<!-- Динамическое присвоение значения переменной. -->
<blog-post :is-published="post.currentUserFavorited"></blog-post>

1
2
3
4
5
6
7
8
9

# Передача массивов

<!-- Несмотря на то, что указан статический массив, нам нужен v-bind, -->
<!-- чтобы сообщить Vue, что это выражение JavaScript, а не строка.   -->
<blog-post :comment-ids="[234, 266, 273]"></blog-post>

<!-- Динамическое присвоение значения переменной. -->
<blog-post :comment-ids="post.commentIds"></blog-post>

1
2
3
4
5
6

# Передача объектов

<!-- Несмотря на то, что указан статический объект, нам нужен v-bind, -->
<!-- чтобы сообщить Vue, что это выражение JavaScript, а не строка.   -->
<blog-post
  :author="{ name: 'Veronica', company: 'Veridian Dynamics' }"
></blog-post>

<!-- Динамическое присвоение значения переменной. -->
<blog-post :author="post.author"></blog-post>

1
2
3
4
5
6
7
8

# Передача свойств объекта

Если вы хотите передать все свойства объекта в качестве входных параметров, вы можете использовать v-bind без аргументов (v-bind вместо v-bind:prop-name). Например, для объекта post:

post: {
  id: 1,
  title: 'My Journey with Vue'
}

1
2
3
4

Следующий шаблон:

<blog-post v-bind="post"></blog-post>

1

Будет аналогичен:

<blog-post v-bind:id="post.id" v-bind:title="post.title"></blog-post>

1

# Однонаправленный поток данных

Все входные параметры образуют одностороннюю привязку между дочерним свойством и родительским: когда родительское свойство обновляется — оно будет передаваться дочернему, но не наоборот. Это предотвращает случайное изменение дочерними компонентами родительского состояния, что может затруднить понимание потока данных вашего приложения.

Кроме того, каждый раз, когда обновляется родительский компонент, все входные параметры дочернего компонента будут обновлены актуальными значениями. Это означает, что вы не должны пытаться изменять входной параметр внутри дочернего компонента. Если вы это сделаете, Vue отобразит предупреждение в консоли.

Обычно встречаются два случая, когда возникает соблазн изменять входной параметр:

  1. Входной параметр используется для передачи начального значения; дочерний компонент хочет использовать его как локальное свойство данных в дальнейшем. В этом случае лучше всего определить локальное свойство в данных, которое использует значение входного параметра в качестве начального:
props: ['initialCounter'],
data() {
  return {
    counter: this.initialCounter
  }
}

1
2
3
4
5
6

  1. Входной параметр передаётся как необработанное значение, которое необходимо преобразовать. В этом случае лучше всего определить вычисляемое свойство с использованием входного параметра:
props: ['size'],
computed: {
  normalizedSize: function () {
    return this.size.trim().toLowerCase()
  }
}

1
2
3
4
5
6

Совет

Обратите внимание, что объекты и массивы в JavaScript передаются по ссылке, поэтому если входной параметр является массивом или объектом, то изменение объекта или массива внутри дочернего компонента будет влиять на состояние родителя.

# Валидация входных параметров

Компоненты могут указывать требования к своим входным параметрам, такие как определение типа, которые вы уже видели. Если эти требования не выполнены — Vue предупредит вас сообщением в JavaScript-консоли браузера. Это особенно полезно при разработке компонента, который предназначен для использования другими.

Чтобы указать валидации входного параметра, вы можете предоставить в props объект с валидациями для проверки значения, вместо массива строк. Например:

app.component('my-component', {
  props: {
    // Просто проверка типа (`null` и `undefined` проходят проверку для любого типа)
    propA: Number,
    // Несколько допустимых типов
    propB: [String, Number],
    // Обязательное значение строкового типа
    propC: {
      type: String,
      required: true
    },
    // Число со значением по умолчанию
    propD: {
      type: Number,
      default: 100
    },
    // Объект со значением по умолчанию
    propE: {
      type: Object,
      // Для объектов или массивов значения по умолчанию
      // должны возвращаться из функции
      default: function() {
        return { message: 'hello' }
      }
    },
    // Пользовательская функция для валидации
    propF: {
      validator: function(value) {
        // Значение должно соответствовать одной из этих строк
        return ['success', 'warning', 'danger'].indexOf(value) !== -1
      }
    },
    // Функция с значением по умолчанию
    propG: {
      type: Function,
      // В отличие от объекта или массива по умолчанию, это не фабричная функция — это функция, служащая в качестве значения по умолчанию
      default: function() {
        return 'Функция по умолчанию'
      }
    }
  }
})

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

Когда валидация входного параметра заканчивается ошибкой — Vue выдаст предупреждение в консоли (если используется сборка для разработки).

Совет

Обратите внимание, что входные параметры валидируются перед созданием экземпляра компонента, поэтому свойства экземпляра (например, data, computed и т.д.) не будут доступны внутри default или функций validator.

# Проверка типа

Значением type может быть один из следующих нативных конструкторов:

  • String
  • Number
  • Boolean
  • Array
  • Object
  • Date
  • Function
  • Symbol

Кроме того, type также может быть пользовательской функцией-конструктором и валидация будет выполняться проверкой с помощью instanceof. Например, если существует следующая функция-конструктор:

function Person(firstName, lastName) {
  this.firstName = firstName
  this.lastName = lastName
}

1
2
3
4

Вы можете использовать:

app.component('blog-post', {
  props: {
    author: Person
  }
})

1
2
3
4
5

чтобы проверить, что значение входного параметра author было создано с помощью new Person.

# Регистр в именах входных параметров

Имена HTML-атрибутов не чувствительны к регистру, поэтому любые заглавные символы браузеры будут интерпретировать как строчные. Это значит, что при использовании шаблонов в DOM, входные параметры в camelCase должны быть указаны в kebab-case (разделённые дефисом) эквиваленте:

const app = Vue.createApp({})

app.component('blog-post', {
  // camelCase в JavaScript
  props: ['postTitle'],
  template: '<h3>{{ postTitle }}</h3>'
})

1
2
3
4
5
6
7

<!-- kebab-case в HTML -->
<blog-post post-title="hello!"></blog-post>

1
2

Опять же, при использовании строковых шаблонов это ограничение не применяется.

I’m a bit confused about how to change properties inside components, let’s say I have the following component:

{
    props: {
        visible: {
            type: Boolean,
            default: true
        }
    },
    methods: {
         hide() {
              this.visible = false;
         }
    }
} 

Although it works, it would give the following warning:

Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop’s value. Prop being mutated: «visible»
(found in component )

Now I’m wondering what the best way to handle this is, obviously the visible property is passed in when created the component in the DOM: <Foo :visible="false"></Foo>

Saurabh's user avatar

Saurabh

70.1k40 gold badges177 silver badges241 bronze badges

asked Nov 24, 2016 at 7:53

woutr_be's user avatar

7

Referencing the code in your fiddle

Somehow, you should decide on one place for the state to live, not two. I don’t know whether it’s more appropriate to have it just in the Alert or just in it’s parent for your use case, but you should pick one.

How to decide where state lives

Does the parent or any sibling component depend on the state?

  • Yes: Then it should be in the parent (or in some external state management)
  • No: Then it’s easier to have it in the state of the component itself
  • Kinda both: See below

In some rare cases, you may want a combination. Perhaps you want to give both parent and child the ability to hide the child. Then you should have state in both parent and child (so you don’t have to edit the child’s props inside child).

For example, child can be visible if: visible && state_visible, where visible comes from props and reflects a value in the parent’s state, and state_visible is from the child’s state.

I’m not sure if this is the behavour that you want, but here is a snippet. I would kinda assume you actually want to just call the toggleAlert of the parent component when you click on the child.

var Alert = Vue.component('alert', {
  template: `
        <div class="alert" v-if="visible && state_visible">
        Alert<br> 
        <span v-on:click="close">Close me</span>
      </div>`,
  props: {
    visible: {
      required: true,
      type: Boolean,
      default: false
    }
  },
  data: function() {
    return {
      state_visible: true
    };
  },
  methods: {
    close() {
      console.log('Clock this');
      this.state_visible = false;
    }
  }
});

var demo = new Vue({
  el: '#demo',
  components: {
    'alert': Alert
  },
  data: {
    hasAlerts: false
  },
  methods: {
    toggleAlert() {
      this.hasAlerts = !this.hasAlerts
    }
  }
})
.alert {
  background-color: #ff0000;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="demo" v-cloak>
  <alert :visible="hasAlerts"></alert>

  <span v-on:click="toggleAlert">Toggle alerts</span>
</div>

leonheess's user avatar

leonheess

13.3k13 gold badges67 silver badges104 bronze badges

answered Nov 24, 2016 at 10:52

ArneHugo's user avatar

ArneHugoArneHugo

5,8451 gold badge25 silver badges47 bronze badges

9

According to the Vue.js component doc:

When the parent property updates, it will flow down to the child, but not the other way around. So, how do we communicate back to the parent when something happens? This is where Vue’s custom event system comes in.

Use $emit('my-event) from the child to send an event to the parent. Receive the event on the child declaration inside the parent with v-on:my-event (or @my-event).

Working example:

// child

Vue.component('child', {
  template: '<div><p>Child</p> <button @click="hide">Hide</button></div>',
  methods: {
    hide () {
      this.$emit('child-hide-event')
    }
  },
})

// parent

new Vue({
  el: '#app',
  data: {
    childVisible: true
  },
  methods: {
    childHide () {
      this.childVisible = false
    },
    childShow () {
      this.childVisible = true
    }
  }
})
.box {
  border: solid 1px grey;
  padding: 16px;
}
<script src="https://unpkg.com/vue/dist/vue.min.js"></script>
<div id="app" class="box">
  <p>Parent | childVisible: {{ childVisible }}</p>
  <button @click="childHide">Hide</button>
  <button @click="childShow">Show</button>
  <p> </p>
  <child @child-hide-event="childHide" v-if="childVisible" class="box"></child>
</div>

tony19's user avatar

tony19

117k16 gold badges203 silver badges272 bronze badges

answered Nov 7, 2017 at 0:26

François Romain's user avatar

François RomainFrançois Romain

13.2k17 gold badges87 silver badges120 bronze badges

If the prop is only useful for this child component, give the child a prop like initialVisible, and a data like mutableVisible, and in the created hook (which is called when the component’s data structure is assembled), simply this.mutableVisible = this.initialVisible.

If the prop is shared by other children of the parent component, you’ll need to make it the parent’s data to make it available for all children. Then in the child, this.$emit('visibleChanged', currentVisible) to notify the parent to change visible. In parent’s template, use <ThatChild ... :visibleChanged="setVisible" ...>. Take a look at the guide: https://v2.vuejs.org/v2/guide/components.html

tony19's user avatar

tony19

117k16 gold badges203 silver badges272 bronze badges

answered Nov 24, 2016 at 10:01

JJPandari's user avatar

JJPandariJJPandari

3,4141 gold badge16 silver badges23 bronze badges

After a read of your latest comments it seems that you are concerned about having the logic to show/hide the alerts on the parent. Therefore I would suggest the following:

parent

# template
<alert :alert-visible="alertVisible"></alert>

# script
data () {
  alertVisible: false,
  ...
},
...

Then on the child alert you would $watch the value of the prop and move all logic into the alert:

child (alert)

# script
data: {
  visible: false,
  ...
},
methods: {
  hide () {
    this.visible = false
  },
  show () {
    this.visible = true
  },
  ...
},
props: [
  'alertVisible',
],
watch: {
  alertVisible () {
    if (this.alertVisible && !this.visible) this.show()
    else if (!this.alertVisible && this.visible) this.hide()
  },
  ...
},
...

answered Nov 24, 2016 at 9:52

GuyC's user avatar

GuyCGuyC

6,4741 gold badge29 silver badges43 bronze badges

3

To help anybody, I was facing the same issue. I just changed my var that was inside v-model=»» from props array to data. Remember the difference between props and data, im my case that was not a problem changing it, you should weight your decision.

E.g.:

<v-dialog v-model="dialog" fullscreen hide-overlay transition="dialog-bottom-transition">

Before:

export default {
    data: function () {
        return {
            any-vars: false
        }
    },
    props: {
            dialog: false,
            notifications: false,
            sound: false,
            widgets: false
        },
    methods: {
        open: function () {
            var vm = this;

            vm.dialog = true;
        }
    }
}

After:

export default {
    data: function () {
        return {
            dialog: false
        }
    },
    props: {
            notifications: false,
            sound: false,
            widgets: false
        },
    methods: {
        open: function () {
            var vm = this;

            vm.dialog = true;
        }
    }
}

answered May 22, 2018 at 20:12

Marco's user avatar

MarcoMarco

2,6501 gold badge20 silver badges24 bronze badges

Maybe it looks like on hack and violates the concept of a single data source, but its work)
This solution is creating local proxy variable and inherit data from props. Next work with proxy variable.

Vue.component("vote", {
    data: function() {
        return {
            like_: this.like,
            dislike_: this.dislike,
        }
    },

    props: {
        like: {
            type: [String, Number],
            default: 0
        },
        dislike: {
            type: [String, Number],
            default: 0
        },
        item: {
            type: Object
        }
    },

    template: '<div class="tm-voteing"><span class="tm-vote tm-vote-like" @click="onVote(item, 'like')"><span class="fa tm-icon"></span><span class="tm-vote-count">{{like_}}</span></span><span class="tm-vote tm-vote-dislike" @click="onVote(item, 'dislike')"><span class="fa tm-icon"></span><span class="tm-vote-count">{{dislike_}}</span></span></div>',

    methods: {
        onVote: function(data, action) {
            var $this = this;
            // instead of jquery ajax can be axios or vue-resource
            $.ajax({
                method: "POST",
                url: "/api/vote/vote",
                data: {id: data.id, action: action},
                success: function(response) {
                    if(response.status === "insert") {
                        $this[action + "_"] = Number($this[action + "_"]) + 1;
                    } else {
                        $this[action + "_"] = Number($this[action + "_"]) - 1;
                    }
                },
                error: function(response) {
                    console.error(response);
                }
            });
        }
    }
});

use component and pass props

<vote :like="item.vote_like" :dislike="item.vote_dislike" :item="item"></vote>

answered Jul 15, 2017 at 19:17

Vitaliy's user avatar

VitaliyVitaliy

4904 silver badges6 bronze badges

I wonder why it is missed by others when the warning has a hint

Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop’s value. Prop being mutated: «visible» (found in component )

Try creating a computed property out of the prop received in the child component as

computed: {
  isVisible => this.visible
}

And use this computed in your child component as well as to emit the changes to your parent.

vsync's user avatar

vsync

113k55 gold badges297 silver badges382 bronze badges

answered Apr 13, 2020 at 18:23

Saksham's user avatar

SakshamSaksham

8,9056 gold badges44 silver badges72 bronze badges

You don’t have to use the JSON object.

const child = {
  props:["user"],
  data(){
    return {
      localUser: Object.assign({}, this.user)
    }
  }
}

Use localUser (or whatever you want to call it) inside your child.

Edit

I had modified a fiddle created for another answer to this question to demonstrate the above concept and @user3743266 asked

I’m coming to grips with this myself, and I’m finding this very
useful. Your example works well. In the child, you’ve created an
element in data that takes a copy of the prop, and the child works
with the copy. Interesting and useful, but… it’s not clear to me
when the local copy gets updated if something else modifies the
parent. I modified your fiddle, removing the v-ifs so everything is
visible, and duplicating the edit component. If you modify name in one
component, the other is orphaned and gets no changes?

The current component looks like this:

Vue.component('edit-user', {
  template: `
  <div>
    <input type="text" v-model="localUser.name">
    <button @click="$emit('save', localUser)">Save</button>
    <button @click="$emit('cancel')">Cancel</button>
  </div>
  `,
  props: ['user'],
  data() {
    return {
      localUser: Object.assign({}, this.user)
    }
  }
})

Because I made the design decision to use a local copy of the user, @user3743266 is correct, the component is not automatically updated. The property user is updated, but localUser is not. In this case, if you wanted to automatically update local data whenever the property changed, you would need a watcher.

Vue.component('edit-user', {
  template: `
  <div>
    <input type="text" v-model="localUser.name">
    <button @click="$emit('save', localUser)">Save</button>
    <button @click="$emit('cancel')">Cancel</button>
  </div>
  `,
  props: ['user'],
  data() {
    return {
      localUser: Object.assign({}, this.user)
    }
  },
  watch:{
    user(newUser){
        this.localUser = Object.assign({}, newUser)
    }
  }
})

Here is the updated fiddle.

This allows you full control over when/if the local data is updated or emitted. For example, you might want to check a condition before updating the local state.

  watch:{
    user(newUser){
      if (condition)
        this.localUser = Object.assign({}, newUser)
    }
  }

As I said elsewhere, there are times when you might want to take advantage of objects properties being mutable, but there are also times like this where you might want more control.

You don’t have to use the JSON object.

const child = {
  props:["user"],
  data(){
    return {
      localUser: Object.assign({}, this.user)
    }
  }
}

Use localUser (or whatever you want to call it) inside your child.

Edit

I had modified a fiddle created for another answer to this question to demonstrate the above concept and @user3743266 asked

I’m coming to grips with this myself, and I’m finding this very
useful. Your example works well. In the child, you’ve created an
element in data that takes a copy of the prop, and the child works
with the copy. Interesting and useful, but… it’s not clear to me
when the local copy gets updated if something else modifies the
parent. I modified your fiddle, removing the v-ifs so everything is
visible, and duplicating the edit component. If you modify name in one
component, the other is orphaned and gets no changes?

The current component looks like this:

Vue.component('edit-user', {
  template: `
  <div>
    <input type="text" v-model="localUser.name">
    <button @click="$emit('save', localUser)">Save</button>
    <button @click="$emit('cancel')">Cancel</button>
  </div>
  `,
  props: ['user'],
  data() {
    return {
      localUser: Object.assign({}, this.user)
    }
  }
})

Because I made the design decision to use a local copy of the user, @user3743266 is correct, the component is not automatically updated. The property user is updated, but localUser is not. In this case, if you wanted to automatically update local data whenever the property changed, you would need a watcher.

Vue.component('edit-user', {
  template: `
  <div>
    <input type="text" v-model="localUser.name">
    <button @click="$emit('save', localUser)">Save</button>
    <button @click="$emit('cancel')">Cancel</button>
  </div>
  `,
  props: ['user'],
  data() {
    return {
      localUser: Object.assign({}, this.user)
    }
  },
  watch:{
    user(newUser){
        this.localUser = Object.assign({}, newUser)
    }
  }
})

Here is the updated fiddle.

This allows you full control over when/if the local data is updated or emitted. For example, you might want to check a condition before updating the local state.

  watch:{
    user(newUser){
      if (condition)
        this.localUser = Object.assign({}, newUser)
    }
  }

As I said elsewhere, there are times when you might want to take advantage of objects properties being mutable, but there are also times like this where you might want more control.

Однонаправленный поток данных

Последнее обновление: 29.09.2017

При использовании props в компонентах следует учитывать, что данные в props представляют однонаправленный поток данных от родительского компонента к
дочерним компонентам. Изменение свойства родителя приведет к изменению в дочерних компонентах. Однако дочерние компоненты не могут изменить свойство родителя.
То есть поток данных идет только в одном направлении: родителя к потомкам. Кроме того, если мы попробуем изменить в дочерних компонентах
значения, переданные через props, то на консоль браузера будет выведено предупреждение о том, что это не следует делать.

Более того при любом обновлении родительского компонента у дочерних компонентов обновляются значения, передаваемые через props.
Например, рассмотрим следующую ситуацию:

<!DOCTYPE html>
<html>
<head>
<title>Компоненты Vue.js</title>
<meta charset="utf-8" />
</head>
<body>
<div id="app">
	<h2>Hello, {{name}}</h2>
    <useredit :user="name"></useredit>
	<button v-on:click="resetName">Set Name</button>
</div>
<script src="https://unpkg.com/vue@2.7.14/dist/vue.js"></script>
<script>
Vue.component('useredit', {
	props: ["user"],
	template: '<div><input type="text" v-model="user" /><p>Name: {{user}}</p></div>'
});
new Vue({
    el: "#app",
    data: {
        name: 'Tom'
    },
	methods:{
		resetName: function(){
			this.name = 'Bob';
		}
	}
});
</script>
</body>
</html>

Из родителя — объекта Vue в компонент useredit через props передается значение name. В этом компоненте мы можем попробовать управлять переданным значением через
поле ввода и механизм двусторонней привязки:

<input type="text" v-model="user" />

Но если мы попробуем изменить через поле ввода данное значение, то оно изменится только в рамках компонента useredit, а на консоль будет выведено предупреждение:

Mutating props in Vue.js

Значение свойства name в родительском объекте Vue никак не будет затронуто. Почему так происходит? Дело в том, что данные простых типов — String, Number, Boolean передаются по значению, то есть в дочерний компонент
передается копия значения. И в данном случае в компонент useredit как раз передается копия строки name. Соответственно все действия с этой копией никак не повлияют на
родительский объект.

В отличие от простых типов сложные объекты и массивы передаются по ссылке. Поэтому из дочерних компонентов мы можем изменять массивы или сложные объекты, которые определены
в родительских объектах.

Более того в объекте Vue посредством нажатия кнопки мы можем вызвать метод resetName, который изменит свойство name с Tom на Bob:

Mutating props in components in Vue.js

То есть несмотря на то, что внутри компонента useredit мы меняем значение из props, но изменение родителя — объекта Vue приведет к изменению дочернего компонента.

Однако могут быть ситуации, когда нам все таки нужно изменить значения в props и при этом не допустить изменения этих значений извне.
Что делать в этом случае? В этом случае мы можем определить локальную переменную. Через props устанавливается ее начальное значение, и затем компонент работает только с этой переменной:

<div id="app">
	<h2>Hello, {{name}}</h2>
    <useredit :user="name"></useredit>
	<button v-on:click="resetName">Set Name</button>
</div>
<script src="https://unpkg.com/vue@2.7.14/dist/vue.js"></script>
<script>
Vue.component('useredit', {
	props: ["user"],
	data: function () {
	  return { userName: this.user}
	},
	template: '<div><input type="text" v-model="userName" /><p>Name: {{userName}}</p></div>'
});
new Vue({
    el: "#app",
    data: {
        name: 'Tom'
    },
	methods:{
		resetName: function(){
			this.name = 'Bob';
		}
	}
});
</script>

Теперь все действия будут идти с локальной переменной userName. Значения из props в компоненте не будут изменяться, а изменения в родительском компоненте
никак не повлияют на значение переменной userName.

How to Use Props in Vue.js

We use props to pass information/data from a parent component to child components. In this article, I will explain everything you need to know about props and why you should use props in Vue.js.

Here’s a brief outline of what we’ll cover in this guide:

  • What are props in Vue.js?
  • How to register props inside a component
  • How to work with multiple props
  • Vue.js prop types
  • How to pass data to props
  • How to pass functions to props
  • How to validate props
  • How to set default values for props

What are Props in Vue.js?

“Props” is a special keyword which stands for properties. It can be registered on a component to pass data from a parent component to one of its children components.

This is a lot easier compared to using state management libraries like vuex for Vue.js applications.

Data in props can only flow one way – from the top, or parent component, to the bottom, or child components. This simply means you cannot pass data from a child to a parent.

Another thing to keep in mind is that Props are read-only and cannot be modified by the child component because the parent component «owns» that value.

Let’s balance things up now – the parent components pass props to the child component(s) while the child component emit events to the parent component(s).

How to Register Props Inside a Component

Let’s now take a look at how we can register props inside a component.

Vue.component('user-detail', {
  props: ['name'],
  template: '<p>Hi {{ name }}</p>'
})
.js

or, in a Vue Single File Component:

<template>
  <p>{{ name }}</p>
</template>

<script>
export default {
  props: ['name']
}
</script>

In the above code, we registered a prop called name which we can call in the template section of our app.

Note: This is the child component and this prop is going to receive data from the parent component. I will explain this more later.

How to Work with Multiple Props

You can have more than one prop by appending them to the props array, just like this:

Vue.component('user-detail', {
  props: ['firstName', 'lastName'],
  template: '<p>Hi {{ firstName }} {{ lastName }}</p>'
})

or, in a Vue Single File Component:

<template>
  <p>Hi {{ firstName }} {{ lastName }}</p>
</template>

<script>
export default {
  props: [
    'firstName', 
    'lastName'
  ],
}
</script>

Vue.js Prop Types

To specify the type of prop you want to use in Vue, you will use an object instead of an array. You’ll use the name of the property as the key of each property, and the type as the value.

If the type of the data passed does not match the prop type, Vue sends an alert (in development mode) in the console with a warning. The valid types you can use are:

  • String
  • Number
  • Boolean
  • Array
  • Object
  • Date
  • Function
  • Symbol
Vue.component('user-detail', {
  props: {
    firstName: String,
    lastName: String
  },
  template: '<p>Hi {{ firstName }} {{ lastName }}</p>'
})

or, in a Vue Single File Component:

<template>
  <p>Hi {{ firstName }} {{ lastName }}</p>
</template>

<script>
export default {
  props: {
    firstName: String,
    lastName: String
  },
}
</script>

How to Pass Data to Props in Vue

The major goal of using props is to pass down data/information. You can either pass your value as a data property using v-bind, such as in this code:

<template>
  <ComponentName :title=title />
</template>

<script>
export default {
  //...
  data() {
    return {
      title: 'Understanding Props in vuejs'
    }
  },
  //...
}
</script>

or as a static value like this:

<ComponentName title="Understanding Props in vuejs" />

Suppose we are building an app which has so many buttons with different text/background colors. Instead of repeating the button syntax in all our files, it’s better to create a button component and then pass the text/background colors as props.

Here’s the parent component:

<template>
  <div id="app">
    <Button :name='btnName' bgColor='red' />
    <Button :name='btnName' bgColor='green' />
    <Button :name='btnName' bgColor='blue' />
  </div>
</template>

<script>
import Button from './components/Button'

export default {
  name: 'App',
  data(){
    return{
      btnName:"Joel",
    }
  },
  components: {
    Button
  }
}
</script>

And here’s the child component:

<template>
  <button class="btn" :style="{backgroundColor:bgColor}">{{name}}</button>
</template>
<script>
export default {
  name: 'Button',
  props:{
    name:String,
    bgColor:String
  }
}
</script>

The above code shows you how to use both data property and static values when you’re getting data from a parent component and using that data in a child component.

Note: you can also use a ternary operator inside the prop value to check a truthy condition and pass a value that depends on it.

<template>
  <div id="app">
    <Button :tagUser="signedUp ? 'Logout' : 'Login'" bgColor='red' />
  </div>
</template>
<script>
import Button from './components/Button'
export default {
  name: 'App',
  data(){
    return{
      signedUp: true,
    }
  },
  components: {
    Button
  }
}
</script>

In the above code, we are checking the signedUp data property. If it’s true, the data sent should be Logout, else it should be Login.

How to Pass Functions to Props

Passing a function or a method down to a child component as a prop is relatively straightforward. It’s basically the same process as passing any other variable.

But there are reasons why you shouldn’t use props as functions – instead you should use emit. This article properly explains why.

<template>
  <ChildComponent :function="newFunction" />
</template>
<script>
export default {
  methods: {
    newFunction() {
      // ...
    }
  }
};
</script>

How to Validate Props in Vue

Vue makes validating props very easy. All you have to do is add the required key and its value to the prop. We can validate with both the prop type and by using required:

props: {
  name: {
    type: String,
    required: true
  }
}

How to Set Default Prop Values

Before concluding this article, let’s now see how to set default values to our props. Default values are rendered if the child component is unable to get data from the parent component.

Vue allows you to specify a default value, just as we specified required earlier.

props: {
  name: {
    type: String,
    required: true,
    default: 'John Doe'
  },
  img: {
    type: String,
    default: '../image-path/image-name.jpg',
   },
}

You can also define the default value as an object. And it can be a function that returns an appropriate value, rather than being the actual value.

Conclusion

In this article, we have learned what props do and how props works in Vue.js.

In summary, we use props to pass down data from the parent components to the child component(s). The child component also emit events to the parent component(s) in case you need to send data/events from the child to the parent component.

image-50

Thank you for reading!

Useful Links

  • Vue.js Component Props — flaviocopes
  • Props — Vue documentation


Learn to code for free. freeCodeCamp’s open source curriculum has helped more than 40,000 people get jobs as developers. Get started

title type order

Props

guide

102

<script>
const __pageRedirects = {
‘#Non-Prop-Attributes’: ‘/guide/components/attrs.html’
}
</script>

This page assumes you’ve already read the Components Basics. Read that first if you are new to components.

Prop Casing (camelCase vs kebab-case)

HTML attribute names are case-insensitive, so browsers will interpret any uppercase characters as lowercase. That means when you’re using in-DOM templates, camelCased prop names need to use their kebab-cased (hyphen-delimited) equivalents:

Vue.component('blog-post', {
  // camelCase in JavaScript
  props: ['postTitle'],
  template: '<h3>{{ postTitle }}</h3>'
})
<!-- kebab-case in HTML -->
<blog-post post-title="hello!"></blog-post>

Again, if you’re using string templates, this limitation does not apply.

Prop Types

So far, we’ve only seen props listed as an array of strings:

props: ['title', 'likes', 'isPublished', 'commentIds', 'author']

Usually though, you’ll want every prop to be a specific type of value. In these cases, you can list props as an object, where the properties’ names and values contain the prop names and types, respectively:

props: {
  title: String,
  likes: Number,
  isPublished: Boolean,
  commentIds: Array,
  author: Object,
  callback: Function,
  contactsPromise: Promise // or any other constructor
}

This not only documents your component, but will also warn users in the browser’s JavaScript console if they pass the wrong type. You’ll learn much more about type checks and other prop validations further down this page.

Passing Static or Dynamic Props

So far, you’ve seen props passed a static value, like in:

<blog-post title="My journey with Vue"></blog-post>

You’ve also seen props assigned dynamically with v-bind, such as in:

<!-- Dynamically assign the value of a variable -->
<blog-post v-bind:title="post.title"></blog-post>

<!-- Dynamically assign the value of a complex expression -->
<blog-post
  v-bind:title="post.title + ' by ' + post.author.name"
></blog-post>

In the two examples above, we happen to pass string values, but any type of value can actually be passed to a prop.

Passing a Number

<!-- Even though `42` is static, we need v-bind to tell Vue that -->
<!-- this is a JavaScript expression rather than a string.       -->
<blog-post v-bind:likes="42"></blog-post>

<!-- Dynamically assign to the value of a variable. -->
<blog-post v-bind:likes="post.likes"></blog-post>

Passing a Boolean

<!-- Including the prop with no value will imply `true`. -->
<blog-post is-published></blog-post>

<!-- Even though `false` is static, we need v-bind to tell Vue that -->
<!-- this is a JavaScript expression rather than a string.          -->
<blog-post v-bind:is-published="false"></blog-post>

<!-- Dynamically assign to the value of a variable. -->
<blog-post v-bind:is-published="post.isPublished"></blog-post>

Passing an Array

<!-- Even though the array is static, we need v-bind to tell Vue that -->
<!-- this is a JavaScript expression rather than a string.            -->
<blog-post v-bind:comment-ids="[234, 266, 273]"></blog-post>

<!-- Dynamically assign to the value of a variable. -->
<blog-post v-bind:comment-ids="post.commentIds"></blog-post>

Passing an Object

<!-- Even though the object is static, we need v-bind to tell Vue that -->
<!-- this is a JavaScript expression rather than a string.             -->
<blog-post
  v-bind:author="{
    name: 'Veronica',
    company: 'Veridian Dynamics'
  }"
></blog-post>

<!-- Dynamically assign to the value of a variable. -->
<blog-post v-bind:author="post.author"></blog-post>

Passing the Properties of an Object

If you want to pass all the properties of an object as props, you can use v-bind without an argument (v-bind instead of v-bind:prop-name). For example, given a post object:

post: {
  id: 1,
  title: 'My Journey with Vue'
}

The following template:

<blog-post v-bind="post"></blog-post>

Will be equivalent to:

<blog-post
  v-bind:id="post.id"
  v-bind:title="post.title"
></blog-post>

One-Way Data Flow

All props form a one-way-down binding between the child property and the parent one: when the parent property updates, it will flow down to the child, but not the other way around. This prevents child components from accidentally mutating the parent’s state, which can make your app’s data flow harder to understand.

In addition, every time the parent component is updated, all props in the child component will be refreshed with the latest value. This means you should not attempt to mutate a prop inside a child component. If you do, Vue will warn you in the console.

There are usually two cases where it’s tempting to mutate a prop:

  1. The prop is used to pass in an initial value; the child component wants to use it as a local data property afterwards. In this case, it’s best to define a local data property that uses the prop as its initial value:
props: ['initialCounter'],
data: function () {
  return {
    counter: this.initialCounter
  }
}
  1. The prop is passed in as a raw value that needs to be transformed. In this case, it’s best to define a computed property using the prop’s value:
props: ['size'],
computed: {
  normalizedSize: function () {
    return this.size.trim().toLowerCase()
  }
}

Note that objects and arrays in JavaScript are passed by reference, so if the prop is an array or object, mutating the object or array itself inside the child component **will** affect parent state.

Prop Validation

Components can specify requirements for their props, such as the types you’ve already seen. If a requirement isn’t met, Vue will warn you in the browser’s JavaScript console. This is especially useful when developing a component that’s intended to be used by others.

To specify prop validations, you can provide an object with validation requirements to the value of props, instead of an array of strings. For example:

Vue.component('my-component', {
  props: {
    // Basic type check (`null` and `undefined` values will pass any type validation)
    propA: Number,
    // Multiple possible types
    propB: [String, Number],
    // Required string
    propC: {
      type: String,
      required: true
    },
    // Number with a default value
    propD: {
      type: Number,
      default: 100
    },
    // Object with a default value
    propE: {
      type: Object,
      // Object or array defaults must be returned from
      // a factory function
      default: function () {
        return { message: 'hello' }
      }
    },
    // Custom validator function
    propF: {
      validator: function (value) {
        // The value must match one of these strings
        return ['success', 'warning', 'danger'].includes(value)
      }
    }
  }
})

When prop validation fails, Vue will produce a console warning (if using the development build).

Note that props are validated **before** a component instance is created, so instance properties (e.g. `data`, `computed`, etc) will not be available inside `default` or `validator` functions.

Type Checks

The type can be one of the following native constructors:

  • String
  • Number
  • Boolean
  • Array
  • Object
  • Date
  • Function
  • Symbol

In addition, type can also be a custom constructor function and the assertion will be made with an instanceof check. For example, given the following constructor function exists:

function Person (firstName, lastName) {
  this.firstName = firstName
  this.lastName = lastName
}

You could use:

Vue.component('blog-post', {
  props: {
    author: Person
  }
})

to validate that the value of the author prop was created with new Person.

Non-Prop Attributes

A non-prop attribute is an attribute that is passed to a component, but does not have a corresponding prop defined.

While explicitly defined props are preferred for passing information to a child component, authors of component libraries can’t always foresee the contexts in which their components might be used. That’s why components can accept arbitrary attributes, which are added to the component’s root element.

For example, imagine we’re using a 3rd-party bootstrap-date-input component with a Bootstrap plugin that requires a data-date-picker attribute on the input. We can add this attribute to our component instance:

<bootstrap-date-input data-date-picker="activated"></bootstrap-date-input>

And the data-date-picker="activated" attribute will automatically be added to the root element of bootstrap-date-input.

Replacing/Merging with Existing Attributes

Imagine this is the template for bootstrap-date-input:

<input type="date" class="form-control">

To specify a theme for our date picker plugin, we might need to add a specific class, like this:

<bootstrap-date-input
  data-date-picker="activated"
  class="date-picker-theme-dark"
></bootstrap-date-input>

In this case, two different values for class are defined:

  • form-control, which is set by the component in its template
  • date-picker-theme-dark, which is passed to the component by its parent

For most attributes, the value provided to the component will replace the value set by the component. So for example, passing type="text" will replace type="date" and probably break it! Fortunately, the class and style attributes are a little smarter, so both values are merged, making the final value: form-control date-picker-theme-dark.

Disabling Attribute Inheritance

If you do not want the root element of a component to inherit attributes, you can set inheritAttrs: false in the component’s options. For example:

Vue.component('my-component', {
  inheritAttrs: false,
  // ...
})

This can be especially useful in combination with the $attrs instance property, which contains the attribute names and values passed to a component, such as:

{
  required: true,
  placeholder: 'Enter your username'
}

With inheritAttrs: false and $attrs, you can manually decide which element you want to forward attributes to, which is often desirable for base components:

Vue.component('base-input', {
  inheritAttrs: false,
  props: ['label', 'value'],
  template: `
    <label>
      {{ label }}
      <input
        v-bind="$attrs"
        v-bind:value="value"
        v-on:input="$emit('input', $event.target.value)"
      >
    </label>
  `
})

Note that `inheritAttrs: false` option does **not** affect `style` and `class` bindings.

This pattern allows you to use base components more like raw HTML elements, without having to care about which element is actually at its root:

<base-input
  label="Username:"
  v-model="username"
  required
  placeholder="Enter your username"
></base-input>

Понравилась статья? Поделить с друзьями:
  • Как изменить props react
  • Как изменить shell windows
  • Как изменить print python
  • Как изменить primary key sql
  • Как изменить primary key oracle