【问题标题】:How propagate form submit event to parent of custom directive?如何将表单提交事件传播到自定义指令的父级?
【发布时间】:2015-06-14 04:01:09
【问题描述】:

我有一个简单的 AngularJS 项目,我从用户那里获取输入数据并根据该数据生成图表。我试图弄清楚如何组织代码以使其符合MVC design pattern。特别是,我一直在弄清楚如何将表单的提交事件传播到自定义指令的父级。我正在寻找某种回调机制。

似乎有几个选项,但我还没有让它们中的任何一个工作。我考虑过使用custom directivesui-Router 和服务(根据AngularJS: How can I pass variables between controllers?

到目前为止,我一直在尝试使自定义指令的方法起作用。我有一个自定义指令<input-form>,它是一个表单,提交时应将其输入传递给另一个自定义指令<index-chart>。我有三个控制器:一个用于主应用程序NavigationController,一个用于输入InputController,它与指令<input-form> 相关联,一个用于输出OutputController,它与<index-chart> 指令相关。

我认为NavigationController 应该知道如何从InputController 中提取输入数据并将其传递给OutputControllerInputControllerOutputController 应该保持不可知,以便可以重复使用。

我想我已经弄清楚了除了流量控制之外的一切。 <input-form> 包含 <form ... ng-submit,因此即使我希望它保持不可知,它也负责触发响应用户提交的输入的操作。然而,该操作的代码应该在 OutputController 中,InputController 不应该知道。

如何使NavigationController 响应包含在控制器为InputController 的自定义指令<input-form> 中的提交事件?然后NavigationController 如何从InputController 的实例中提取数据并调用包含在OutputController 中应该呈现图表的代码(即下面代码中的renderChart())?

下面的代码也在 Plunker 上:http://plnkr.co/edit/wm4suXMcSUE6obYFk3hp?p=preview index.html

<html ng-app="a3d">
    <div ng-controller="NavigationController as navCtrl">
        <input-form ng-show="navCtrl.shouldShowInputForm()"></input-form>
        <index-chart ng-show="navCtrl.shouldShowOutputChart()"></index-chart>
    </div>
</html>

a3j.js

(function(){
    var app = angular.module('a3d', ['input-form', 'index-chart']);

    app.controller('NavigationController', function(){
        this.inputMode = true;

        this.shouldShowInputForm = function(){
            return this.inputMode;
        };

        this.shouldShowOutputChart = function(){
            return !this.inputMode;
        };

        this.flipMode = function(){
            this.inputMode = !this.inputMode;
        }
    });
})();

input-form.html

<form name="inputForm" ng-controller="InputController as inputCtrl"
                          ng-submit="inputForm.$valid && ???" novalidate>
    <textarea name="topic1Data" ng-model="inputCtrl.inputValues.topic1Data" rows="10" cols="30" required></textarea>
    <button type="submit" class="btn btn-info btn-lg" ng-disabled="!inputForm.$valid">Compare</button>
</form>

inputForm.js

(function(){
    var app = angular.module('input-form', [ ]);

    app.directive('inputForm', function(){
        return {
            restrict: 'E',
            templateUrl: 'input-form.html',

        };
    });

    app.controller('InputController', ['$window', '$log', function($window, $log, appData){
    // ...
    }]);

index-chart.html

<!-- I haven't really gotten to this part yet -->
<div id="indexchart" style="min-width: 310px; max-width: 800px; height: 900px; margin: 0 auto"></div>

indexChart.js

(function(){
    var app = angular.module('index-chart', [ ]);

    app.directive('indexChart', function(){
        return {
            restrict: 'E',
            templateUrl: 'index-chart.html'
        };
    });

    app.controller('OutputController', ['$window', '$log', function($window, $log, appData){
        this.renderChart = function(){

            // This is where the chart should get rendered

        };
    }]);    
})();

【问题讨论】:

  • 抱歉,要处理的内容很多。 :-( Plunker 或 Fiddle 的任何机会?
  • 我正在寻找解决方案,但这是一个很好的建议,谢谢@camden_kid。我不认为我写的代码那么重要,它更多的是关于 AngularJS 希望我们如何进行事件传播的问题。
  • 我认为最好的方法是在指令上使用ng-model,并在表单控制器上使用观察者$scope.$watch('inputModel',fn...
  • @camden_kid 我确实将它放入 Plunker:plnkr.co/edit/wm4suXMcSUE6obYFk3hp?p=preview
  • 感谢@Matho,但可以在指令上使用 ng-model 吗? stackoverflow.com/questions/14115701/…的解决方案不直接使用ng-model;它使用隔离范围并定义属性myDirectiveVar。但是我不知道如何将这个想法应用于我的自定义输入指令(我称之为inputForm)。隔离范围方法对输出指令(我称之为indexChart)是有意义的。

标签: angularjs angularjs-directive


【解决方案1】:

这是一种方法 - Plunker

a3j.js

app.controller('NavigationController', function(){
  var navCtrl = this;
  navCtrl.data = null;
});

index.html

<div ng-controller="NavigationController as navCtrl">
    <input-form data="navCtrl.data"></input-form>
    <index-chart data="navCtrl.data"></index-chart>
</div>

inputForm.js

inputForm.directive('inputForm', function() {
    return {
      restrict: 'E',
      templateUrl: 'input-form.html',
      scope: {data: "="},
      controllerAs: 'inputCtrl',
      bindToController: true,
      controller: function() {
        var inputCtrl = this;
        inputCtrl.inputValues = {topic1Data: 123456789};

        inputCtrl.emitData = function() {
          inputCtrl.data =  inputCtrl.inputValues.topic1Data;
        };
      }
    };
});

input-form.html

<form name="inputForm" ng-submit="inputForm.$valid && inputCtrl.emitData()" novalidate>
  <textarea name="topic1Data" ng-model="inputCtrl.inputValues.topic1Data" rows="10" cols="30" required></textarea>
  <button type="submit" class="btn btn-info btn-lg" ng-disabled="!inputForm.$valid">Compare</button>
</form>

indexChart.js

  indexChart.directive('indexChart', function() {
    return {
      restrict: 'E',
      templateUrl: 'index-chart.html',
      scope: {data: "="},
      controllerAs: 'chartCtrl',
      bindToController: true,
      controller: ['$scope', function($scope) {
        var chartCtrl = this;

        $scope.$watch('chartCtrl.data', function(newValue) {
          if (angular.isDefined(newValue)) {
            console.log(newValue);
          }
        });
      }]
    };
  });

index-chart.html

{{chartCtrl.data}}

需要注意的要点有:

  • 每个指令都有一个带有 data 属性的隔离范围
  • NavigationController 将相同的值传递给这些指令
  • 可以观察值的任何变化并采取行动
  • 每个指令都是独立的,无需单独的控制器
  • 每个指令彼此独立运行

【讨论】:

  • 谢谢@camden_kid!您的解决方案确实有效:-) 我确实在 StackOverflow 上发布了一个后续问题作为新条目:stackoverflow.com/questions/29566078/…
  • @MichaelOsofsky 我可以让它与 ng-controller 一起工作,但我更愿意向您展示它是如何应该完成的。 :-)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-12
  • 1970-01-01
  • 1970-01-01
  • 2015-07-13
  • 1970-01-01
  • 2021-07-11
相关资源
最近更新 更多