Ver Fonte

add .sync support for Dialog and Pagination

Leopoldthecoder há 8 anos atrás
pai
commit
defd128f54

+ 12 - 0
FAQ.md

@@ -57,6 +57,12 @@
 ```
 </details>
 
+<details>
+<summary>所有组件的任意属性都支持 `.sync` 修饰符吗?</summary>
+  
+不是。对于支持 `.sync` 修饰符的属性,我们会在文档的 API 表格中注明。更多 `.sync` 的用法请查看 [Vue 文档](https://vuejs.org/v2/guide/components.html#sync-Modifier)。
+</details>
+
 <details>
 <summary>你们的文档怎么偷偷更新了?</summary>
 
@@ -144,6 +150,12 @@ Now you can use them as you do with built-in icons. For example, in `el-input`:
 ```
 </details>
 
+<details>
+<summary>Can I use `.sync` modifier on every attribute?</summary>
+  
+No, only a few attributes supports the `.sync` modifier, and we have explicitly marked them on the documentation's API table. For more information about `.sync`, please refer to [Vue documentation](https://vuejs.org/v2/guide/components.html#sync-Modifier).
+</details>
+
 <details>
 <summary>When do you update documentations of Element?</summary>
 

+ 5 - 16
examples/docs/en-US/dialog.md

