From b5cd524830bce0f68d322eb909f5399d2a23ae04 Mon Sep 17 00:00:00 2001 From: sdras Date: Sat, 10 Mar 2018 13:34:11 +0000 Subject: [PATCH 01/42] create components sidebar --- themes/vue/layout/partials/toc.ejs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/themes/vue/layout/partials/toc.ejs b/themes/vue/layout/partials/toc.ejs index 1d13a8aa9b..8dba87f046 100644 --- a/themes/vue/layout/partials/toc.ejs +++ b/themes/vue/layout/partials/toc.ejs @@ -5,6 +5,9 @@ <% if (fileName === 'installation') { %>
  • Essentials

  • <% } %> + <% if (fileName === 'components') { %> +
  • Components

  • + <% } %> <% if (fileName === 'transitions') { %>
  • Transitions & Animation

  • <% } %> From 38ae34cc24640824c9bbf5e5bd92b50e8263dd01 Mon Sep 17 00:00:00 2001 From: sdras Date: Sat, 10 Mar 2018 13:50:46 +0000 Subject: [PATCH 02/42] break up first two sections --- src/v2/guide/components.md | 284 +----------------------- src/v2/guide/custom-events.md | 396 ++++++++++++++++++++++++++++++++++ src/v2/guide/props.md | 285 ++++++++++++++++++++++++ 3 files changed, 682 insertions(+), 283 deletions(-) create mode 100644 src/v2/guide/custom-events.md create mode 100644 src/v2/guide/props.md diff --git a/src/v2/guide/components.md b/src/v2/guide/components.md index 8db71229a4..29bcbb894b 100644 --- a/src/v2/guide/components.md +++ b/src/v2/guide/components.md @@ -1,5 +1,5 @@ --- -title: Components +title: Using Components type: guide order: 11 --- @@ -225,288 +225,6 @@ In Vue, the parent-child component relationship can be summarized as **props dow props down, events up

    -## Props - -### Passing Data with Props - -Every component instance has its own **isolated scope**. This means you cannot (and should not) directly reference parent data in a child component's template. Data can be passed down to child components using **props**. - -A prop is a custom attribute for passing information from parent components. A child component needs to explicitly declare the props it expects to receive using the [`props` option](../api/#props): - -``` js -Vue.component('child', { - // declare the props - props: ['message'], - // like data, the prop can be used inside templates and - // is also made available in the vm as this.message - template: '{{ message }}' -}) -``` - -Then we can pass a plain string to it like so: - -``` html - -``` - -Result: - -{% raw %} -
    - -
    - -{% endraw %} - -### camelCase vs. kebab-case - -HTML attributes are case-insensitive, so when using non-string templates, camelCased prop names need to use their kebab-case (hyphen-delimited) equivalents: - -``` js -Vue.component('child', { - // camelCase in JavaScript - props: ['myMessage'], - template: '{{ myMessage }}' -}) -``` - -``` html - - -``` - -Again, if you're using string templates, then this limitation does not apply. - -### Dynamic Props - -Similar to binding a normal attribute to an expression, we can also use `v-bind` for dynamically binding props to data on the parent. Whenever the data is updated in the parent, it will also flow down to the child: - -``` html -
    - -
    - -
    -``` - -``` js -new Vue({ - el: '#prop-example-2', - data: { - parentMsg: 'Message from parent' - } -}) -``` - -You can also use the shorthand syntax for `v-bind`: - -``` html - -``` - -Result: - -{% raw %} -
    - -
    - -
    - -{% endraw %} - -If you want to pass all the properties in an object as props, you can use `v-bind` without an argument (`v-bind` instead of `v-bind:prop-name`). For example, given a `todo` object: - -``` js -todo: { - text: 'Learn Vue', - isComplete: false -} -``` - -Then: - -``` html - -``` - -Will be equivalent to: - -``` html - -``` - -### Literal vs. Dynamic - -A common mistake beginners tend to make is attempting to pass down a number using the literal syntax: - -``` html - - -``` - -However, since this is a literal prop, its value is passed down as a plain string `"1"` instead of an actual number. If we want to pass down an actual JavaScript number, we need to use `v-bind` so that its value is evaluated as a JavaScript expression: - -``` html - - -``` - -### 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. - -2. The prop is passed in as a raw value that needs to be transformed. - -The proper answer to these use cases are: - -1. Define a local data property that uses the prop's initial value as its initial value: - - ``` js - props: ['initialCounter'], - data: function () { - return { counter: this.initialCounter } - } - ``` - -2. Define a computed property that is computed from the prop's value: - - ``` js - 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 **will** affect parent state.

    - -### Prop Validation - -It is possible for a component to specify requirements for the props it is receiving. If a requirement is not met, Vue will emit warnings. This is especially useful when you are authoring a component that is intended to be used by others. - -Instead of defining the props as an array of strings, you can use an object with validation requirements: - -``` js -Vue.component('example', { - props: { - // basic type check (`null` means accept any type) - propA: Number, - // multiple possible types - propB: [String, Number], - // a required string - propC: { - type: String, - required: true - }, - // a number with default value - propD: { - type: Number, - default: 100 - }, - // object/array defaults should be returned from a - // factory function - propE: { - type: Object, - default: function () { - return { message: 'hello' } - } - }, - // custom validator function - propF: { - validator: function (value) { - return value > 10 - } - } - } -}) -``` - -The `type` can be one of the following native constructors: - -- String -- Number -- Boolean -- Function -- Object -- Array -- Symbol - -In addition, `type` can also be a custom constructor function and the assertion will be made with an `instanceof` check. - -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 within `default` or `validator` functions, instance properties such as from `data`, `computed`, or `methods` will not be available. - -## 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 `bs-date-input` component with a Bootstrap plugin that requires a `data-3d-date-picker` attribute on the `input`. We can add this attribute to our component instance: - -``` html - -``` - -And the `data-3d-date-picker="true"` attribute will automatically be added to the root element of `bs-date-input`. - -### Replacing/Merging with Existing Attributes - -Imagine this is the template for `bs-date-input`: - -``` html - -``` - -To specify a theme for our date picker plugin, we might need to add a specific class, like this: - -``` html - -``` - -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="large"` 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`. - ## Custom Events We have learned that the parent can pass data down to the child using props, but how do we communicate back to the parent when something happens? This is where Vue's custom event system comes in. diff --git a/src/v2/guide/custom-events.md b/src/v2/guide/custom-events.md new file mode 100644 index 0000000000..4eff31a708 --- /dev/null +++ b/src/v2/guide/custom-events.md @@ -0,0 +1,396 @@ +--- +title: Custom Events +type: guide +order: 13 +--- + +We have learned that the parent can pass data down to the child using props, but how do we communicate back to the parent when something happens? This is where Vue's custom event system comes in. + +## Using `v-on` with Custom Events + +Every Vue instance implements an [events interface](../api/#Instance-Methods-Events), which means it can: + +- Listen to an event using `$on(eventName)` +- Trigger an event using `$emit(eventName, optionalPayload)` + +

    Note that Vue's event system is different from the browser's [EventTarget API](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget). Though they work similarly, `$on` and `$emit` are __not__ aliases for `addEventListener` and `dispatchEvent`.

    + +In addition, a parent component can listen to the events emitted from a child component using `v-on` directly in the template where the child component is used. + +

    You cannot use `$on` to listen to events emitted by children. You must use `v-on` directly in the template, as in the example below.

    + +Here's an example: + +``` html +
    +

    {{ total }}

    + + +
    +``` + +``` js +Vue.component('button-counter', { + template: '', + data: function () { + return { + counter: 0 + } + }, + methods: { + incrementCounter: function () { + this.counter += 1 + this.$emit('increment') + } + }, +}) + +new Vue({ + el: '#counter-event-example', + data: { + total: 0 + }, + methods: { + incrementTotal: function () { + this.total += 1 + } + } +}) +``` + +{% raw %} +
    +

    {{ total }}

    + + +
    + +{% endraw %} + +In this example, it's important to note that the child component is still completely decoupled from what happens outside of it. All it does is report information about its own activity, just in case a parent component might care. + + +Here's an example on how to use payload data: + +``` html +
    +

    {{ msg }}

    + +
    +``` + +``` js +Vue.component('button-message', { + template: `
    + + +
    `, + data: function () { + return { + message: 'test message' + } + }, + methods: { + handleSendMessage: function () { + this.$emit('message', { message: this.message }) + } + } +}) + +new Vue({ + el: '#message-event-example', + data: { + messages: [] + }, + methods: { + handleMessage: function (payload) { + this.messages.push(payload.message) + } + } +}) +``` + +{% raw %} +
    +

    {{ msg }}

    + +
    + +{% endraw %} + +In this second example, it's important to note that the child component is still completely decoupled from what happens outside of it. All it does is report information about its own activity including a payload data into event emitter, just in case a parent component might care. + +## Binding Native Events to Components + +There may be times when you want to listen for a native event on the root element of a component. In these cases, you can use the `.native` modifier for `v-on`. For example: + +``` html + +``` + +## `.sync` Modifier + +> 2.3.0+ + +In some cases we may need "two-way binding" for a prop - in fact, in Vue 1.x this is exactly what the `.sync` modifier provided. When a child component mutates a prop that has `.sync`, the value change will be reflected in the parent. This is convenient, however it leads to maintenance issues in the long run because it breaks the one-way data flow assumption: the code that mutates child props are implicitly affecting parent state. + +This is why we removed the `.sync` modifier when 2.0 was released. However, we've found that there are indeed cases where it could be useful, especially when shipping reusable components. What we need to change is **making the code in the child that affects parent state more consistent and explicit.** + +In 2.3.0+ we re-introduced the `.sync` modifier for props, but this time it is only syntax sugar that automatically expands into an additional `v-on` listener: + +The following + +``` html + +``` + +is expanded into: + +``` html + +``` + +For the child component to update `foo`'s value, it needs to explicitly emit an event instead of mutating the prop: + +``` js +this.$emit('update:foo', newValue) +``` + +The `.sync` modifier can also be used with `v-bind` when using an object to set multiple properties at once: + +```html + +``` + +This has the effect of adding `v-on` update listeners for both `foo` and `bar`. + +## Form Input Components using Custom Events + +Custom events can also be used to create custom inputs that work with `v-model`. Remember: + +``` html + +``` + +is syntactic sugar for: + +``` html + +``` + +When used with a component, it instead simplifies to: + +``` html + + +``` + +So for a component to work with `v-model`, it should (these can be configured in 2.2.0+): + +- accept a `value` prop +- emit an `input` event with the new value + +Let's see it in action with a simple currency input: + +``` html + +``` + +``` js +Vue.component('currency-input', { + template: '\ + \ + $\ + \ + \ + ', + props: ['value'], + methods: { + // Instead of updating the value directly, this + // method is used to format and place constraints + // on the input's value + updateValue: function (value) { + var formattedValue = value + // Remove whitespace on either side + .trim() + // Shorten to 2 decimal places + .slice( + 0, + value.indexOf('.') === -1 + ? value.length + : value.indexOf('.') + 3 + ) + // If the value was not already normalized, + // manually override it to conform + if (formattedValue !== value) { + this.$refs.input.value = formattedValue + } + // Emit the number value through the input event + this.$emit('input', Number(formattedValue)) + } + } +}) +``` + +{% raw %} +
    + +
    + +{% endraw %} + +The implementation above is pretty naive though. For example, users are allowed to enter multiple periods and even letters sometimes - yuck! So for those that want to see a non-trivial example, here's a more robust currency filter: + + + +## Customizing Component `v-model` + +> New in 2.2.0+ + +By default, `v-model` on a component uses `value` as the prop and `input` as the event, but some input types such as checkboxes and radio buttons may want to use the `value` prop for a different purpose. Using the `model` option can avoid the conflict in such cases: + +``` js +Vue.component('my-checkbox', { + model: { + prop: 'checked', + event: 'change' + }, + props: { + checked: Boolean, + // this allows using the `value` prop for a different purpose + value: String + }, + // ... +}) +``` + +``` html + +``` + +The above will be equivalent to: + +``` html + + +``` + +

    Note that you still have to declare the `checked` prop explicitly.

    + +## Non Parent-Child Communication + +Sometimes two components may need to communicate with one-another but they are not parent/child to each other. In simple scenarios, you can use an empty Vue instance as a central event bus: + +``` js +var bus = new Vue() +``` +``` js +// in component A's method +bus.$emit('id-selected', 1) +``` +``` js +// in component B's created hook +bus.$on('id-selected', function (id) { + // ... +}) +``` + +In more complex cases, you should consider employing a dedicated [state-management pattern](state-management.html). \ No newline at end of file diff --git a/src/v2/guide/props.md b/src/v2/guide/props.md new file mode 100644 index 0000000000..dd7f9b660e --- /dev/null +++ b/src/v2/guide/props.md @@ -0,0 +1,285 @@ +--- +title: Props +type: guide +order: 12 +--- + +## Passing Data with Props + +Every component instance has its own **isolated scope**. This means you cannot (and should not) directly reference parent data in a child component's template. Data can be passed down to child components using **props**. + +A prop is a custom attribute for passing information from parent components. A child component needs to explicitly declare the props it expects to receive using the [`props` option](../api/#props): + +``` js +Vue.component('child', { + // declare the props + props: ['message'], + // like data, the prop can be used inside templates and + // is also made available in the vm as this.message + template: '{{ message }}' +}) +``` + +Then we can pass a plain string to it like so: + +``` html + +``` + +Result: + +{% raw %} +
    + +
    + +{% endraw %} + +### camelCase vs. kebab-case + +HTML attributes are case-insensitive, so when using non-string templates, camelCased prop names need to use their kebab-case (hyphen-delimited) equivalents: + +``` js +Vue.component('child', { + // camelCase in JavaScript + props: ['myMessage'], + template: '{{ myMessage }}' +}) +``` + +``` html + + +``` + +Again, if you're using string templates, then this limitation does not apply. + +### Dynamic Props + +Similar to binding a normal attribute to an expression, we can also use `v-bind` for dynamically binding props to data on the parent. Whenever the data is updated in the parent, it will also flow down to the child: + +``` html +
    + +
    + +
    +``` + +``` js +new Vue({ + el: '#prop-example-2', + data: { + parentMsg: 'Message from parent' + } +}) +``` + +You can also use the shorthand syntax for `v-bind`: + +``` html + +``` + +Result: + +{% raw %} +
    + +
    + +
    + +{% endraw %} + +If you want to pass all the properties in an object as props, you can use `v-bind` without an argument (`v-bind` instead of `v-bind:prop-name`). For example, given a `todo` object: + +``` js +todo: { + text: 'Learn Vue', + isComplete: false +} +``` + +Then: + +``` html + +``` + +Will be equivalent to: + +``` html + +``` + +### Literal vs. Dynamic + +A common mistake beginners tend to make is attempting to pass down a number using the literal syntax: + +``` html + + +``` + +However, since this is a literal prop, its value is passed down as a plain string `"1"` instead of an actual number. If we want to pass down an actual JavaScript number, we need to use `v-bind` so that its value is evaluated as a JavaScript expression: + +``` html + + +``` + +## 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. + +2. The prop is passed in as a raw value that needs to be transformed. + +The proper answer to these use cases are: + +1. Define a local data property that uses the prop's initial value as its initial value: + + ``` js + props: ['initialCounter'], + data: function () { + return { counter: this.initialCounter } + } + ``` + +2. Define a computed property that is computed from the prop's value: + + ``` js + 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 **will** affect parent state.

    + +## Prop Validation + +It is possible for a component to specify requirements for the props it is receiving. If a requirement is not met, Vue will emit warnings. This is especially useful when you are authoring a component that is intended to be used by others. + +Instead of defining the props as an array of strings, you can use an object with validation requirements: + +``` js +Vue.component('example', { + props: { + // basic type check (`null` means accept any type) + propA: Number, + // multiple possible types + propB: [String, Number], + // a required string + propC: { + type: String, + required: true + }, + // a number with default value + propD: { + type: Number, + default: 100 + }, + // object/array defaults should be returned from a + // factory function + propE: { + type: Object, + default: function () { + return { message: 'hello' } + } + }, + // custom validator function + propF: { + validator: function (value) { + return value > 10 + } + } + } +}) +``` + +The `type` can be one of the following native constructors: + +- String +- Number +- Boolean +- Function +- Object +- Array +- Symbol + +In addition, `type` can also be a custom constructor function and the assertion will be made with an `instanceof` check. + +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 within `default` or `validator` functions, instance properties such as from `data`, `computed`, or `methods` will not be available. + +## 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 `bs-date-input` component with a Bootstrap plugin that requires a `data-3d-date-picker` attribute on the `input`. We can add this attribute to our component instance: + +``` html + +``` + +And the `data-3d-date-picker="true"` attribute will automatically be added to the root element of `bs-date-input`. + +### Replacing/Merging with Existing Attributes + +Imagine this is the template for `bs-date-input`: + +``` html + +``` + +To specify a theme for our date picker plugin, we might need to add a specific class, like this: + +``` html + +``` + +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="large"` 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`. \ No newline at end of file From c81649864e26c492ea272ab69541770564499ce6 Mon Sep 17 00:00:00 2001 From: sdras Date: Sat, 10 Mar 2018 13:57:09 +0000 Subject: [PATCH 03/42] break it up further --- src/v2/guide/components.md | 624 ------------------------------------- src/v2/guide/slots.md | 236 ++++++++++++++ 2 files changed, 236 insertions(+), 624 deletions(-) create mode 100644 src/v2/guide/slots.md diff --git a/src/v2/guide/components.md b/src/v2/guide/components.md index 29bcbb894b..e592f67261 100644 --- a/src/v2/guide/components.md +++ b/src/v2/guide/components.md @@ -225,630 +225,6 @@ In Vue, the parent-child component relationship can be summarized as **props dow props down, events up

    -## Custom Events - -We have learned that the parent can pass data down to the child using props, but how do we communicate back to the parent when something happens? This is where Vue's custom event system comes in. - -### Using `v-on` with Custom Events - -Every Vue instance implements an [events interface](../api/#Instance-Methods-Events), which means it can: - -- Listen to an event using `$on(eventName)` -- Trigger an event using `$emit(eventName, optionalPayload)` - -

    Note that Vue's event system is different from the browser's [EventTarget API](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget). Though they work similarly, `$on` and `$emit` are __not__ aliases for `addEventListener` and `dispatchEvent`.

    - -In addition, a parent component can listen to the events emitted from a child component using `v-on` directly in the template where the child component is used. - -

    You cannot use `$on` to listen to events emitted by children. You must use `v-on` directly in the template, as in the example below.

    - -Here's an example: - -``` html -
    -

    {{ total }}

    - - -
    -``` - -``` js -Vue.component('button-counter', { - template: '', - data: function () { - return { - counter: 0 - } - }, - methods: { - incrementCounter: function () { - this.counter += 1 - this.$emit('increment') - } - }, -}) - -new Vue({ - el: '#counter-event-example', - data: { - total: 0 - }, - methods: { - incrementTotal: function () { - this.total += 1 - } - } -}) -``` - -{% raw %} -
    -

    {{ total }}

    - - -
    - -{% endraw %} - -In this example, it's important to note that the child component is still completely decoupled from what happens outside of it. All it does is report information about its own activity, just in case a parent component might care. - - -Here's an example on how to use payload data: - -``` html -
    -

    {{ msg }}

    - -
    -``` - -``` js -Vue.component('button-message', { - template: `
    - - -
    `, - data: function () { - return { - message: 'test message' - } - }, - methods: { - handleSendMessage: function () { - this.$emit('message', { message: this.message }) - } - } -}) - -new Vue({ - el: '#message-event-example', - data: { - messages: [] - }, - methods: { - handleMessage: function (payload) { - this.messages.push(payload.message) - } - } -}) -``` - -{% raw %} -
    -

    {{ msg }}

    - -
    - -{% endraw %} - -In this second example, it's important to note that the child component is still completely decoupled from what happens outside of it. All it does is report information about its own activity including a payload data into event emitter, just in case a parent component might care. - -### Binding Native Events to Components - -There may be times when you want to listen for a native event on the root element of a component. In these cases, you can use the `.native` modifier for `v-on`. For example: - -``` html - -``` - -### `.sync` Modifier - -> 2.3.0+ - -In some cases we may need "two-way binding" for a prop - in fact, in Vue 1.x this is exactly what the `.sync` modifier provided. When a child component mutates a prop that has `.sync`, the value change will be reflected in the parent. This is convenient, however it leads to maintenance issues in the long run because it breaks the one-way data flow assumption: the code that mutates child props are implicitly affecting parent state. - -This is why we removed the `.sync` modifier when 2.0 was released. However, we've found that there are indeed cases where it could be useful, especially when shipping reusable components. What we need to change is **making the code in the child that affects parent state more consistent and explicit.** - -In 2.3.0+ we re-introduced the `.sync` modifier for props, but this time it is only syntax sugar that automatically expands into an additional `v-on` listener: - -The following - -``` html - -``` - -is expanded into: - -``` html - -``` - -For the child component to update `foo`'s value, it needs to explicitly emit an event instead of mutating the prop: - -``` js -this.$emit('update:foo', newValue) -``` - -The `.sync` modifier can also be used with `v-bind` when using an object to set multiple properties at once: - -```html - -``` - -This has the effect of adding `v-on` update listeners for both `foo` and `bar`. - -### Form Input Components using Custom Events - -Custom events can also be used to create custom inputs that work with `v-model`. Remember: - -``` html - -``` - -is syntactic sugar for: - -``` html - -``` - -When used with a component, it instead simplifies to: - -``` html - - -``` - -So for a component to work with `v-model`, it should (these can be configured in 2.2.0+): - -- accept a `value` prop -- emit an `input` event with the new value - -Let's see it in action with a simple currency input: - -``` html - -``` - -``` js -Vue.component('currency-input', { - template: '\ - \ - $\ - \ - \ - ', - props: ['value'], - methods: { - // Instead of updating the value directly, this - // method is used to format and place constraints - // on the input's value - updateValue: function (value) { - var formattedValue = value - // Remove whitespace on either side - .trim() - // Shorten to 2 decimal places - .slice( - 0, - value.indexOf('.') === -1 - ? value.length - : value.indexOf('.') + 3 - ) - // If the value was not already normalized, - // manually override it to conform - if (formattedValue !== value) { - this.$refs.input.value = formattedValue - } - // Emit the number value through the input event - this.$emit('input', Number(formattedValue)) - } - } -}) -``` - -{% raw %} -
    - -
    - -{% endraw %} - -The implementation above is pretty naive though. For example, users are allowed to enter multiple periods and even letters sometimes - yuck! So for those that want to see a non-trivial example, here's a more robust currency filter: - - - -### Customizing Component `v-model` - -> New in 2.2.0+ - -By default, `v-model` on a component uses `value` as the prop and `input` as the event, but some input types such as checkboxes and radio buttons may want to use the `value` prop for a different purpose. Using the `model` option can avoid the conflict in such cases: - -``` js -Vue.component('my-checkbox', { - model: { - prop: 'checked', - event: 'change' - }, - props: { - checked: Boolean, - // this allows using the `value` prop for a different purpose - value: String - }, - // ... -}) -``` - -``` html - -``` - -The above will be equivalent to: - -``` html - - -``` - -

    Note that you still have to declare the `checked` prop explicitly.

    - -### Non Parent-Child Communication - -Sometimes two components may need to communicate with one-another but they are not parent/child to each other. In simple scenarios, you can use an empty Vue instance as a central event bus: - -``` js -var bus = new Vue() -``` -``` js -// in component A's method -bus.$emit('id-selected', 1) -``` -``` js -// in component B's created hook -bus.$on('id-selected', function (id) { - // ... -}) -``` - -In more complex cases, you should consider employing a dedicated [state-management pattern](state-management.html). - -## Content Distribution with Slots - -When using components, it is often desired to compose them like this: - -``` html - - - - -``` - -There are two things to note here: - -1. The `` component does not know what content it will receive. It is decided by the component using ``. - -2. The `` component very likely has its own template. - -To make the composition work, we need a way to interweave the parent "content" and the component's own template. This is a process called **content distribution** (or "transclusion" if you are familiar with Angular). Vue.js implements a content distribution API that is modeled after the current [Web Components spec draft](https://github.com/w3c/webcomponents/blob/gh-pages/proposals/Slots-Proposal.md), using the special `` element to serve as distribution outlets for the original content. - -### Compilation Scope - -Before we dig into the API, let's first clarify which scope the contents are compiled in. Imagine a template like this: - -``` html - - {{ message }} - -``` - -Should the `message` be bound to the parent's data or the child data? The answer is the parent. A simple rule of thumb for component scope is: - -> Everything in the parent template is compiled in parent scope; everything in the child template is compiled in child scope. - -A common mistake is trying to bind a directive to a child property/method in the parent template: - -``` html - - -``` - -Assuming `someChildProperty` is a property on the child component, the example above would not work. The parent's template is not aware of the state of a child component. - -If you need to bind child-scope directives on a component root node, you should do so in the child component's own template: - -``` js -Vue.component('child-component', { - // this does work, because we are in the right scope - template: '
    Child
    ', - data: function () { - return { - someChildProperty: true - } - } -}) -``` - -Similarly, distributed content will be compiled in the parent scope. - -### Single Slot - -Parent content will be **discarded** unless the child component template contains at least one `` outlet. When there is only one slot with no attributes, the entire content fragment will be inserted at its position in the DOM, replacing the slot itself. - -Anything originally inside the `` tags is considered **fallback content**. Fallback content is compiled in the child scope and will only be displayed if the hosting element is empty and has no content to be inserted. - -Suppose we have a component called `my-component` with the following template: - -``` html -
    -

    I'm the child title

    - - This will only be displayed if there is no content - to be distributed. - -
    -``` - -And a parent that uses the component: - -``` html -
    -

    I'm the parent title

    - -

    This is some original content

    -

    This is some more original content

    -
    -
    -``` - -The rendered result will be: - -``` html -
    -

    I'm the parent title

    -
    -

    I'm the child title

    -

    This is some original content

    -

    This is some more original content

    -
    -
    -``` - -### Named Slots - -`` elements have a special attribute, `name`, which can be used to further customize how content should be distributed. You can have multiple slots with different names. A named slot will match any element that has a corresponding `slot` attribute in the content fragment. - -There can still be one unnamed slot, which is the **default slot** that serves as a catch-all outlet for any unmatched content. If there is no default slot, unmatched content will be discarded. - -For example, suppose we have an `app-layout` component with the following template: - -``` html -
    -
    - -
    -
    - -
    -
    - -
    -
    -``` - -Parent markup: - -``` html - -

    Here might be a page title

    - -

    A paragraph for the main content.

    -

    And another one.

    - -

    Here's some contact info

    -
    -``` - -The rendered result will be: - -``` html -
    -
    -

    Here might be a page title

    -
    -
    -

    A paragraph for the main content.

    -

    And another one.

    -
    -
    -

    Here's some contact info

    -
    -
    -``` - -The content distribution API is a very useful mechanism when designing components that are meant to be composed together. - -### Scoped Slots - -> New in 2.1.0+ - -A scoped slot is a special type of slot that functions as a reusable template (that can be passed data to) instead of already-rendered-elements. - -In a child component, pass data into a slot as if you are passing props to a component: - -``` html -
    - -
    -``` - -In the parent, a `