vue.js中v-for和v-on:click使用初探

vue

Posted by Hanxu on July 2, 2018

技术点

vue.js实现v-for生成元素和v-on:click点击为元素添加样式


vue.js中v-for和v-on:click使用初探

实现一组元素中单个元素class变换

在影院详情页面cinema-detail-page中,先遍历生成该影院下属影片列表的所有影片。

将海报图展示如下:

click01

我们需要的响应动作是,点击一个电影海报,将对应div标签的class添加active项,以便用户由对应元素的动态变化确认自己选中了对应电影。

将结果图展示如下:

click02

实现方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<div class="movie" v-for="(movie_img, index) in cinema.online_moive" v-if="notSameFilm(originalFilm.img,movie_img) && index < 6" :key="index" :data-index="index" :class="{'active': isActive==index}" v-on:click="clickMovie(index)">
    <img :src="movie_img" alt="movie_img" />
</div>

<script>

export default {
  data() {
    return {
      isActive: -1,
    }
  },
  methods: {
    clickMovie: function(index) {
        if (this.isActive != index) {
            this.isActive = index;
        }
    }
  }
}
<style>
  .movie {
      width: 162px;
      height: 227px;
      border: 4px solid #fff;
      box-shadow: 0 1px 3px 0 hsla(0, 0%, 66%, .5);
      display: inline-block;
      transform-origin: 50%;
      transform: scale(.82);
      transition: transform, .1s, -webkit-transform .1s;
      word-spacing: 0;
      white-space: nowrap;
  }

  .movie img {
      width: 100%;
      height: 100%;
  }
  .movie.active {
      transform: scale(1);
      border-color: #2A5CAA;
  }

</style>

实现一组元素中多个元素class变换(各自独立点击触发)

在选座页面chooseSeats中,我们需要点击选择座位并激活相应样式对应的class。

将座位图展示如下:

click03

将结果图展示如下:

click04

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
<div class="seats-wrapper">
    <div id="seatWrap">
        <div class="seat" v-for="(n,index) in clickList" :key="index" :class="getSeatState(index)" v-on:click="handler(index)">
            
        </div>
    </div>
</div>

<script>

</script>

export default {
    data() {
      return {
        clickIndex: 0,
        ticketPrice: 28,
        totalPrice: 0,
        clickList:[
          0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 2, 2, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0,
          0, 0, 0, 0, 0, 0, 0, 0
        ],
        seatNum: 0,
        isTicketActive: false
      }

    },
    methods: {
      handler: function(index) {
        if (this.clickList[index] == 0) {
          if (this.seatNum < 4) {
            this.$set(this.clickList,index,1);
            this.seatNum++;
            this.isTicketActive = true;
          } else {
            alert("最多选4个座位!")
          }
        } else if (this.clickList[index] == 1) {
          this.$set(this.clickList,index,0);
          this.seatNum--;
          if (this.seatNum == 0) {
            this.isTicketActive = false;
          }
        }
        this.totalPrice = this.ticketPrice * this.seatNum;
      },
      getSeatState: function(index) {
        if (this.clickList[index] == 0) {
          return "seat";
        } else if (this.clickList[index] == 1) {
          return "seatChoosed";
        } else if (this.clickList[index] == 2) {
          return "seatSold";
        }
      }
    }

<style>
    #seatWrap {
      width:440px;
      margin:auto;
      background:#ccc;
    }

    .seat, .seatSold, .seatChoosed{
      cursor: pointer;
      width:23px;
      height:19px;
      margin:3px 14px 14px 14px;
      float:left;
      text-align:center;
      color:#fff;
    }
    .seat {
      background-image: url("../../assets/img/chooseSeats/selectable_seat.gif");
      background-repeat: no-repeat;
    }

    .seatChoosed {
      background-image: url("../../assets/img/chooseSeats/selected_seat.gif");
      background-repeat: no-repeat;
    }

    .seatSold {
      background-image: url("../../assets/img/chooseSeats/sold_seat.gif");
      background-repeat: no-repeat;
    }

</style>

列表渲染注意事项

由于 JavaScript 的限制,Vue 不能检测以下变动的数组:

  1. 当你利用索引直接设置一个项时,例如:vm.items[indexOfItem] = newValue
  2. 当你修改数组的长度时,例如:vm.items.length = newLength

举个例子:

1
2
3
4
5
6
7
8
var vm = new Vue({
  data: {
    items: ['a', 'b', 'c']
  }
})
vm.items[1] = 'x' // 不是响应性的
vm.items.length = 2 // 不是响应性的

为了解决第一类问题,以下两种方式都可以实现和 vm.items[indexOfItem] = newValue 相同的效果,同时也将触发状态更新:

1
2
// Vue.set
Vue.set(vm.items, indexOfItem, newValue)
1
2
// Array.prototype.splice
vm.items.splice(indexOfItem, 1, newValue)

也可以使用 vm.$set 实例方法,该方法是全局方法 Vue.set 的一个别名:

1
vm.$set(vm.items, indexOfItem, newValue)

为了解决第二类问题,可以使用 splice:

1
vm.items.splice(newLength)

另外:检测变化的注意事项

受现代 JavaScript 的限制,Vue 不能检测到对象属性的添加或删除。由于 Vue 会在初始化实例时对属性执行 getter/setter 转化过程,所以属性必须在 data 对象上存在才能让 Vue 转换它,这样才能让它是响应的。例如:

1
2
3
4
5
6
7
8
9
10
11
var vm = new Vue({
  data:{
  a:1
  }
})

// `vm.a` 是响应的

vm.b = 2
// `vm.b` 是非响应的

Vue 不允许在已经创建的实例上动态添加新的根级响应式属性 (root-level reactive property)。然而它可以使用 Vue.set(object, key, value) 方法将响应属性添加到嵌套的对象上:

1
Vue.set(vm.someObject, 'b', 2)

还可以使用 vm.$set 实例方法,这也是全局 Vue.set 方法的别名:

1
this.$set(this.someObject,'b',2)