Skip to content

Commit

Permalink
Moving Components Page into its Own Category (vuejs#1482)
Browse files Browse the repository at this point in the history
* create components sidebar

* break up first two sections

* break it up further

* split up the rest

* clean up

* initial pass, creating new, much slimmer components intro page that links to more in-depth pages

* finish initial component registration page

* finish initial refactor of component props page

* beginning work on components custom events page

* finish initial components custom events page

* finish initial components slots page

* finish initial dynamic and async components page

* finish initial dynamic-async and edge-cases components pages

* components review and tweaks up to the props page

* fix components mistakes found by @phanan

* fix typo in components-edge-cases

* reorder Compiliation Scope in components-slots

* fix link in components basics

* fix codeblock language typo in components

* add example of local registration in es2015 modules

* add keep-alive example

* remove redundant word in components

* make dom template parsing caveats h2

* change simple example to base example in components

* add async import example to circular references

* fix typo in components-custom-events

* remove redundand language in componenents-custom-events

* add missing comma in components-custom-events

* restate context in component-dynamic-async

* remove redundant actuallys in components-dynamic-async

* clarify .sync modifier usage

* set up hash redirects for all old components sections

* remove note about intentional single root elements

* fix dom template parsing caveats example

* fix closing </code> tags in components-edge-cases

* fix typo in components-edge-cases

* fix typo in components-edge-cases

* remove extra 'a' in components-registration

* add name casing and various tweaks to components-registration

* complete custom v-model example in components-custom-events

* minor tweaks to components pages

* rename signature to definition for clarity
  • Loading branch information
sdras authored and chrisvfritz committed Mar 26, 2018
1 parent 9d1a534 commit 5adbc5f
Show file tree
Hide file tree
Showing 14 changed files with 2,375 additions and 1,481 deletions.
1 change: 0 additions & 1 deletion _config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -187,4 +187,3 @@ alias:
examples/svg.html: v2/examples/svg.html
examples/todomvc.html: v2/examples/todomvc.html
examples/tree-view.html: v2/examples/tree-view.html

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "vuejs.org",
"private": true,
"hexo": {
"version": "3.4.2"
"version": "3.6.0"
},
"scripts": {
"start": "hexo server",
Expand Down
2 changes: 1 addition & 1 deletion src/v2/api/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -1432,7 +1432,7 @@ type: api
- **Details:**
Contains parent-scope `v-on` event listeners (without `.native` modifiers). This can be passed down to an inner component via `v-on="$listeners"` - useful when creating higher-order components.
Contains parent-scope `v-on` event listeners (without `.native` modifiers). This can be passed down to an inner component via `v-on="$listeners"` - useful when creating transparent wrapper components.
## Instance Methods / Data
Expand Down
168 changes: 168 additions & 0 deletions src/v2/guide/components-custom-events.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
---
title: Custom Events
type: guide
order: 103
---

> This page assumes you've already read the [Components Basics](components.html). Read that first if you are new to components.
## Event Names

Unlike components and props, event names don't provide any automatic case transformation. Instead, the name of an emitted event must exactly match the name used to listen to that event. For example, if emitting a camelCased event name:

```js
this.$emit('myEvent')
```

Listening to the kebab-cased version will have no effect:

```html
<my-component v-on:my-event="doSomething"></my-component>
```

Unlike components and props, event names will never be used as variable or property names in JavaScript, so there's no reason to use camelCase or PascalCase. Additionally, `v-on` event listeners inside DOM templates will be automatically transformed to lowercase (due to HTML's case-insensitivity), so `v-on:myEvent` would become `v-on:myevent` -- making `myEvent` impossible to listen to.

For these reasons, we recommend you **always use kebab-case for event names**.

## 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` attribute for a [different purpose](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/checkbox#Value). Using the `model` option can avoid a conflict in such cases:

```js
Vue.component('base-checkbox', {
model: {
prop: 'checked',
event: 'change'
},
props: {
checked: Boolean
},
template: `
<input
type="checkbox"
v-bind:checked="checked"
v-on:change="$emit('change', $event.target.value)"
>
`
})
```

Now when using `v-model` on this component:

```js
<base-checkbox v-model="lovingVue"></base-checkbox>
```

the value of `lovingVue` will be passed to the `checked` prop. The `lovingVue` property will then be updated when `<base-checkbox>` emits a `change` event with a new value.

<p class="tip">Note that you still have to declare the <code>checked</code> prop in component's <code>props</code> option.</p>

## Binding Native Events to Components

There may be times when you want to listen directly to a native event on the root element of a component. In these cases, you can use the `.native` modifier for `v-on`:

```html
<base-input v-on:focus.native="onFocus"></base-input>
```

This can be useful sometimes, but it's not a good idea when you're trying to listen on a very specific element, like an `<input>`. For example, the `<base-input>` component above might refactor so that the root element is actually a `<label>` element:

```html
<label>
{{ label }}
<input
v-bind="$attrs"
v-bind:value="value"
v-on:input="$emit('input', $event.target.value)"
>
</label>
```

In that case, the `.native` listener in the parent would silently break. There would be no errors, but the `onFocus` handler wouldn't be called when we expected it to.

To solve this problem, Vue provides a `$listeners` property containing an object of listeners being used on the component. For example:

```js
{
focus: function (event) { /* ... */ }
input: function (value) { /* ... */ },
}
```

Using the `$listeners` property, you can forward all event listeners on the component to a specific child element with `v-on="$listeners"`. For elements like `<input>`, that you also want to work with `v-model`, it's often useful to create a new computed property for listeners, like `inputListeners` below:

```js
Vue.component('base-input', {
inheritAttrs: false,
props: ['label', 'value'],
computed: {
inputListeners: function () {
var vm = this
// `Object.assign` merges objects together to form a new object
return Object.assign({},
// We add all the listeners from the parent
this.$listeners,
// Then we can add custom listeners or override the
// behavior of some listeners.
{
// This ensures that the component works with v-model
input: function (event) {
vm.$emit('input', event.target.value)
}
}
)
}
},
template: `
<label>
{{ label }}
<input
v-bind="$attrs"
v-bind:value="value"
v-on="inputListeners"
>
</label>
`
})
```

Now the `<base-input>` component is a **fully transparent wrapper**, meaning it can be used exactly like a normal `<input>` element: all the same attributes and listeners will work.

## `.sync` Modifier

> New in 2.3.0+
In some cases, we may need "two-way binding" for a prop. Unfortunately, true two-way binding can create maintenance issues, because child components can mutate the parent without the source of that mutation being obvious in both the parent and the child.

That's why instead, we recommend emitting events in the pattern of `update:my-prop-name`. For example, in a hypothetical component with a `title` prop, we could communicate the intent of assigning a new value with:

```js
this.$emit('update:title', newTitle)
```

Then the parent can listen to that event and update a local data property, if it wants to. For example:

```html
<text-document
v-bind:title="doc.title"
v-on:update:title="doc.title = $event"
></text-document>
```

For convenience, we offer a shorthand for this pattern with the `.sync` modifier:

```html
<text-document v-bind:title.sync="doc.title"></text-document>
```

The `.sync` modifier can also be used with `v-bind` when using an object to set multiple props at once:

```html
<text-document v-bind.sync="doc"></text-document>
```

This passes each property in the `doc` object (e.g. `title`) as an individual prop, then adds `v-on` update listeners for each one.

<p class="tip">Using <code>v-bind.sync</code> with a literal object, such as in <code>v-bind.sync="{ title: doc.title }"</code>, will not work. If you want to include multiple, unrelated data properties in the same <code>v-bind.sync</code>, we recommend creating a computed property that returns an object.</p>
Loading

0 comments on commit 5adbc5f

Please sign in to comment.