使用 ControllerAs 时出现 Angular 嵌套指令的问题

Posted

技术标签:

【中文标题】使用 ControllerAs 时出现 Angular 嵌套指令的问题【英文标题】:Trouble with Angular Nested Directives when using ControllerAs 【发布时间】:2016-03-11 01:20:57 【问题描述】:

我正在构建一个巨大的表单,它调用各种指令来构建一个完整的表单。调用 Form Builder 的 Main Page 传递 ng-model 数据,如下所示:

<div form-builder form-data=“formData”></div>

然后Form Builder Page调用各种子指令来构建Form的各个部分:

FormBuilder.html:

<div form-fields></div>
<div photo-fields></div>
<div video-fields></div>
 .. etc.. etc...

当在控制器中使用$scope 时,我在子指令中访问$scope 没有问题,如下所示:

function formBuilder() 
    return 
         restrict: 'A',
         replace: true,
         scope: 
            formData: '='
         ,
         templateUrl: 'FormBuilder.html',
         controller: function($scope) 
            $scope.formSubmit = function() 
            // Submits the formData.formFields and formData.photoFields
            // to the server
            // The data for these objects are created through 
            // the child directives below
         
     
   


function formFields() 
    return 
            restrict: 'A',
            replace: true,
            templateUrl: 'FormFields.html',
            controller: function($scope) 
               console.log($scope.formData.formFields);
            
    


function photoFields() 
    return 
            restrict: 'A',
            replace: true,
            templateUrl: 'PhotoFields.html',
            controller: function($scope) 
               console.log($scope.formData.photoFields);
            
   

... etc..

但是自从我摆脱了$scope 并开始使用ControllerAs 之后,我在访问与父子控制器的 2 路绑定时遇到了各种麻烦。

function formBuilder() 
    return 
         restrict: 'A',
         replace: true,
         scope: 
           formData: '='
         ,
         templateUrl: 'FormBuilder.html',
         controller: function() 
              var vm = this;
             console.log(vm.formData);  // Its fine here

             vm.formSubmit = function() 
               // I cannot change formData.formFields and formData.photoFields 
               // from Child Directive "Controllers"
            
        ,
        controllerAs: ‘fb’,
        bindToController: true
   


function formFields() 
    return 
            restrict: 'A',
            replace: true,
            templateUrl: 'FormFields.html',
            controller: function() 
                var vm = this;
                console.log(vm.formData.formFields); 
                // No way to access 2 way binding with this Object!!!
            
   


function photoFields() 
    return 
        restrict: 'A',
        replace: true,
        templateUrl: 'PhotoFields.html',
        controller: function() 
            var vm = this;
            console.log(vm.formData.photoFields); 
            // No way to access 2 way binding with this Object!!!
        
    

无论我尝试什么,我都会遇到障碍。我尝试过的事情是:

    隔离范围:我尝试通过 formData.formFieldsformData.photoFields 作为子指令的隔离范围, 但我最终得到$compile: MultiDir错误,因为 嵌套的隔离范围,所以这是不可能的。 如果我没有 每个表单部分的单独指令,并将它们全部包含在 formBuilder 指令下的 1 个指令,然后它变成一个 宏大的指令。以上只是一个草图,但每个孩子 指令最终构建了1个大表格。所以合并它们 在一起真的是最后的手段,因为它确实变得很难 维护和不可读。 我认为没有办法访问 父指令的 ControllerAs 来自子指令的 Controller 任何其他方式 从我目前所见。 如果我使用父级的 ControllerAs 在 子指令模板的 ng-model 像 &lt;input type=“text” ng-model=“fb.formData.formFields.text" /&gt;,效果很好,但我 需要从 Child 指令的控制器访问相同的 一些我无法做的处理。 如果我摆脱 controllerAs 并再次使用$scope,它像以前一样工作,但我是 试图完全摆脱$scope 为自己做好准备 未来的 Angular 变化。

由于它是一种高级表单,我需要有单独的指令来处理各种表单部分,并且由于自 Angular 1.2 以来不允许嵌套的隔离范围,这使得它变得更加困难,尤其是在尝试摆脱 $scope 使用ControllerAs.

