You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

AngularJS骰子应用:实现rollDice函数独立调用的OOP改造

嘿,我完全懂你现在的困扰!多个骰子组共用同一个控制器里的状态,点一个按钮另一个也跟着变,这确实是AngularJS新手很容易踩的坑。咱们用面向对象的思路来封装一下,就能彻底解决这个问题啦~

核心问题分析

你现在的控制器里,$scope.dice、$scope.marked这些状态都是全局(相对于控制器)的变量,所有骰子组共用同一套数据,自然会互相影响。解决思路就是把每个骰子组的状态和对应的方法封装成独立的对象,让每个组拥有自己的专属数据和行为。

下面给你两种常用的实现方案,按需选择:


方案1:用Factory封装骰子组对象

这种方式适合在同一个控制器里管理多个独立的骰子实例,把骰子的逻辑封装成可实例化的类,每个实例都是完全独立的。

代码实现

首先创建一个DiceSet工厂,封装状态和方法:

angular.module('diceApp', [])
.factory('DiceSet', function() {
  // 定义骰子组构造函数
  function DiceSet(numDice, sides) {
    this.numDice = numDice;
    this.sides = sides;
    this.dice = Array(numDice).fill(0); // 存储骰子结果
    this.marked = Array(numDice).fill(false); // 存储骰子标记状态
  }

  // 掷骰子方法:只掷未标记的骰子
  DiceSet.prototype.rollDice = function() {
    this.dice = this.dice.map((val, index) => {
      return this.marked[index] ? val : Math.floor(Math.random() * this.sides) + 1;
    });
  };

  // 标记/取消标记骰子方法
  DiceSet.prototype.markDice = function(index) {
    this.marked[index] = !this.marked[index];
  };

  // 检查标记骰子的逻辑(示例:返回标记数量)
  DiceSet.prototype.checkMarkedDice = function() {
    return this.marked.filter(isMarked => isMarked).length;
  };

  return DiceSet;
})
.controller('DiceController', function($scope, DiceSet) {
  // 创建两个独立的骰子组实例
  $scope.diceGroup1 = new DiceSet(4, 6);
  $scope.diceGroup2 = new DiceSet(3, 6);
});

对应的HTML模板,每个组绑定自己的实例:

<div ng-controller="DiceController">
  <!-- 第一组:4d6 -->
  <div>
    <button ng-click="diceGroup1.rollDice()">Roll 4d6</button>
    <div ng-repeat="die in diceGroup1.dice track by $index">
      <span 
        ng-click="diceGroup1.markDice($index)"
        ng-class="{'marked': diceGroup1.marked[$index]}"
      >
        {{die}}
      </span>
    </div>
    <p>Marked dice: {{diceGroup1.checkMarkedDice()}}</p>
  </div>

  <!-- 第二组:3d6 -->
  <div>
    <button ng-click="diceGroup2.rollDice()">Roll 3d6</button>
    <div ng-repeat="die in diceGroup2.dice track by $index">
      <span 
        ng-click="diceGroup2.markDice($index)"
        ng-class="{'marked': diceGroup2.marked[$index]}"
      >
        {{die}}
      </span>
    </div>
    <p>Marked dice: {{diceGroup2.checkMarkedDice()}}</p>
  </div>
</div>

为什么能解决问题?

每个DiceSet实例都有自己的dice和marked数组,方法也是属于实例的,两个骰子组完全独立,不会再互相干扰。


方案2:用Directive创建可复用骰子组件

如果想把骰子组做成可复用的独立组件(比如在页面多个地方使用),用Directive的隔离作用域更合适,这也是AngularJS组件化的标准做法。

代码实现

angular.module('diceApp', [])
.directive('diceSet', function() {
  return {
    restrict: 'E', // 作为标签使用
    scope: {
      numDice: '@', // 接收外部传入的骰子数量
      sides: '@'    // 接收外部传入的骰子面数
    },
    template: `
      <button ng-click="rollDice()">Roll {{numDice}}d{{sides}}</button>
      <div ng-repeat="die in dice track by $index">
        <span 
          ng-click="markDice($index)"
          ng-class="{'marked': marked[$index]}"
        >
          {{die}}
        </span>
      </div>
      <p>Marked dice: {{checkMarkedDice()}}</p>
    `,
    controller: function($scope) {
      // 初始化数据
      $scope.numDice = parseInt($scope.numDice);
      $scope.sides = parseInt($scope.sides);
      $scope.dice = Array($scope.numDice).fill(0);
      $scope.marked = Array($scope.numDice).fill(false);

      // 掷骰子方法
      $scope.rollDice = function() {
        $scope.dice = $scope.dice.map((val, index) => {
          return $scope.marked[index] ? val : Math.floor(Math.random() * $scope.sides) + 1;
        });
      };

      // 标记骰子方法
      $scope.markDice = function(index) {
        $scope.marked[index] = !$scope.marked[index];
      };

      // 检查标记骰子
      $scope.checkMarkedDice = function() {
        return $scope.marked.filter(isMarked => isMarked).length;
      };
    }
  };
});

HTML使用起来超级简洁,直接复用组件:

<dice-set num-dice="4" sides="6"></dice-set>
<dice-set num-dice="3" sides="6"></dice-set>

为什么能解决问题?

Directive的scope: {}创建了隔离作用域,每个<dice-set>标签都有自己独立的状态和方法,完全不会和其他组件共享数据,完美实现了组件的独立性和复用性。


内容的提问来源于stack exchange,提问作者DuFuS

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:19:47