在控制器之间共享模型数据

Sharing model data between controllers

警告:详细描述我实际在做什么。

我有一个具有以下布局的单页应用程序:

如您所见,这是一个由选项卡组成的布局,每个选项卡都是一个指令,具有自己的控制器(和范围)。所有形式它本身就是一个指令,由小指令组成,包含在标签中,如下:

main.html

<div class="container-fluid">
  <div class="evaCont">
    <div class="eva_title">Form</div>
    <form id="mainForm" ng-submit="submit()">
      <div class="col-xs-12">
        <input type="submit" class="btn btn-primary" value="Save"/>
      </div>

      <div class="tab col-xs-12">
        <tabset justified="true">
          <tab heading="Data section 1">
            <ds1></ds1>
          </tab>
          <tab heading="Data section 2">
            <ds2></ds2>
          </tab>
          <tab heading="...">
            <ds3></ds3>
          </tab>
        </tabset>
      </div>
    </form>
  </div>
</div>

为了用数据填充 SPA,我正在调用 REST API 来获取它 (JSON)。为此,我提供了一项服务:

'use strict';


angular.module('frontendApp').factory('backendService', function($resource, $location){
    var objBackend = {};

    objBackend.query = function(id) {
        var data = $resource('http://localhost:9010/API/:dataID', {dataID: id}, {
            query: {
                method: 'GET',
                headers: {'token': '1588526404200542348177491835893601182229853'}
            }
        });

        var dataForFrontEnd = data.query().$promise.then(function(d){
            console.log(d);
            return d;
        },
        function(error){
            ...
        });

        return dataForFrontEnd;
    }

    return objBackend;
});

我有一个自定义版本的 $resource 查询方法,用于管理 REST 调用的成功和失败(以及发送自定义 'token' header)。 我的 AngularJS 应用程序需要 API 的数据,然后我将其设置在主控制器的范围内。 我这样做是因为,如果我注入服务并调用 query() 方法 Angular 将为每个指令调用 API n 次,返回完全相同的 object。相反,我在应用程序加载中只调用它一次,并在所有控制器(每个选项卡一个控制器)之间共享 JSON 数据。

主窗体控制器:

'use strict';

angular.module('frontendApp')
  .controller('MainCtrl', function ($scope, $routeParams, data, datepickerPopupConfig) {
    $scope.data = data;

    datepickerPopupConfig.showButtonBar = false;

    $scope.submit = function(){
      console.log($scope.data.val1);
    };
});

app.js:

'use strict';

angular
  .module('frontendApp', [
    'ngAnimate',
    'ngAria',
    'ngCookies',
    'ngMessages',
    'ngResource',
    'ngRoute',
    'ngSanitize',
    'ngTouch',
    'ui.bootstrap'
  ])
  .config(function ($routeProvider) {
    $routeProvider
      .when('/API/:dataID', {
        templateUrl: 'main/directives/main.html',
        controller: 'MainCtrl',
        resolve: {
          data: function(backendService, $route){
            var id = $route.current.params.dataID;
            return backendService.query(id);
          }
        }
      })
      .otherwise({
        redirectTo: '/notfound'
      });
  });

指令 1(显示我如何根据 parent 范围绑定数据):

JS(指令):

'use strict';

angular.module('frontendApp')
.directive('ds1', function($http){
    return {
        restrict: 'E',
        replace: true,
        scope: true,
        templateUrl: 'basic/directives/basic.html',
        controller: 'ds1'
    };
});

JS(控制器):

'use strict';


angular.module('frontendApp')
  .controller('ds1', function ($scope) {
    var data = $scope.data;

    $scope.val1 = data.val1;
    $scope.val2 = data.val2;
    $scope.val3 = data.val3;

    //Datepicker
    $scope.dpOpened = {
      val1: false,
      val2: false,
      val3: false
    };

    $scope.open = function($event, opened) {
      $event.preventDefault();
      $event.stopPropagation();
      $scope.dpOpened[opened] = true;
    };

    $scope.dateOptions = {
      formatYear: 'yy',
      startingDay: 1
    };

    $scope.format = 'yyyy-MM-dd';
    //Datepicker
});

HTML:

<div class="container-fluid">
    <h4>Data section 1</h4>
    <div class="form-group">
        <div class="col-xs-6 col-sm-3">
            <label for="value1">Value 1/label>
            <select id="value1" class="form-control" ng-model="val1" ng-required="true">
                <option value="1">X</option>
                <option value="2">Y</option>
                <option value="3">Z</option>
            </select>
        </div>

        <div class="col-xs-6 col-sm-3">
            <label for="value2">Val 2</label>
            <select id="value2" class="form-control" ng-model="val2" ng-required="true">
                <option value="A">A</option>
                <option value="B">B</option>
            </select>
        </div>

        <div class="col-xs-6 col-sm-3">
            <label for="value3">Value 3</label>
            <input id="value3" type="text" ng-model="val3" ng-required="true" disabled class="form-control"/>
        </div>
    </div>
</div>

问题(和问题): 当我单击提交按钮发送表单数据时 AngularJS 无法识别两个更改的值。我认为我的问题是范围。当我将 main object 的值复制到指令范围的值时,我看不到用户在表单中所做的更改。

问题:根据我的布局和描述,什么是在所有指令之间共享数据并获取更改的正确方法,知道我正在使用 ng-model?

请记住,我没有注入我的服务,因为我不想每次为每个指令调用我的 API,而是我想要一个 API-Call 并返回数据在 parent 控制器中,但在 child 控制器中共享它。

提前致谢

您应该在服务中拥有状态,并且您应该使用 $watch 从指令中关注状态。

这是一篇关于它的简短文章:https://coderwall.com/p/dhgljg/angularjs-watch-for-changes-in-a-service

更新: 如果您想要一个性能更高的解决方案,您可以为您的服务提供一个回调,它会在数据更改时调用该回调。 $watch 的性能不是很好,所以你应该谨慎使用它。参见:AngularJS : How to watch service variables?

主控制器看不到更改,因为您正在复制指令中的数据,例如

var data = scope.data;
$scope.val1 = data.val1;

这有效地破坏了主控制器数据和指令数据之间的关系,因此更新不起作用。在您的指令中直接使用 $scope.data.val1 例如:

angular.module('frontendApp')
  .controller('ds1', function ($scope) {
    //Datepicker
    $scope.dpOpened = {
      $scope.data.val1: false,
      $scope.data.val2: false,
      $scope.data.val3: false
    };

    // ..... removed for brevity 
});

然后在您的指令模板中:

<select id="value1" class="form-control" ng-model="data.val1" ng-required="true">

但是,这种共享父作用域的方法存在一个问题,它基本上将您的子指令与父指令紧密耦合 controller/directive。实现双向数据绑定并使您的指令不与父控制器耦合的推荐方法是使用隔离范围以及 = 双向绑定功能。作为一个高级示例,使用隔离范围,您的指令将如下所示:

angular.module('frontendApp').directive('ds1', function($http){
    return {
        restrict: 'E',
        replace: true,
        scope: {
            data: '=data'
        },
        templateUrl: 'basic/directives/basic.html',
        controller: 'ds1'
    };
});

现在您可以通过双向隔离作用域绑定将主控制器的数据传递给您的指令:

<ds1 data='data'></ds1>

这样你的指令是完全自包含的,它不依赖于任何父范围值或属性,并且可以重复使用。这里有一些关于独立作用域的更多信息:

https://docs.angularjs.org/guide/directive

https://docs.angularjs.org/api/ng/service/$compile