【问题标题】:Is one of these the more "Angular" way of communicating with a directive?其中一种是与指令通信的更“角度”的方式吗?
【发布时间】:2026-01-20 00:20:03
【问题描述】:

我在程序员的堆栈交换上问了这个问题,但没有得到任何答复,所以我想我会在这里碰碰运气......

我正在做一个项目,我想封装一个指令库并将其分发给其他开发人员使用。我想在这个封装的代码中保留对模型的更改,所以我真的不希望开发人员在 lib 之外更改范围变量。

在我的代码中,我有两种不同的方法可以从父控制器与我的库进行通信。

第一个理论是创建一个包含指令和服务的库。父控制器将调用该服务,该服务将处理对 lib 模型的所有更改,并且指令将根据这些更改做出反应。

第二个理论是将所有改变模型的函数放在指令本身中,并在父级上调用指令范围进行更改。

这是一个更详细地显示我所要求的内容的 plunker。这是一个简单的示例,但说明了两种不同的方法。

http://plnkr.co/edit/CR350Vx7NiHs5tkjNWZL?p=preview

我倾向于第二种方法,因为从开发场景中实现它看起来更简洁。

Angular 专家有什么建议吗?

Plunker Html:

<body ng-app="myApp">
    This is Example 1 - Using a service to modify directive
    <div ng-controller="Example1Ctrl">
        <example1-directive ng-model='example1Model'></example1-directive>
        <br>
        <br>
        <input type="button" value="Change Example 1" ng-click='changeExample1()' />
    </div>
    <br>
    <br>
    This is Example 2 - Modifying directive in the scope of the directive
    <div ng-controller="Example2Ctrl">
        <example2-directive ng-model='example2Model'></example2-directive>
        <br>
        <br>
        <input type="button" value="Change Example 2" ng-click='changeExample2()' />
    </div>
</body>

Plunker js

var app = angular.module("myApp", []);

//-------------------------------------------------- 
//-------- This is example 1
//-------------------------------------------------- 
app.controller("Example1Ctrl", function($scope, example1Svc) {
  $scope.example1Model = {
    value: "Example 1 - Original Value"
  }

  $scope.changeExample1 = function() {
    example1Svc.change($scope.example1Model, "Example 1 - Changed Value");
  }
});

/// This part would be encapsulated in a lib 
app.directive("example1Directive", function() {
  return {
    restrict: "E",
    scope: {
      model: "=ngModel"
    },
    template: "{{model.value}}"
  }
});

app.service("example1Svc", function() {
  this.change = function(example1Model, newValue) {
    example1Model.value = newValue;
  }
})
// End lib

//-------------------------------------------------- 
//-------- This is example 2
//-------------------------------------------------- 
app.controller("Example2Ctrl", function($scope, example1Svc) {
  $scope.example2Model = {
    value: "Example 2 - Original Value"
  }

  $scope.changeExample2 = function() {
    $scope.example2Model.change("Example 2 - Changed Value");
  }
});

/// This part would be encapsulated in a lib 
app.directive("example2Directive", function() {
  return {
    restrict: "E",
    scope: {
      model: "=ngModel"
    },
    template: "{{model.value}}",
    controller: function ($scope) {
      $scope.model.change = function(newValue) {
        $scope.model.value = newValue;
      }
    }
  }
});
// end lib

【问题讨论】:

  • 我不明白 - 你是否试图从控制器的指令中调用函数?
  • 在某种程度上。我正在尝试调用一个将修改指令外观的函数。我正在实现的现实世界指令是一个可分页的数据列表视图,我希望能够调用诸如 refresh()、firstPage()、lastPage() 等函数。问题是,我是否将它们放在一个服务并传入父作用域模型,还是我将函数放在父作用域模型上并调用它们而不需要传入模型(因为它已经在模型上)。

标签: angularjs angularjs-directive


【解决方案1】:

我对您的示例 #1 感到有些困惑。 exampleSvc.change 是做什么的?

示例 #2 肯定违反了 MVVM 最佳实践,因为它将控制器与视图耦合在一起。控制器(视图)应该与视图无关。他们应该只更改 ViewModel 以反映应用程序的当前状态。然后 View 将对 ViewModel 中的更改做出反应(无论 View 选择如何)。

尤其是,这些行“冒犯”了我认为的最佳实践:

$scope.model.change = function(newValue) {
   $scope.model.value = newValue;
}

现在你的控制器依赖于视图来定义函数的作用(或者它是否被定义为开始)。另外,如果另一个指令决定更改 .change 函数怎么办?

编辑: 看看这个SO question,尤其是 Mark 的回答。

编辑#2: 当某些事件需要到达可能感兴趣的任何指令或子控制器时,有一个有趣的案例。使用 $scope.$broadcast(在控制器中)和 $scope.$on(在指令中)来处理。这是plunker

【讨论】:

  • 阅读该链接后,这似乎正是他们正在做的事情。向指令的控制器添加一个函数,以处理对指令范围的模型数据的更改。我不完全确定如何将控制器与视图耦合。在某些时候,视图必须与控制器交互,我在 ng-click 中做到了这一点。然后控制器调用指令的范围来修改模型数据,从而改变了视图。我不知道该怎么做。我看不出 $scope.pageNumber = 1 和 $scope.setPageNumber(1) 的区别。我错过了什么吗?
  • 对不起,我的意思是他们正在向指令控制器的范围添加一个函数。
  • 指令通过绑定到属性的本地范围属性与父范围通信。对于函数,使用&attr。在你的情况下,你正在走另一条路。视图模型中的值“驱动”视图的行为(除了用户输入)
  • 要回答您的其他问题,控制器与视图的耦合方式有多种。 1) Controller 期望 View 定义 example1Model.change 函数(controller 负责定义 ViewModel)。 2)控制器期望模型值的更改只能通过某些指令发生(即那些知道他们需要调用 .change 函数的指令)
  • @Scottie,我添加了您的 plunker 的修改版本,以说明控制器如何使指令对某些事件做出反应(例如刷新)
【解决方案2】:

我将同意@New Dev 并添加更多想法。如果您正在构建一个指令库,您不想同时捆绑库的使用者必须使用的控制器。您的指令应该或多或少是自包含的,并提供足够的 api 可扩展并可能在其他指令中使用。

这是什么意思?您的指令可能想要定义一个控制器,以便可以将它们注入到其他指令中。例如

//your library
directiveModule.directive("example1Directive", function() {
  return {
    controller: function($scope, $element, $attrs) {
     ...
  },
    ...
  }
});

-

//application
app.directive("appDirective", function() {
  return {
    require: '?example1Directive',
    link: function(scope, element, attrs, example1Directive) {
     ...
  }
});

您可能还想指定可以使用参数在指令上设置的各种选项,例如

<div example1-directive="{opt1: 'val', opt2: scopeProp}"></div>

然后,您的指令需要解析属性并在作用域上执行以生成选项。你可以做的还有很多,我建议看看 ngmodules.org 看看其他人在做什么。

【讨论】:

    最近更新 更多