3

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

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

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

FormBuilder.html:

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

在控制器中使用$scope时,我可以毫无问题地访问$scopein 子指令,如下所示:

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

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

  1. 隔离范围:我尝试将formData.formFieldsformData.photoFields作为隔离范围传递给子指令,但是由于嵌套的隔离范围,我最终得到了$compile: MultiDir错误,所以这是不可能的。
  2. 如果我没有为每个表单部分设置单独的指令,并且将它们全部放在指令下的 1 个formBuilder指令中,那么它就会变成一个庞大的指令。以上只是一个草图,但每个子指令最终构建了一个大表格。因此,将它们合并在一起确实是最后的手段,因为它确实变得难以维护且不可读。
  3. 到目前为止,我认为没有任何其他方式可以 ControllerAs从子指令访问父指令。Controller
  4. 如果我在子指令模板的 ng-model like 中使用父级的 ControllerAs <input type=“text” ng-model=“fb.formData.formFields.text" />,那效果很好,但是我需要从子指令的控制器中访问它以进行一些我无法执行的处理。
  5. 如果我摆脱 controllerAs$scope再次使用,它就像以前一样工作,但我试图$scope完全摆脱它,为未来的 Angular 更改做好准备。

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

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

4

1 回答 1

2

基本上你需要使用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来自链接函数第四个参数的范围变量。然后只有您可以使用$scope(您不希望在那里)访问该控制器。关于在Github 中以 open status登录的相同问题,您可以在此处查看。

于 2015-12-06T11:08:09.343 回答