Vue.component('simple-grid', { template: '#grid-template', props: ['dataList', 'columns', 'searchKey'], data: function() { return { sortKey: '', sortOrder: -1, mode: 0, item: {}, title: '', //currentKey: '', test: false //测试是可行的 不过必须是同步状态 } }, methods: { deleteItem: function(entry) { var data = this.dataList data.forEach(function(item, i) { if (item === entry) { data.splice(i, 1) return } }) }, sortBy: function(key) { //alert(key.name) this.sortKey = key.name; this.sortOrder = this.sortOrder > 0 ? -1 : 1; }, openNewItemDialog: function(title) { //测试不用广播打开对话框 //测试是可行的 不过必须是同步状态 //广播利于解耦 this.test = true; //对话框标题 this.title = title; // mode = 1 表示新建模式 this.mode = 1; // 初始化this.item //原版为空 this.item = { sex: 'Male' }; // 广播事件,showDialog是modal-dialog组件的一个方法,传入参数true表示显示对话框 //广播比派发简单多了 不用绕一下不错. this.$broadcast('showDialog', true); }, openEditItemDialog: function(index, title) { // 对话框标题 this.title = title; // 模式2 修改 this.mode = 2; // 修改数据 this.item = this.dataList[index]; // 打开对话框 this.$broadcast('showDialog',true); //修改当期key 以便保存的时候用. this.currentKey = index; //下面是曾经测试用的 // //终于明白API说的 不能检测是啥意思了. 这个可以这么设置,但是页面不能自动刷新 // //虽然数据变了,但是页面上不显示 // this.dataList[index] = { // name: 'elick', // age: 34, // sex: 'Male' // } // //所以用这种方法可以更新数据数组,那么原本的那些复杂的写法真就不懂干啥用了 // this.dataList.$set(index, { // name: 'xwiwi', // age: '24', // sex: 'Female' // }) // var aa = this.dataList[index]; // console.log(aa); // }, createItem: function() { if(!this.itemExists()){ alert('这是方法'); //测试不用广播关闭对话框 this.test = false; //将item追加到dataList this.dataList.push(this.item); //广播事件, 传入参数false表示隐藏对话框 this.$broadcast('showDialog', false); //新建万数据后,清空item对象 this.item = {} }else{ alert('已经存在了'); } }, updateItem: function() { //这真见了鬼了,不需要更改dataList他就自动和item关联起来了. //this.dataList.$set(this.currentKey,this.item); //this.currentKey = ''; this.item = {} this.$broadcast('showDialog',false) }, itemExists: function () { for (var i = 0; i < this.dataList.length; i++) { if(this.dataList[i].name == this.item.name) return true; } return false } }, events: { //这是事件处理器 'create-item': function() { alert('这是事件'); //测试不用广播关闭对话框 this.test = false; //将item追加到dataList this.dataList.push(this.item); //广播事件, 传入参数false表示隐藏对话框 this.$broadcast('showDialog', false); //新建万数据后,清空item对象 this.item = {} } }, components: { 'modal-dialog': { template: '#dialog-template', data: function() { return { show: false } }, props: ['mode', 'title', 'fields', 'item', 'test'], methods: { close: function() { // this.test = false this.show = false }, save: function() { if (this.mode === 1) { // 使用$dispatch调用simple-grid的create-item方法 //为什么不在子组件直接处理呢? //因为数据都在父组件里,并没有把数组穿过来 //这也没必要因为这是个添加内容的组件,不需要知道那么多 this.$dispatch('create-item'); //这里的事件'create-item' 是父事件 //@create-item="createItem" 这两个都是父的东西 } if (this.mode === 2) { this.$dispatch('update-item'); } } }, events: { // 显示或隐藏对话框 //这个也是为什么要通过广播呢? 'showDialog': function(show) { this.show = show; } } } }, }) var demo = new Vue({ el: '#app', data: { searchQuery: '', columns: [{ name: 'name', isKey: true }, { name: 'age' }, { name: 'sex', dataSource: ['Male', 'Female'] }], people: [{ name: 'Jack', age: 30, sex: 'Male' }, { name: 'Bill', age: 26, sex: 'Male' }, { name: 'Tracy', age: 22, sex: 'Female' }, { name: 'Chris', age: 36, sex: 'Male' }] } })
<div id="app"> <div class="container"> <div class="form-group"> <label>Search</label> <input type="text" class="search-input" v-model="searchQuery" /> </div> </div> <div class="container"> <simple-grid :data-list="people" :columns="columns" :search-key="searchQuery"></simple-grid> </div> </div> <template id="grid-template"> <table> <thead> <tr> <th v-for="col in columns" @click="sortBy(col)"> {{ col.name | capitalize}} </th> <th> Delete </th> </tr> </thead> <tbody> <tr v-for="(index,entry) in dataList | filterBy searchKey | orderBy sortKey sortOrder"> <td v-for="col in columns"> <span v-if="col.isKey"><a href="javascript:void(0)" @click="openEditItemDialog(index,'Edit item '+entry[col.name])">{{ entry[col.name] }}</a></span> <span v-else>{{entry[col.name]}}</span> </td> <td class="text-center"> <button class="btn-danger" @click="deleteItem(entry)">delete</button> </td> </tr> </tbody> </table> <!-- 测试不用广播打开对话, test 必须是 :test.sync 同步模式 这样在子组件关闭的时候,父组件也知道状态 可能广播更科学吧 利于解耦 --> <!-- 前面是子后面是父 :fields="columns" :fields 子 columns 父--> <!-- @create-item="createItem" 这个绑定事件 就和其他不一样了 两个都是父东西 把父事件 create-item 绑定到 createItem 方法 --> <!-- 又看遍文档 -@-> 在子组件上监听自定义事件(当子组件触发 “my-event” 时将调用事件处理器) --> <!-- 其实是约定,自定义子组件有这么一个事件, 如果子组件触发,发送了这个事件, 父组件就用 **同名** 的事件处理器,处理这个事件的响应 --> <!-- 或者这么理解吧 有助于记忆 绑定子发送的事件 @create-item 到父方法 createItem 这样就又是 子:父 --> <modal-dialog :mode="mode" :title="title" :fields="columns" :item="item" :test.sync='test' @create-item="createItem" @update-item="updateItem"> </modal-dialog> <div class="container"> <button class="btn" @click="openNewItemDialog('Create new item')">Create</button> </div> </template> <template id="dialog-template"> <div id="dialogs"> <!-- <div class="dialog" :class="{ 'dialog-active' : test }"> --> <div class="dialog" :class="{ 'dialog-active' : show }"> <div class="dialog-content"> <header class="dialog-header"> <h1 class="dialog-title">{{ title }}</h1> </header> <div v-for="field in fields" class="form-group"> <label>{{ field.name }}</label> <select v-if="field.dataSource" v-model="item[field.name]" :disabled="mode === 2 && field.isKey "> <option v-for="opt in field.dataSource" :value="opt"> {{ opt }}</option> </select> <input v-else type="text" v-model="item[field.name]" :disabled="mode === 2 && field.isKey"> </div> <footer class="dialog-footer"> <div class="form-group"> <label></label> <button @click="save">Save</button> <button @click="close">Close</button> </div> </footer> </div> </div> <div class="dialog-overlay"></div> </div> </template>
* { margin: 0; padding: 0; box-sizing: border-box; font-family: Helvetica, simhei, Arial, sans-serif; } html { font-size: 1rem; } body { margin-top: 100px; } table, td, th { border-collapse: collapse; border-spacing: 0 } table { width: 100%; } td, th { border: 1px solid #bcbcbc; padding: 5px 10px; } th { padding: 10px; font-weight: 400; color: #fff; background: #0090d3; cursor: pointer; } tr:nth-of-type(odd) { background: #fff } tr:nth-of-type(even) { background: #eee } h1 { font-size: 1.5rem; margin-bottom: 2rem; } input { outline: none } input[type=text] { padding: 3px 6px; font-size: 1.2rem; border: 1px solid #ccc; } input[type=text]:focus { border-color: #0090d3; transition: .3s ease-in; } button { display: inline-block; box-sizing: border-box; padding: 10px 30px; background: #0090d3; color: #fff; border: 1px solid #0090d3; border-radius: 3px; outline: 0; transition: .4s ease-out; } button:hover, button:focus { opacity: 0.8; cursor: pointer; transition: .15s ease-in; } #app { margin: 0 auto; max-width: 640px; } .btn-danger { padding: 5px 15px; border: 1px solid salmon; background: salmon; } .btn-save { border: 1px solid #0090d3; background: #0090d3; } .btn-close { border: 1px solid #ccc; background: #ccc; } .container { padding-left: 15px; padding-right: 15px; margin: 10px; } .search-input { width: 80%; } .form-group { margin: 10px; } .form-group > label { display: inline-block; padding-right: 1rem; width: 5rem; text-align: right; } .form-group > input, .form-group > select { display: inline-block; height: 1.8rem; line-height: 1.8rem; } .text-center { text-align: center; } .dialog { width: 480px; position: fixed; left: 50%; top: 2em; transform: translateX(-50%); z-index: 2000; visibility: hidden; backface-visibility: hidden; perspective: 1300px; } .dialog-active { visibility: visible; } .dialog-active .dialog-content { opacity: 1; transform: rotateY(0); } .dialog-active ~ .dialog-overlay { opacity: 1; visibility: visible; } .dialog-content { border-radius: 3px; background: #fff; overflow: hidden; box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1); transition: .5s ease-in-out; opacity: 0; transform-style: preserve-3d; transform: rotateY(-70deg); } .dialog-header { background: #0090d3; color: #fff; } .dialog-title { margin: 0; font-size: 2em; text-align: center; font-weight: 200; line-height: 2em; } .dialog-body { padding: 2em; } .dialog-footer { margin: 0 2em; padding: 1em 0; border-top: 1px solid rgba(0, 0, 0, 0.1); } .dialog-overlay { content: ""; position: fixed; visibility: hidden; top: 0; left: 0; right: 0; bottom: 0; z-index: 1000; opacity: 0; background: rgba(0, 0, 0, 0.5); transition: all .6s; }