多个指令的共享控制器不起作用

Sharing controller for multiple directives not working

Plunker Code

我在让 ng-class 在 flipCard 指令的模板中工作时遇到问题。从技术上讲它确实有效,如果我在控制器中立即设置 属性 它将添加 css class.

在我看来,子指令可能正在获取控制器的独立副本,而不是共享基础 flipCard 指令的控制器。因此,当我调用 flipCtrl.move 时,它正在调用 flipCardOver 指令的控制器实例,因为在 flipCtrl.move 函数中设置控制器上的 属性 不会更新基本父级 flipped 属性,虽然控制台记录它说它设置为 true。

目标:flipCard 上拥有一个与 flipCardBackflipCardFrontflipCardOver、[= 共享的控制器23=] 同时还允许在一个页面上使用多个 flipCard 指令并且不会发生冲突。

我错过了什么?

angular.module('cardFlip', [])
  .controller('flipCardController', ['$scope', '$element', '$timeout', '$window',
    function($scope, $element, $timeout, $window) {
      var vm = this;

      vm.flipped = false;
      vm.moved = false;

      vm.originalTop = -1;
      vm.originalLeft = -1;


      vm.move = function(e) {

        vm.flipped = true;
        console.log(vm.flipped);
      };


      vm.reset = function() {
        vm.flipped = false;
      };
    }
  ])
  .directive('flipCard', function() {

    return {
      restrict: 'AE',
      controller: 'flipCardController',
      controllerAs: 'flipCtrl',
      scope: true,
      transclude: true,

      // LOOK: this is the template that I am expecting to change
      template: '<div class="container"><div class="panel" ng-class="{ flip: flipCtrl.flipped, slide: flipCtrl.moved }" ng-transclude></div></div>'
    }

  })
  .directive('flipCardFront', function() {
    return {
      restrict: 'AE',
      require: '^flipCard',
      transclude: true,
      template: '<div class="front" ng-transclude></div>'
    }
  })
  .directive('flipCardBack', function() {
    return {
      restrict: 'AE',
      require: '^flipCard',
      transclude: true,
      template: '<div class="back" ng-transclude></div>'
    }
  })
  .directive('flipCardOver', function() {

    return {
      restrict: 'AE',
      require: '^flipCard',
      link: function(scope, element, attribs, flipCtrl) {


        // LOOK: this is the event I am expecting to call move that sets flipped to true
        element.on('click', flipCtrl.move);
      }
    }
  })
  .directive('flipCardReset', function() {
    return {
      restrict: 'AE',
      require: '^flipCard',
      link: function(scope, element, attribs, flipCtrl) {

        element.on('click', flipCtrl.reset);

      }
    }
  });
/* Styles go here */

[ng-click] {
  cursor: pointer;
}
.panel .pad {
  padding: 0 15px;
}
.panel.flip .action {
  display: none;
}
.panel {
  float: left;
  width: 200px;
  height: 200px;
  margin: 20px;
  position: relative;
  font-size: .8em;
  -webkit-perspective: 600px;
  perspective: 600px;
}
/* -- make sure to declare a default for every property that you want animated -- */

/* -- general styles, including Y axis rotation -- */

.panel .front {
  float: none;
  position: absolute;
  top: 0;
  left: 0;
  z-index: 900;
  width: inherit;
  height: inherit;
  background: #6b7077;
  -webkit-transform: rotateX(0) rotateY(0);
  transform: rotateX(0) rotateY(0);
  -webkit-transform-style: preserve-3d;
  transform-style: preserve-3d;
  -webkit-backface-visibility: hidden;
  backface-visibility: hidden;
  /* -- transition is the magic sauce for animation -- */
  -webkit-transition: all .4s ease-in-out;
  transition: all .4s ease-in-out;
}
.panel.flip .front {
  z-index: 900;
  -webkit-transform: rotateY(179deg);
  transform: rotateY(179deg);
}
.panel.slide {
  top: 15%;
  left: 15%;
  -webkit-transition: all 5s ease-in-out;
  transition: all 5s ease-in-out;
}
.panel .back {
  float: none;
  position: absolute;
  top: 0;
  left: 0;
  z-index: 800;
  width: inherit;
  height: inherit;
  box-shadow: 0 2px 15px rgba(0, 0, 0, 0.2);
  -webkit-transform: rotateY(-179deg);
  transform: rotateY(-179deg);
  -webkit-transform-style: preserve-3d;
  transform-style: preserve-3d;
  -webkit-backface-visibility: hidden;
  backface-visibility: hidden;
  /* -- transition is the magic sauce for animation -- */
  -webkit-transition: all .4s ease-in-out;
  transition: all .4s ease-in-out;
}
.panel.flip .back {
  z-index: 1000;
  -webkit-transform: rotateX(0) rotateY(0);
  transform: rotateX(0) rotateY(0);
}
<!DOCTYPE html>
<html>

<head>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.8/angular.js"></script>
</head>

<body>
  <div ng-app="cardFlip">
    <div ui-view="main">
      <flip-card>
        <flip-card-front>
          <div style="width:50px;height:50px;" flip-card-over="function(){return true;}">
            FRONT1
          </div>
        </flip-card-front>
        <flip-card-back>
          <div style="width:100px;height:100px;" flip-card-reset="function(){return true;}">
            BACK1
          </div>
        </flip-card-back>
      </flip-card>
    </div>
  </div>