@@ -36,9 +36,6 @@
       };
     },
     methods: {
-      openDialog() {
-        this.$refs.dialogBind.open();
-      },
       handleClose(done) {
         this.$confirm('Are you sure to close this dialog?')
           .then(_ => {
@@ -58,14 +55,14 @@ Informs users while preserving the current page state.
 
 Dialog pops up a dialog box, and it's quite customizable.
 
-:::demo Set the `v-model` attribute with a `Boolean`, and Dialog shows when it is `true`. The Dialog has two parts: `body` and `footer`, and the latter requires a `slot` named `footer`. The optional `title` attribute (empty by default) is for defining a title. This example explicitly changes the value of `v-model` to toggle Dialog. In addition, we also provide `open` and `close` method, which you can call to open/close the Dialog. Finally, this example demonstrates how `before-close` is used.
+:::demo Set the `visible` attribute with a `Boolean`, and Dialog shows when it is `true`. The Dialog has two parts: `body` and `footer`, and the latter requires a `slot` named `footer`. The optional `title` attribute (empty by default) is for defining a title. Finally, this example demonstrates how `before-close` is used.
 
 ```html
 <el-button type="text" @click="dialogVisible = true">click to open the Dialog</el-button>
 
 <el-dialog
   title="Tips"
-  v-model="dialogVisible"
+  :visible.sync="dialogVisible"
   size="tiny"
   :before-close="handleClose">
   <span>This is a message</span>
@@ -106,7 +103,7 @@ The content of Dialog can be anything, even a table or a form. This example show
 <!-- Table -->
 <el-button type="text" @click="dialogTableVisible = true">open a Table nested Dialog</el-button>
 
-<el-dialog title="Shipping address" v-model="dialogTableVisible">
+<el-dialog title="Shipping address" :visible.sync="dialogTableVisible">
   <el-table :data="gridData">
     <el-table-column property="date" label="Date" width="150"></el-table-column>
     <el-table-column property="name" label="Name" width="200"></el-table-column>
@@ -117,7 +114,7 @@ The content of Dialog can be anything, even a table or a form. This example show
 <!-- Form -->
 <el-button type="text" @click="dialogFormVisible = true">open a Form nested Dialog</el-button>
 
-<el-dialog title="Shipping address" v-model="dialogFormVisible">
+<el-dialog title="Shipping address" :visible.sync="dialogFormVisible">
   <el-form :model="form">
     <el-form-item label="Promotion name" :label-width="formLabelWidth">
       <el-input v-model="form.name" auto-complete="off"></el-input>
@@ -180,6 +177,7 @@ The content of Dialog can be anything, even a table or a form. This example show
 
 | Attribute      | Description          | Type      | Accepted Values       | Default  |
 |---------- |-------------- |---------- |--------------------------------  |-------- |
+| visible   | visibility of Dialog, supports the .sync modifier | boolean | — | false |
 | title     | title of Dialog. Can also be passed with a named slot (see the following table) | string    | — | — |
 | size      | size of Dialog | string    | tiny/small/large/full | small |
 | top      | value for `top` of Dialog CSS, works when `size` is not `full` | string    | — | 15% |
@@ -200,17 +198,8 @@ The content of Dialog can be anything, even a table or a form. This example show
 | title | content of the Dialog title |
 | footer | content of the Dialog footer |
 
-### Methods
-Each `el-dialog` instance has the following methods that can be used to open/close the instance without explicitly changing the value of `v-model`:
-
-| Method | Description |
-|------|--------|
-| open | open the current instance |
-| close | close the current instance |
-
 ### Events
 | Event Name | Description | Parameters |
 |---------- |-------- |---------- |
 | open | triggers when the Dialog opens | — |
 | close | triggers when the Dialog closes | — |
-

+ 5 - 7
examples/docs/en-US/pagination.md

@@ -50,7 +50,7 @@ Add more modules based on your scenario.
     <el-pagination
       @size-change="handleSizeChange"
       @current-change="handleCurrentChange"
-      :current-page="currentPage1"
+      :current-page.sync="currentPage1"
       :page-size="100"
       layout="total, prev, pager, next"
       :total="1000">
@@ -61,7 +61,7 @@ Add more modules based on your scenario.
     <el-pagination
       @size-change="handleSizeChange"
       @current-change="handleCurrentChange"
-      :current-page="currentPage2"
+      :current-page.sync="currentPage2"
       :page-sizes="[100, 200, 300, 400]"
       :page-size="100"
       layout="sizes, prev, pager, next"
@@ -73,7 +73,7 @@ Add more modules based on your scenario.
     <el-pagination
       @size-change="handleSizeChange"
       @current-change="handleCurrentChange"
-      :current-page="currentPage3"
+      :current-page.sync="currentPage3"
       :page-size="100"
       layout="prev, pager, next, jumper"
       :total="1000">
@@ -84,7 +84,7 @@ Add more modules based on your scenario.
     <el-pagination
       @size-change="handleSizeChange"
       @current-change="handleCurrentChange"
-      :current-page="currentPage4"
+      :current-page.sync="currentPage4"
       :page-sizes="[100, 200, 300, 400]"
       :page-size="100"
       layout="total, sizes, prev, pager, next, jumper"
@@ -99,7 +99,6 @@ Add more modules based on your scenario.
         console.log(`${val} items per page`);
       },
       handleCurrentChange(val) {
-        this.currentPage = val;
         console.log(`current page: ${val}`);
       }
     },
@@ -131,7 +130,6 @@ Add more modules based on your scenario.
         console.log(`${val} items per page`);
       },
       handleCurrentChange(val) {
-        this.currentPage = val;
         console.log(`current page: ${val}`);
       }
     },
@@ -154,7 +152,7 @@ Add more modules based on your scenario.
 | page-size              | item count of each page  | number |      —       | 10 |
 | total | total item count | number | — | — |
 | page-count | total page count. Set either `total` or `page-count` and pages will be displayed; if you need `page-sizes`, `total` is required | number | — | — |
-| current-page | current page number | number | — | 1 |
+| current-page | current page number, supports the .sync modifier | number | — | 1 |
 | layout | layout of Pagination, elements separated with a comma | string | `sizes`, `prev`, `pager`, `next`, `jumper`, `->`, `total`, `slot` | 'prev, pager, next, jumper, ->, total'  |
 | page-sizes | options of item count per page | number[] | — |  [10, 20, 30, 40, 50, 100] |
 

+ 5 - 14
examples/docs/zh-CN/dialog.md

@@ -36,9 +36,6 @@
       };
     },
     methods: {
-      openDialog() {
-        this.$refs.dialogBind.open();
-      },
       handleClose(done) {
         this.$confirm('确认关闭?')
           .then(_ => {
@@ -79,14 +76,14 @@
 
 Dialog 弹出一个对话框,适合需要定制性更大的场景。
 
-:::demo 需要设置`v-model`属性,它接收`Boolean`,当为`true`时显示 Dialog。Dialog 分为两个部分:`body`和`footer`,`footer`需要具名为`footer`的`slot`。`title`属性用于定义标题,它是可选的,默认值为空。本例通过显式改变`v-model`的值来打开 Dialog,此外我们还为 Dialog 实例提供了`open`和`close`方法,可以通过调用它们来打开/关闭 Dialog。最后,本例还展示了`beforeClose`的用法。
+:::demo 需要设置`visible`属性,它接收`Boolean`,当为`true`时显示 Dialog。Dialog 分为两个部分:`body`和`footer`,`footer`需要具名为`footer`的`slot`。`title`属性用于定义标题,它是可选的,默认值为空。最后,本例还展示了`beforeClose`的用法。
 
 ```html
 <el-button type="text" @click="dialogVisible = true">点击打开 Dialog</el-button>
 
 <el-dialog
   title="提示"
-  v-model="dialogVisible"
+  :visible.sync="dialogVisible"
   size="tiny"
   :before-close="handleClose">
   <span>这是一段信息</span>
@@ -126,7 +123,7 @@ Dialog 组件的内容可以是任意的,甚至可以是表格或表单,下
 <!-- Table -->
 <el-button type="text" @click="dialogTableVisible = true">打开嵌套表格的 Dialog</el-button>
 
-<el-dialog title="收货地址" v-model="dialogTableVisible">
+<el-dialog title="收货地址" :visible.sync="dialogTableVisible">
   <el-table :data="gridData">
     <el-table-column property="date" label="日期" width="150"></el-table-column>
     <el-table-column property="name" label="姓名" width="200"></el-table-column>
@@ -137,7 +134,7 @@ Dialog 组件的内容可以是任意的,甚至可以是表格或表单,下
 <!-- Form -->
 <el-button type="text" @click="dialogFormVisible = true">打开嵌套表单的 Dialog</el-button>
 
-<el-dialog title="收货地址" v-model="dialogFormVisible">
+<el-dialog title="收货地址" :visible.sync="dialogFormVisible">
   <el-form :model="form">
     <el-form-item label="活动名称" :label-width="formLabelWidth">
       <el-input v-model="form.name" auto-complete="off"></el-input>
@@ -199,6 +196,7 @@ Dialog 组件的内容可以是任意的,甚至可以是表格或表单,下
 ### Attributes
 | 参数      | 说明          | 类型      | 可选值                           | 默认值  |
 |---------- |-------------- |---------- |--------------------------------  |-------- |
+| visible   | 是否显示 Dialog,支持 .sync 修饰符 | boolean | — | false |
 | title     | Dialog 的标题,也可通过具名 slot (见下表)传入 | string    | — | — |
 | size      | Dialog 的大小 | string    | tiny/small/large/full | small |
 | top       | Dialog CSS 中的 top 值(仅在 size 不为 full 时有效) | string | — | 15% |
@@ -218,13 +216,6 @@ Dialog 组件的内容可以是任意的,甚至可以是表格或表单,下
 | title | Dialog 标题区的内容 |
 | footer | Dialog 按钮操作区的内容 |
 
-### 方法
-每个 `el-dialog` 实例都暴露了如下方法,用于在不显式改变 `v-model` 值的情况下打开 / 关闭实例:
-| 方法名 | 说明 |
-|------|--------|
-| open | 打开当前实例 |
-| close | 关闭当前实例 |
-
 ### Events
 | 事件名称      | 说明    | 回调参数      |
 |---------- |-------- |---------- |

+ 4 - 6
examples/docs/zh-CN/pagination.md

@@ -50,7 +50,7 @@
     <el-pagination
       @size-change="handleSizeChange"
       @current-change="handleCurrentChange"
-      :current-page="currentPage1"
+      :current-page.sync="currentPage1"
       :page-size="100"
       layout="total, prev, pager, next"
       :total="1000">
@@ -61,7 +61,7 @@
     <el-pagination
       @size-change="handleSizeChange"
       @current-change="handleCurrentChange"
-      :current-page="currentPage2"
+      :current-page.sync="currentPage2"
       :page-sizes="[100, 200, 300, 400]"
       :page-size="100"
       layout="sizes, prev, pager, next"
@@ -73,7 +73,7 @@
     <el-pagination
       @size-change="handleSizeChange"
       @current-change="handleCurrentChange"
-      :current-page="currentPage3"
+      :current-page.sync="currentPage3"
       :page-size="100"
       layout="prev, pager, next, jumper"
       :total="1000">
@@ -99,7 +99,6 @@
         console.log(`每页 ${val} 条`);
       },
       handleCurrentChange(val) {
-        this.currentPage = val;
         console.log(`当前页: ${val}`);
       }
     },
@@ -120,7 +119,6 @@
   export default {
     methods: {
       handleSizeChange(val) {
-        this.currentPage = val;
         console.log(`每页 ${val} 条`);
       },
       handleCurrentChange(val) {
@@ -204,7 +202,7 @@
 | page-size | 每页显示条目个数 | Number | — | 10 |
 | total | 总条目数 | Number | — | — |
 | page-count | 总页数,total 和 page-count 设置任意一个就可以达到显示页码的功能;如果要支持 page-sizes 的更改,则需要使用 total 属性 | Number | — | — |
-| current-page | 当前页数 | Number | — | 1 |
+| current-page | 当前页数,支持 .sync 修饰符 | Number | — | 1 |
 | layout | 组件布局,子组件名用逗号分隔| String | `sizes`, `prev`, `pager`, `next`, `jumper`, `->`, `total`, `slot` | 'prev, pager, next, jumper, ->, total'  |
 | page-sizes | 每页显示个数选择器的选项设置 | Number[] | — |  [10, 20, 30, 40, 50, 100] |
 

+ 2 - 10
packages/dialog/src/component.vue

@@ -84,18 +84,10 @@
       },
       beforeClose: Function
     },
-    data() {
-      return {
-        visible: false
-      };
-    },
 
     watch: {
-      value(val) {
-        this.visible = val;
-      },
       visible(val) {
-        this.$emit('input', val);
+        this.$emit('update:visible', val);
         if (val) {
           this.$emit('open');
           this.$el.addEventListener('scroll', this.updatePopper);
@@ -137,7 +129,7 @@
     },
 
     mounted() {
-      if (this.value) {
+      if (this.visible) {
         this.rendered = true;
         this.open();
       }

+ 3 - 3
packages/message-box/src/main.js

@@ -79,7 +79,7 @@ const showNextMsg = () => {
   }
   instance.action = '';
 
-  if (!instance.value || instance.closeTimer) {
+  if (!instance.visible || instance.closeTimer) {
     if (msgQueue.length > 0) {
       currentMsg = msgQueue.shift();
 
@@ -110,7 +110,7 @@ const showNextMsg = () => {
       document.body.appendChild(instance.$el);
 
       Vue.nextTick(() => {
-        instance.value = true;
+        instance.visible = true;
       });
     }
   }
@@ -199,7 +199,7 @@ MessageBox.prompt = (message, title, options) => {
 };
 
 MessageBox.close = () => {
-  instance.value = false;
+  instance.visible = false;
   msgQueue = [];
   currentMsg = null;
 };

+ 4 - 4
packages/message-box/src/main.vue

@@ -1,6 +1,6 @@
 <template>
   <transition name="msgbox-fade">
-    <div class="el-message-box__wrapper" v-show="value" @click.self="handleWrapperClick">
+    <div class="el-message-box__wrapper" v-show="visible" @click.self="handleWrapperClick">
       <div class="el-message-box" :class="customClass">
         <div class="el-message-box__header" v-if="title !== undefined">
           <div class="el-message-box__title">{{ title || t('el.messagebox.title') }}</div>
@@ -103,8 +103,8 @@
         };
       },
       doClose() {
-        if (!this.value) return;
-        this.value = false;
+        if (!this.visible) return;
+        this.visible = false;
         this._closing = true;
 
         this.onClose && this.onClose();
@@ -187,7 +187,7 @@
         }
       },
 
-      value(val) {
+      visible(val) {
         if (val) this.uid++;
         if (this.$type === 'alert' || this.$type === 'confirm') {
           this.$nextTick(() => {

+ 2 - 0
packages/pagination/src/pagination.js

@@ -312,10 +312,12 @@ export default {
         this.$nextTick(() => {
           this.internalCurrentPage = newVal;
           if (oldVal !== newVal) {
+            this.$emit('update:currentPage', newVal);
             this.$emit('current-change', this.internalCurrentPage);
           }
         });
       } else {
+        this.$emit('update:currentPage', newVal);
         this.$emit('current-change', this.internalCurrentPage);
       }
     },

+ 2 - 10
packages/transfer/src/main.vue

@@ -1,15 +1,11 @@
 <template>
   <div class="el-transfer">
     <transfer-panel
-      :filterable="filterable"
-      :filter-method="filterMethod"
+      v-bind="$props"
       :data="sourceData"
-      :render-content="renderContent"
       :title="titles[0] || t('el.transfer.titles.0')"
-      :format="footerFormat"
       :default-checked="leftDefaultChecked"
       :placeholder="filterPlaceholder || t('el.transfer.filterPlaceholder')"
-      :props="props"
       @checked-change="onSourceCheckedChange">
       <slot name="left-footer"></slot>
     </transfer-panel>
@@ -32,15 +28,11 @@
       </el-button>
     </div>
     <transfer-panel
-      :filterable="filterable"
-      :filter-method="filterMethod"
+      v-bind="$props"
       :data="targetData"
-      :render-content="renderContent"
       :title="titles[1] || t('el.transfer.titles.1')"
-      :format="footerFormat"
       :default-checked="rightDefaultChecked"
       :placeholder="filterPlaceholder || t('el.transfer.filterPlaceholder')"
-      :props="props"
       @checked-change="onTargetCheckedChange">
       <slot name="right-footer"></slot>
     </transfer-panel>

+ 2 - 2
packages/transfer/src/transfer-panel.vue

@@ -95,7 +95,7 @@
       placeholder: String,
       title: String,
       filterable: Boolean,
-      format: Object,
+      footerFormat: Object,
       filterMethod: Function,
       defaultChecked: Array,
       props: Object
@@ -167,7 +167,7 @@
       checkedSummary() {
         const checkedLength = this.checked.length;
         const dataLength = this.data.length;
-        const { noChecked, hasChecked } = this.format;
+        const { noChecked, hasChecked } = this.footerFormat;
         if (noChecked && hasChecked) {
           return checkedLength > 0
             ? hasChecked.replace(/\${checked}/g, checkedLength).replace(/\${total}/g, dataLength)

+ 9 - 9
src/utils/popup/index.js

@@ -49,8 +49,12 @@ const getDOM = function(dom) {
 };
 
 export default {
+  model: {
+    prop: 'visible',
+    event: 'visible-change'
+  },
   props: {
-    value: {
+    visible: {
       type: Boolean,
       default: false
     },
@@ -120,7 +124,7 @@ export default {
   },
 
   watch: {
-    value(val) {
+    visible(val) {
       if (val) {
         if (this._opening) return;
         if (!this.rendered) {
@@ -141,7 +145,7 @@ export default {
     open(options) {
       if (!this.rendered) {
         this.rendered = true;
-        this.$emit('input', true);
+        this.$emit('visible-change', true);
       }
 
       const props = merge({}, this.$props || this, options);
@@ -170,10 +174,7 @@ export default {
 
       this._opening = true;
 
-      // 使用 vue-popup 的组件,如果需要和父组件通信显示的状态,应该使用 value,它是一个 prop,
-      // 这样在父组件中用 v-model 即可;否则可以使用 visible,它是一个 data
-      this.visible = true;
-      this.$emit('input', true);
+      this.$emit('visible-change', true);
 
       const dom = getDOM(this.$el);
 
@@ -244,8 +245,7 @@ export default {
     },
 
     doClose() {
-      this.visible = false;
-      this.$emit('input', false);
+      this.$emit('visible-change', false);
       this._closing = true;
 
       this.onClose && this.onClose();

+ 4 - 4
test/unit/specs/message-box.spec.js

@@ -32,12 +32,12 @@ describe('MessageBox', () => {
     });
     setTimeout(() => {
       const msgbox = document.querySelector('.el-message-box__wrapper');
-      expect(msgbox.__vue__.$parent.value).to.true;
+      expect(msgbox.__vue__.$parent.visible).to.true;
       expect(msgbox.querySelector('.el-message-box__title').textContent).to.equal('消息');
       expect(msgbox.querySelector('.el-message-box__message')
         .querySelector('p').textContent).to.equal('这是一段内容');
       MessageBox.close();
-      expect(msgbox.__vue__.$parent.value).to.false;
+      expect(msgbox.__vue__.$parent.visible).to.false;
       done();
     }, 300);
   });
@@ -58,7 +58,7 @@ describe('MessageBox', () => {
     setTimeout(() => {
       document.querySelector('.v-modal').click();
       expect(document.querySelector('.el-message-box__wrapper')
-        .__vue__.$parent.value).to.true;
+        .__vue__.$parent.visible).to.true;
       expect(document.querySelector('.el-message-box__wrapper')
         .__vue__.$parent.type).to.equal('warning');
       done();
@@ -74,7 +74,7 @@ describe('MessageBox', () => {
       document.querySelector('.el-message-box__wrapper')
         .querySelector('.el-button--primary').click();
       expect(document.querySelector('.el-message-box__wrapper')
-        .__vue__.$parent.value).to.false;
+        .__vue__.$parent.visible).to.false;
       done();
     }, 200);
   });