使用angularjs指令

时间:2016-07-03 20:59:09

标签: angularjs

我有一个有两个属性的指令:
1.从控制器传递的标题对象
2.从控制器传递的onchange函数。

scope: {
  title: '=',
  change: '&'
}


//on the link function I update the object, and call the function.

link: function(scope) {
    scope.setValue = function() {
      scope.title = "Yaniv"
      scope.change();
    };
},
不知何故,尽管我以相反的方式写了它,但似乎首先它调用了函数,然后才更新标题对象。 克服这个问题的最佳方法是什么?我已经考虑过使用setTimeout,它实际上是在解决这个问题。但是,我想知道为什么会出现这个问题,以及这里是否有更清洁的解决方案。 附上JS小提琴:http://jsfiddle.net/sz82r7pg/

2 个答案:

答案 0 :(得分:1)

title双向绑定在摘要的末尾的父范围中更新。在此之前,title值更改不会从指令范围传播到父范围。

setValueng-click调用,并在摘要期间运行。这意味着应在当前摘要后使用changesetTimeout调用$imeout

   scope.setValue = function () {
                scope.title = "Yaniv"
                $imeout(() => scope.change());
   };

这表示难闻的气味和XY问题,因为title更新已经与摘要相关联,change是多余的。这是框架的工作。应使用

跟踪title更改
$scope.$watch('title', (newValue, oldValue) => {
  if (newValue === oldValue) return;
  ...
});
在父范围内

答案 1 :(得分:1)

首先要注意的是该指令有自己的isolate scope对象,其title属性绑定到控制器作用域的标题。这个数据绑定基本上是由手表完成的,您可能知道使用手表进行的更改检测不是即时的,发生在摘要循环中,这总是在更改完成后发生,并且调用堆栈被显示(在下一个微/宏任务中)我不确切知道哪个,但这里无关紧要)。然而,调用函数是瞬时的。所以会发生以下情况:

  • 设置指令范围的标题
  • 呼叫更改,调用foo,控制器范围标题是旧的
  • 调用angular运行摘要循环后,注意指令范围的变化,并将其传播到控制器范围

这是完全预期的行为。在你的情况下最简单的方法是不使用你自己的变化检测,但使用内置于角度的那个:

angular.module('zippyModule', [])
.controller("Ctrl3", function ($scope) {
  $scope.title = 'Ori';
  $scope.$watch("title", function(newValue, oldValue) {
    if (newValue !== oldValue) {
      alert(newValue);
    }
  });
})
.directive('zippy', function() {
  return {
    restrict: 'AE',
    scope: {
      title:'=zTitle'
    },
    template: '<button ng-click="setValue()">What would be the value of title??</button>',
    link: function(scope) {
      scope.setValue = function () {
        scope.title = "Yaniv";
      };
    }
  }
});
button {
  font-size:24px;
  margin:40px;
  padding: 10px 40px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.7/angular.js"></script>

<div ng-app="zippyModule">
  <div ng-controller="Ctrl3">
    <div zippy z-title="title"></div>
  </div>
</div>

在写这个片段时,我意识到你在小提琴中使用了古老版本的angularjs(1.0.2)。我必须做一些修改才能使用1.5.x。

如果您不想使用手表,那么您可以做的最好的事情就是将新标题传递给更改功能。

angular.module('zippyModule', [])
.controller("Ctrl3", function ($scope) {
  $scope.title = 'Ori';
  $scope.foo = function(newTitle) {
    alert(newTitle);
  };
})
.directive('zippy', function() {
  return {
    restrict: 'AE',
    scope: {
      change: '&'
    },
    template: '<button ng-click="setValue()">What would be the value of title??</button>',
    link: function(scope) {
      scope.setValue = function () {
        scope.change({title:"Yaniv"});
      };
    }
  }
});
button {
  font-size:24px;
  margin:40px;
  padding: 10px 40px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.7/angular.js"></script>

<div ng-app="zippyModule">
  <div ng-controller="Ctrl3">
    <div zippy change="foo(title)"></div>
  </div>
</div>

<强>更新

在评论中你希望在控制器和指令之间建立一种更紧密的耦合。这样的事情通常被认为是一种糟糕的设计,但有时候这是唯一正确的解决方案(大多数时候涉及非角度组件)。因此,我们的想法是创建一个可以直接操作的API对象,并将其传递给指令。

angular.module('zippyModule', [])
.controller("Ctrl3", function ($scope) {
  $scope.title = 'Ori';
  $scope.fooApiImpl = {
    title: "Ori",
    change: function() {
      alert($scope.fooApiImpl.title);
    }
  };
})
.directive('zippy', function() {
  return {
    restrict: 'AE',
    scope: {
      fooApi: '='
    },
    template: '<button ng-click="setValue()">What would be the value of title??</button>',
    link: function(scope) {
      scope.setValue = function () {
        scope.fooApi.title = "Yaniv";
        scope.fooApi.change();
      };
    }
  }
});
button {
  font-size:24px;
  margin:40px;
  padding: 10px 40px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.7/angular.js"></script>

<div ng-app="zippyModule">
  <div ng-controller="Ctrl3">
    <div zippy foo-api="fooApiImpl"></div>
  </div>
</div>