有人可以指导我在这里有哪些选择吗?感谢您阅读我的长文。

【问题讨论】:

【参考方案1】:

基本上你需要使用指令的require 选项(require 选项用于指令与指令的通信)。只需在子指令中提及require 选项,即可访问其父控制器。您还需要使用bindToController: true,它基本上会将隔离范围数据添加到指令控制器。

代码

function formBuilder() 
    return 
         restrict: 'A',
         replace: true,
         bindToController: true, 
         scope: 
            formData: '='
         ,
         templateUrl: 'FormBuilder.html',
         controller: function($scope) 
            $scope.formSubmit = function() 
            // Submits the formData.formFields and formData.photoFields
            // to the server
            // The data for these objects are created through 
            // the child directives below
         
     
   

然后您需要在子指令中添加require 选项。基本上require 选项将具有formBuilder 指令和^(表示formBuilder 将存在于父元素中),如require: '^formBuilder',

通过编写require 选项,您可以在链接函数的第 4 个参数中获取该指令的控制器。

代码

function formFields() 
    return 
        restrict: 'A',
        replace: true,
        require: '^formBuilder',
        templateUrl: 'FormFields.html',
        //4th parameter is formBuilder controller
        link: function(scope, element, attrs, formBuilderCtrl)
            scope.formBuilderCtrl = formBuilderCtrl;
        ,
        controller: function($scope, $timeout) 
            var vm = this;
            //getting the `formData` from `formBuilderCtrl` object
            //added timeout here to run code after link function, means after next digest
            $timeout(function()
                console.log($scope.formBuilderCtrl.formData.formFields);
            )
        
    


function photoFields() 
    return 
        restrict: 'A',
        replace: true,
        require: '^formBuilder',
        templateUrl: 'PhotoFields.html',
        //4th parameter is formBuilder controller
        link: function(scope, element, attrs, formBuilderCtrl) 
            scope.formBuilderCtrl = formBuilderCtrl;
        ,
        controller: function($scope, $timeout) 
            var vm = this;
            console.log(vm.formData.photoFields);
            //to run the code in next digest cycle, after link function gets called.
            $timeout(function()
                console.log($scope.formBuilderCtrl.formData.formFields);
            )
        
    

编辑

上述解决方案的一个问题是,为了在指令控制器中访问父指令的控制器,我做了一些棘手的事情。第一个包括 formBuilderCtrl 到来自链接函数第 4 个参数的范围变量。然后只有您可以使用$scope(您不希望在那里)访问该控制器。关于Github with open status 中记录的相同问题,您可以在此处查看。

【讨论】:

这是一个很棒的解决方案@pankajParkar。我会马上试试。非常感谢您,因为我被困在这 3 天试图找出解决方案!但是,我对link 函数中的scope 有疑问。从我读到的内容来看,Angular 2 可能会摆脱$scope,那么由于scope 用于link(),所以上面的Angular 2 会中断吗?我知道,现在为 Angular 2 做准备可能还为时过早,但您对这件事的看法是否已成为今天的谣言? @Neel 是的,你是对的。我知道你正在使用controllerAs 模式接近 angular2 迁移。但我没有解决方案。我们需要在controllerlink 函数中有范围变量。 AngularJS 问题列表github.com/angular/angular.js/issues/5893 上仍有未解决的错误 感谢您的意见@PankajParkar 和 +1 的链接。我希望 Angular 2 能提供一种优雅的方式来做到这一点。但是现在,您的解决方案将对我有所帮助。再次感谢。 :) @Neel angular2 使用 DI 对此有更好的实现。如果你阅读我提供给你的链接。你会知道很多事情。

以上是关于使用 ControllerAs 时出现 Angular 嵌套指令的问题的主要内容,如果未能解决你的问题,请参考以下文章

在 Angular 1.5 组件中使用 ControllerAs

使用 controllerAs 语法时将当前范围传递给 modalInstance

Angular - 带有 controllerAs、bindToController 和 $scope.$watch 的指令

指令未使用 controllerAs 和 bindToController 使用异步数据更新视图

在ControllerAs AngularJS中调用子函数

带有 ControllerAs 和 TypeScript 类的 AngularJs 指令