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

3

我正在构建一个巨大的表单,调用各种指令来构建完整的表单。调用表单构建器的主页面像这样传递ng-model数据:

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

然后表单生成器页面调用各种子指令来构建表单的各个部分:

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,我在访问父子控制器的双向绑定时遇到了各种问题。

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!!!
        }
    }
}

无论我尝试什么,都遇到了阻碍。我尝试过以下方法:
  1. 隔离作用域:我尝试将formData.formFieldsformData.photoFields作为隔离作用域传递给子指令,但由于嵌套的隔离作用域,我最终会收到$ compile: MultiDir错误,因此这是不可能的。
  2. 如果我没有为每个表单部分设置单独的指令,并将它们全部放在formBuilder指令下,则会变成一个庞大的指令。以上只是一个草图,但每个子指令最终构建1个大型表单并将它们组合在一起。因此,合并它们确实是最后的手段,因为它很难维护且难以阅读。
  3. 我认为除了从目前看到的情况来看,没有其他方法可以访问父指令的ControllerAs,从子指令的Controller中。
  4. 如果我在子指令模板的ng-model中使用父级的ControllerAs,例如<input type=“text” ng-model=“fb.formData.formFields.text" />,那么这样做就可以正常工作,但我需要从子指令的控制器中访问相同的内容进行一些处理,但我无法做到这一点。
  5. 如果我摆脱controllerAs并再次使用$scope,那么就像以前一样工作,但我正在尝试完全摆脱$scope,以准备未来的Angular更改。
由于这是一个高级表单,我需要有单独的指令来处理各种表单部分,而由于Angular 1.2不允许嵌套隔离作用域,因此这使得它变得更加困难,特别是当尝试使用ControllerAs摆脱$scope时。请问有人能指导我在这里的选择吗?感谢您阅读我的长篇文章。
1个回答

2
基本上,您需要使用指令的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选项,您可以在链接函数的第四个参数中获取该指令的控制器。 代码
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包含在链接函数第四个参数的作用域变量中。只有这样才能使用$scope访问该控制器(你不希望在那里写)。关于同样的问题,在Github上记录为开放状态,你可以在这里查看。


1
那是一个很棒的解决方案@pankajParkar。我会立即尝试。非常感谢,因为我卡在这个问题上已经三天了,一直在尝试找出解决方案!然而,我对link函数中的scope有一个问题。从我所读到的来看,Angular 2可能会摆脱$scope,所以由于在link()中使用了scope,以上代码是否会在Angular 2上失效?我知道,现在准备Angular 2可能还为时过早,但您对今天的传言有何看法? - Neel
1
@Neel,是的,你说得对。我明白你正在使用controllerAs模式来接近Angular2迁移,但我这边没有解决方案。我们需要在controllerlink函数中有作用域变量。在AngularJS问题列表中仍然存在未解决的bug:https://github.com/angular/angular.js/issues/5893 - Pankaj Parkar
感谢@PankajParkar的建议和提供链接的+1。我希望Angular 2能够找到一种优雅的方法来解决这个问题。但现在,你的解决方案会对我有所帮助。再次感谢。 :) - Neel
1
@Neel,Angular2在使用DI方面有更好的实现方式。如果你阅读我提供给你的链接,你会了解到很多东西。 - Pankaj Parkar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接