使用v-bind将click事件绑定到子组件

时间:2019-06-01 15:08:00

标签: vue.js

我创建了一个简单的Minesweeper游戏,当涉及到决策时,呈现哪个单元有三种可能性:

  • 未公开的单元格
  • 暴露的地雷细胞
  • 暴露的中性细胞

我创建了一个行组件,该组件呈现该行包含的所有单元格。

<template>
  <div>

    <component 
      v-for="(cell, columnIndex) in row" 
      :key="columnIndex" 
      v-bind="getCellProps(cell, columnIndex)"
      :is="getComponentCell(cell)"
    />

  </div>
</template>

<script>
// imports here

export default {
  components: {
    UnrevealedCell,
    RevealedNeutralCell,
    RevealedMineCell
  },
  props: {
    row: Array,
    rowIndex: Number
  },
  methods: {
    getCellProps: function(cell, columnIndex) {
      if(cell.revealed) {
        if (cell.isMine) {
          return {};
        } else {
          return {
            mineNeighbours: cell.mineNeighbours
          };
        }
      } else {
        return {
          unrevealedCell: cell,
          x: columnIndex,
          y: this.rowIndex,
          cellClicked: this.onCellClicked
        };
      }
    },
    getComponentCell: function(cell) {
      if(cell.revealed) {
        if (cell.isMine) {
          return RevealedMineCell;
        } else {
          return RevealedNeutralCell;
        }
      } else {
        return UnrevealedCell;
      }
    },
    onCellClicked: function(x, y) {
      debugger;
    }
  }
}
</script>

很遗憾,我的cellClicked事件无法正常工作。子组件能够正确发出事件,但是我的onCellClicked无法执行。我认为这是因为我不会写

cellClicked: this.onCellClicked

通常会是

@cellClicked

如果没有@,则该属性可能会添加为组件属性。如何解决此问题以监听发出的cellClicked事件?

1 个答案:

答案 0 :(得分:0)

一些想法出现。

首先,该方法不起作用的原因是因为v-bind用于设置组件属性和元素属性。 @前缀是v-on的简写,因此从这个意义上说,它不是属性或道具,它本身就是一条指令。 v-on确实支持对象版本,就像v-bind一样,因此您可以执行类似v-on="getCellEvents(cell, columnIndex)"的操作,并为每种单元格类型返回合适的对象。这可能是对您原始问题的最干净直接的答案。还可以提供不太干净和不太直接的答案...

您可以通过将cellClicked用作子单元格的支持,然后将其作为回调函数而不是发出事件来实现。不是说您应该,但是可以。这将与您上面发布的代码完全相同。

另一种选择是只为所有单元格添加事件监听器。在模板中包括@cellClicked="onCellCicked",而不必担心单元格类型。如果其他单元格类型不发出该事件,则不会发生任何事情。 Vue不知道组件可以触发什么事件,您可以听任何东西。

更多想法...

您的细胞模板有点贫血。我知道人们通常建议不要将逻辑放在模板之外,但就您而言,我想您可能已经太过分了,这只会使事情变得更难以理解。有两种解决方法:

  1. 重写您的组件以使用render函数。存在模板是因为人类发现它们比render函数更易于阅读,但是对于您而言,您仍然拥有JavaScript中的所有逻辑。该模板并没有真正添加任何内容,并且使用render函数进行全面学习可能比您当前拥有的更加容易理解。
  2. 将逻辑移到模板中。我看不出有任何明显的理由不按照您发布的代码那样进行。我将在最后发布一个示例。

这两种方法都可以消除您添加事件监听器的问题。

关于click事件的最后一个想法是,您可以使用事件传播来处理它们。在周围组件的适当元素上添加一个单击侦听器,根本不侦听单元格/行上的事件。然后,单个侦听器可以确定(可能是轻率地)单击了哪个单元,以及是否需要对其进行任何操作。尽管这会增加组件之间的耦合,但我想这并不重要,因为这些组件无论如何也无法在其他地方重用。在现阶段,我不建议您使用这种方法,但是每当您发现自己创建大量需要相同事件的重复组件时,就应该牢记这一点。在您的情况下,只有开始遇到性能问题才可能有意义,即使那样,也可能会有更好的方法来解决此类问题。

因此,我向您承诺了模板方法的示例:

<template>
  <div>
    <template v-for="(cell, columnIndex) in row">
      <unrevealed-cell
        v-if="!cell.revealed"
        :key="columnIndex"
        :unrevealed-cell="cell"
        :x="columnIndex"
        :y="rowIndex"
        @cellClicked="onCellClicked"
      />
      <revealed-mine-cell
        v-else-if="cell.mine"
      />
      <revealed-neutral-cell
        v-else
        :mineNeighbours="cell.mineNeighbours"
      />
    </template>
  </div>
</template>

我不确定UnrevealedCell为什么需要xy,但是如果只是为了使其能够作为事件的一部分发出它们,那么您可能要考虑注册侦听器为@cellClicked="onCellClicked(columnIndex, rowIndex)",则无需从单元格发出坐标。我还想知道这些单元是否需要3个单独的组件。我的直觉是,一个组件更适合,而行组件根本不需要对单个单元格有任何了解。