【问题标题】:What can controllers do that directives can't? [duplicate]控制器能做哪些指令不能做的事情? [复制]
【发布时间】:2018-08-24 23:51:22
【问题描述】:

我刚刚开始使用 AngularJS。当我第一次开始阅读它时,从初学者教程看来,控制器是 Angular 应用程序的基本构建块。然而,自从学习指令以来,我一直在创建自己的小 Angular 应用程序,只有指令,实际上不是一个控制器。我不明白为什么我需要一个控制器。

我见过的唯一使用控制器完成的事情就是将变量添加到范围:

angular.controller("myController",
    function($scope)       
    {
        $scope.x = 5;
        $scope.y = 6;
    }
)

但我也可以使用指令来做到这一点,方法是使用传递给链接函数的 scope 参数。

还有什么可以用控制器做的,而不能用指令做的吗?或者至少是控制器比指令更容易做的事情?

例如,如果我只需要使用一些变量 xy 填充范围,我可以这样做:

angular.directive(
    "myDirective",
    function()
    {
        return {
            link: function(scope, element, attributes)
            {
                scope.x = 5;
                scope.y = 6;
            }
        };
    }
);

【问题讨论】:

  • 如果你的应用没有一个控制器……你会做什么?您可以使用 angular 指令等仅在 HTML 中构建前端,但如果您希望它任何复杂的事情,您需要编写实际的 Javascript 代码,将其放入控制器中……
  • 注意:在 stackoverflow 上有 multiple questions 关于控制器与指令的内容,但没有一个特别要求提供可以使用控制器而不是指令来完成的示例。我没有发现任何这些问题的答案有帮助。
  • @deceze 你能举个例子,代码不能只放在指令的链接函数中吗?
  • 可以使用指令构建整个应用程序,但这并不意味着您应该这样做。如果您愿意,您也可以乘坐直升飞机前往杂货店,但还有更好的方法。如果你只有一把锤子……
  • 如果你所做的只是在作用域上初始化变量……那么是的……区别有点学术。开始将控制器编写为类,通过依赖注入使用服务,您可能会开始看到不同之处。简而言之,控制器是一个持久的对象,它与特定的视图/模板并存,并以某种方式绑定到它。 link 只是一个一次性回调函数。它们可以部分用于相同的目的,但它们的生命周期却大不相同。完整的解释可能会很长……

标签: javascript angularjs


【解决方案1】:

当然,您可以在link 回调中编写应用所需的几乎所有内容。请注意,我什至没有将其称为指令,我说的是 link 回调。指令是定义自定义 HTML 标记及其相关功能的东西,link 回调只是其中的特定部分。

问题在于,这只不过是使用 jQuery,或者使用 addEventListener 将行为附加到 HTML 元素。另一方面,您可以将控制器编写为类,而不是操作scope 对象的过程代码。这是我在 中写 的首选风格:

export default class WidgetController {
    error: string;

    static $inject = ['$state', 'FooService', 'BarService'];

    constructor(
        protected $state: angular.ui.IStateService,
        protected foo: FooService,
        protected bar: BarService
    ) {}

    get fooValue() {
        return this.foo.baz;
    }

    doSomething() {
        this.error = null;
        this.bar.getSomething().then(data => {
            if (data.error) {
                this.error = data.error;
            } else {
                this.$state.go('success');
            }
        });
    }
}

此模板可能如下所示:

<h1>{{ $ctrl.fooValue }}</h1>
<button ng-click="$ctrl.doSomething()">Do!</button>
<p>{{ $ctrl.error }}</p>

控制器可以使用 ui-router 附加到模板:

import WidgetController from 'widget-controller';

module.config(['$stateProvider', ($state: angular.ui.IStateProvider) => {
    $state.state('widget', {
        controller: WidgetController,
        controllerAs: '$ctrl',
        templateUrl: '/templates/widget.html',
    });
}]);

或者作为一个组件:

module.component('widget', {
    templateUrl: '/templates/widget.html',
    controller: WidgetController,
    bindings: {
        error: '@'
    }
});

或使用ng-controller 或其他多种方式。

它为您提供更大的灵活性。它允许您很容易地单独测试控制器,因为它只是一个常规类。它允许您为不同的模板重用控制器,并为不同的控制器重用相同的模板(是的,这实际上非常有用)。它的 IMO 更具可读性和更易于理解。特别是在模板中使用$ctrl. 可以防止您构建过于相互依赖的嵌套范围,并显式绑定模板以仅使用其控制器,而不是使用某些隐式范围。

有很多方法可以做事,但随着时间的推移,我发现一件事是处理scope 对象既冗长又烦人,并且很容易导致意大利面条式代码。因此,远离这一点,您很快就会将控制器作为对象。

【讨论】:

  • 但是使用$ctrl 和只使用隔离范围的指令有什么区别?
  • 不多。整个事情的主要问题是您在某种程度上混淆了几件事。 directive 是模板可以绑定到某种控制器的方式之一。它的一个稍微调整的变体是一个组件。 ui-router 状态与做同一件事的方式并无太大区别。比较实际上是您是否应该通过将属性附加到scope 来在link 回调中执行操作,或者您是否应该将内容编写为类/可实例化对象。 (续)
  • 再一次,可能在一次性回调中修改 scope 以获得与控制器基本相同的结果,这根本不是它的意思. link 函数应该是一个接近 HTML 裸机的低级函数,在 HTML 元素等上设置事件处理程序。控制器应该从那些低级细节中抽象出来。很难说明为什么这种区别真的很重要,除非你在一个非常复杂的应用程序中开枪打死了自己,你已经把区别搞砸了,对你不利。
  • 我可能会修改这个答案,以更多地讨论将控制器绑定到视图的方式与使用 link 回调而不是控制器之间的混淆。诚然,这个答案目前集中在错误的事情上。
  • 这可能是题外话,但困扰我的另一件事是,使用指令,您可以通过属性传递数据将它们与周围的应用程序隔离开来。你能用控制器做类似的事情吗?据我所知,从外部将数据获取到控制器的唯一方法是让控制器构造函数从 $scope 服务中获取它,但这意味着控制器必须知道外部变量的名称 - 与指令和属性不同绑定。
猜你喜欢
  • 2017-07-13
  • 1970-01-01
  • 2017-03-05
  • 2011-04-12
  • 2019-09-11
  • 2019-11-08
  • 1970-01-01
  • 2013-01-21
  • 2010-09-25
相关资源
最近更新 更多