</body>

</html>

起初我也被难住了,但后来我意识到 - 你没有告诉 Angular 使用 scope.$evalAsync() 的更改(比 $apply() 或 [=15= 更安全] 以防已经有摘要发生)。这是因为您自己处理点击,而不是使用 ng-click(触发摘要,所以没有这个问题)。

这里有一个 forked Plunkr 可以正常工作。

angular.module('cardFlip', [])
  .controller('flipCardController', ['$scope', '$element', '$timeout', '$window',
    function($scope, $element, $timeout, $window) {
      var vm = this;

      vm.flipped = false;
      vm.moved = false;

      vm.originalTop = -1;
      vm.originalLeft = -1;


      vm.move = function(e) {

        vm.flipped = true;
        console.log(vm.flipped);
      };


      vm.reset = function() {
        vm.flipped = false;
      };
    }
  ])
  .directive('flipCard', function() {

    return {
      restrict: 'AE',
      controller: 'flipCardController',
      controllerAs: 'flipCtrl',
      scope: true,
      transclude: true,

      // LOOK: this is the template that I am expecting to change
      template: '<div class="container"><div class="panel" ng-class="{ flip: flipCtrl.flipped, slide: flipCtrl.moved }" ng-transclude></div></div>'
    }

  })
  .directive('flipCardFront', function() {
    return {
      restrict: 'AE',
      require: '^flipCard',
      transclude: true,
      template: '<div class="front" ng-transclude></div>'
    }
  })
  .directive('flipCardBack', function() {
    return {
      restrict: 'AE',
      require: '^flipCard',
      transclude: true,
      template: '<div class="back" ng-transclude></div>'
    }
  })
  .directive('flipCardOver', function() {

    return {
      restrict: 'AE',
      require: '^flipCard',
      link: function(scope, element, attribs, flipCtrl) {


        // LOOK: this is the event I am expecting to call move that sets flipped to true
        element.on('click', function () {
            flipCtrl.move();
            scope.$evalAsync(); // tell Angular we did something
        });
      }
    }
  })
  .directive('flipCardReset', function() {
    return {
      restrict: 'AE',
      require: '^flipCard',
      link: function(scope, element, attribs, flipCtrl) {

        element.on('click', function () {
            flipCtrl.reset();
            scope.$evalAsync(); // tell Angular
        });
      }
    }
  });
/* Styles go here */

[ng-click] {
  cursor: pointer;
}
.panel .pad {
  padding: 0 15px;
}
.panel.flip .action {
  display: none;
}
.panel {
  float: left;
  width: 200px;
  height: 200px;
  margin: 20px;
  position: relative;
  font-size: .8em;
  -webkit-perspective: 600px;
  perspective: 600px;
}
/* -- make sure to declare a default for every property that you want animated -- */

/* -- general styles, including Y axis rotation -- */

.panel .front {
  float: none;
  position: absolute;
  top: 0;
  left: 0;
  z-index: 900;
  width: inherit;
  height: inherit;
  background: #6b7077;
  -webkit-transform: rotateX(0) rotateY(0);
  transform: rotateX(0) rotateY(0);
  -webkit-transform-style: preserve-3d;
  transform-style: preserve-3d;
  -webkit-backface-visibility: hidden;
  backface-visibility: hidden;
  /* -- transition is the magic sauce for animation -- */
  -webkit-transition: all .4s ease-in-out;
  transition: all .4s ease-in-out;
}
.panel.flip .front {
  z-index: 900;
  -webkit-transform: rotateY(179deg);
  transform: rotateY(179deg);
}
.panel.slide {
  top: 15%;
  left: 15%;
  -webkit-transition: all 5s ease-in-out;
  transition: all 5s ease-in-out;
}
.panel .back {
  float: none;
  position: absolute;
  top: 0;
  left: 0;
  z-index: 800;
  width: inherit;
  height: inherit;
  box-shadow: 0 2px 15px rgba(0, 0, 0, 0.2);
  -webkit-transform: rotateY(-179deg);
  transform: rotateY(-179deg);
  -webkit-transform-style: preserve-3d;
  transform-style: preserve-3d;
  -webkit-backface-visibility: hidden;
  backface-visibility: hidden;
  /* -- transition is the magic sauce for animation -- */
  -webkit-transition: all .4s ease-in-out;
  transition: all .4s ease-in-out;
}
.panel.flip .back {
  z-index: 1000;
  -webkit-transform: rotateX(0) rotateY(0);
  transform: rotateX(0) rotateY(0);
}
<!DOCTYPE html>
<html>

<head>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.8/angular.js"></script>
</head>

<body>
  <div ng-app="cardFlip">
    <div ui-view="main">
      <flip-card>
        <flip-card-front>
          <div style="width:50px;height:50px;" flip-card-over="function(){return true;}">
            FRONT1
          </div>
        </flip-card-front>
        <flip-card-back>
          <div style="width:100px;height:100px;" flip-card-reset="function(){return true;}">
            BACK1
          </div>
        </flip-card-back>
      </flip-card>
    </div>
  </div>
</body>

</html>