背景
今天在做一个小案例,将数据填在表格里。但是表格里价格一栏的数据要随数量变化。
问题
这里我用filter过滤器做。直接报错:
[Vue warn]: Error in render: "TypeError: Cannot read property '0' of undefined"
而且页面也没内容了。
注释掉过滤器就正常显示,所以我认为是我的过滤器有问题。
1 <tbody> 2 <tr v-for="(b,index) in books" :key="index"> 3 <td>{{b.id}}</td> 4 <td>{{b.name}}</td> 5 <td>{{b.time}}</td> 6 <td>{{b.price | priceReal(index) | priceFormat}}</td> 7 <!-- <td>{{b.price | priceFormat}}</td> 这个没问题 --> 8 <td> 9 <button class="btn" @click="decrease(index)">-</button> 10 {{b.counter}} 11 <button class="btn" @click="increase(index)">+</button> 12 </td> 13 <td><button>删除</button></td> 14 </tr> 15 </tbody>
1 filters: { 2 priceReal(val1, index) { 3 return val1 * this.books[index].counter; //报错 4 }, 5 priceFormat(val2) { 6 return '¥' + val2.toFixed(2); 7 }, 8 }
解决问题
从报错信息看,应该是这个index为0时没有对象。所以我们先打印一下index以及this.books看看
1 priceReal(val1, index) { 2 console.log(val1,index); 3 console.log(this); 4 console.log(this.books); 5 // return val1 * this.books[index].counter; 6 },
打印结果如下:
这里形参传过来值是没问题,但是下面到books数组就出问题了。关键问题出在this上,我们想调用的是vue的实例对象下的books数组,这个this却是window,说明我没拿到books,难怪会说第一个数组内容就未定义!
现在只要调用vue实例对象就可以了。
首先设置全局变量
1 let that;
再在vue实例的“创建对象之前” 钩子函数里给that赋值:
1 beforeCreate() { 2 that = this; 3 },
过滤函数里打印that
1 priceReal(val1, index) { 2 console.log(val1,index); 3 console.log(this); 4 console.log(that); 5 console.log(this.books); 6 // return val1 * this.books[index].counter; 7 },
结果
现在终于可以用了。
ps:不过,最后我还是没用filter做这个功能,因为filter改变的数据不是响应式的。即使filter改了数值,在用computed计算属性时也不会发生变化。
总结
1、过滤器调用data里的数据时,this指向的是window而不是vue实例。这个需要注意!
2、filter过滤器不适合做需要响应式的数据。它更适合做格式上的变化。比如大写转小写,数字连接字符串,对一串字符进行筛选等等。
源码
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <title>基本模板语法案例</title> 7 <style> 8 * { 9 padding: 0; 10 margin: 0; 11 } 12 #app { 13 margin: 200px auto; 14 width: 600px; 15 } 16 table { 17 border-collapse: collapse; 18 border: 1px solid #cccccc; 19 text-align: center; 20 width: 600px; 21 } 22 th { 23 background-color: #999; 24 color: #ffffff; 25 } 26 th, td { 27 border: 1px solid #cccccc; 28 } 29 .btn { 30 width: 20px; 31 } 32 /* tbody td:nth-child(5) button { 33 20px; 34 } */ 35 </style> 36 </head> 37 <body> 38 <div id="app"> 39 <table> 40 <thead> 41 <tr> 42 <th></th> 43 <th>书籍名称</th> 44 <th>出版日期</th> 45 <th>价格</th> 46 <th>购买数量</th> 47 <th>操作</th> 48 </tr> 49 </thead> 50 <tbody> 51 <tr v-for="(b,index) in books" :key="index"> 52 <td>{{b.id}}</td> 53 <td>{{b.name}}</td> 54 <td>{{b.time}}</td> 55 <td>{{b.price | priceFormat}}</td> 56 <td> 57 <button class="btn" @click="decrease(index)">-</button> 58 {{b.counter}} 59 <button class="btn" @click="increase(index)">+</button> 60 </td> 61 <td><button @click="del(index)">删除</button></td> 62 </tr> 63 </tbody> 64 </table> 65 <div> 66 <p>总价:{{totalPrice | priceFormat}}</p> 67 <p>总数量:{{totalCounter}}</p> 68 69 </div> 70 </div> 71 <script src="../js/vue.js"></script> 72 <script> 73 let that; //设置vue对象为全局变量 74 const app = new Vue({ 75 el: '#app', 76 data: { 77 books: [{ //图书数据 78 id: 1, 79 name: '《水浒传》', 80 time: '2020-4-15', 81 price: 85, 82 counter: 1 83 }, { 84 id: 2, 85 name: '《红楼梦》', 86 time: '2020-4-15', 87 price: 98, 88 counter: 1 89 }, { 90 id: 3, 91 name: '《三国演义》', 92 time: '2020-4-15', 93 price: 10, 94 counter: 1 95 }, { 96 id: 4, 97 name: '《西游记》', 98 time: '2020-4-15', 99 price: 49, 100 counter: 1 101 }], 102 // totalPrice: 0, 103 }, 104 methods: { 105 increase(index) { //增加数量 106 const price = this.books[index].price/this.books[index].counter;//books里的price变量不是单价,而是该书的小计,所以这里要先把单价保存起来,后面使用。 107 this.books[index].counter++; 108 this.books[index].price = price * this.books[index].counter; 109 }, 110 decrease(index) { //减少数量 111 const price = this.books[index].price/this.books[index].counter; 112 this.books[index].counter--; 113 this.books[index].price = price * this.books[index].counter; 114 if (this.books[index].counter <= 0) { //数量小于等于0去掉这本书 115 this.del(index); 116 // this.$options.methods.del(this);这个方法报错 117 } 118 }, 119 del(index) { //删除该书 120 this.books.splice(index, 1); 121 } 122 }, 123 computed: { 124 totalPrice() { //总价格 125 let p = 0; 126 for (let b of this.books) { 127 p += b.price; 128 } 129 return p; 130 }, 131 totalCounter() { //总数量 132 let c = 0; 133 for (let b of this.books) { 134 c += b.counter; 135 } 136 return c; 137 } 138 }, 139 beforeCreate() { //创建之前保存vue实例对象 140 that = this; 141 }, 142 filters: { 143 // priceReal(val1, index) { 144 // return val1 * that.books[index].counter; //如果要调用data里的变量,好像过滤器不方便 145 // }, 146 priceFormat(val2) { //将整型数据格式化 147 return '¥' + val2.toFixed(2); 148 }, 149 } 150 }); 151 </script> 152 </body> 153 </html>
更多vue小案例