如何使用不同的 angular 控制器重复使用相同的 ui-grid

How to re-use same ui-grid with different angular controllers

我是 Angular 1.6 和 UI-grid 的新手,所以我正在努力解决一些简单的问题。我想在我的主控制器中定义一个 UI-grid,它被重新定义并重新使用来自不同 Angular 控制器的数据。更具体地说,我想在主 Angular 控制器中使用单个网格定义来拥有多个 .CSHTML 网格模板。我想用不同的数据和不同的列定义填充来自不同 Angular 控制器的网格数据数组。一次只会有一个活动网格。

为每个控制器使用单独的网格定义是可行的,但我最终可能有 20 个网格;所以我不想在我的主 Angular 控制器中有那么多可重复的膨胀。网格定义唯一改变的部分是列定义和数据。下面是一个简化的项目布局,以帮助讨论。

在我的主 Angular 控制器中,我有一个 UI-grid 定义,我想从不同文件中的多个 Angular 控制器重新使用它。网格定义如下:

//MainController in app.js
(function () {
    'use strict';

    angular.module('CRNApp', ['ui.bootstrap', 'ui.grid', 'ui.grid.autoResize', 'ui.grid.emptyBaseLayer', 'ui.grid.selection']);

    MainController.$inject = ['$scope', '$http', '$uibModal', '$log','uiGridConstants'];

    angular.module('CRNApp')
    .controller('MainController', MainController);

    $scope.policyList = {                //grid options and data for policy data
        rowHeight: 40,
        enableFiltering: true,
        enableRowSelection: true,
        enableRowHeaderSelection: false,
        data: 'policyData',
        columnDefs: [ //the column defs will be different for each different controller
            { name: 'PolicyId'},
            { name: 'PolicyState'},
            { name: 'AgentName' },
            { name: 'InforcePremium' },
            { name: 'LOB'}
        ]
    };
    $scope.policyData = [];              //stores policy array returned from MVC controller to UI
    $scope.policySelected = 'false';     //tracks if a row is selected. Allows row-depenendent nav buttons to be enabled on bav bar
    $scope.mySelectedPolicy = {};

    //grid options for processor grid
    $scope.policyList.multiSelect = false;
    $scope.policyList.modifierKeysToMultiSelect = false;
    $scope.policyList.noUnselect = false;
    $scope.policyList.onRegisterApi = function (policyApi) {
        $scope.policyApi = policyApi;
        $scope.policyApi.selection.on.rowSelectionChanged($scope, function (row) {
            if (row.isSelected) {
                $scope.policySelected = 'true';
                $scope.mySelectedPolicy = row.entity; //copy selected row so that we can access it from processor controller
            }
            else
                $scope.policySelected = 'false';
        });
    };

    //this function recalculates the height of the visible grid after the row count changes
    $scope.getPolicyTableHeight = function () {
        var rowHeight = 30; // your row height
        var headerHeight = 30; // your header height
        return {
            height: ($scope.policyList.data.length * rowHeight + headerHeight) + "px"
        };
    };

}());

在主 _Layout.cshtml 中,导航栏上有一个按钮绑定到名为 "PolicyController" 的单独 Angular 控制器中的一个功能。该函数的名称称为 "loadPoliciesGrid()"。该函数用数据填充 policyList 网格定义中的 policyData 数组(参见上面的主控制器)。我将在页面上有多个按钮,它们在不同的控制器中调用类似的功能。例如,我将有一个 Processor 按钮调用 ProcessorController Angular 文件中的 "LoadProcessors()" 函数。 "LoadProcessors()" 函数将在主控制器中重复使用相同的网格定义,但列定义和网格数组将不同。

<input style="margin-top: 8px !important; margin-bottom: 8px !important; margin-left: 8px !important;" type="submit" value="Get Records" ng-controller="PolicyController" ng-click="loadPoliciesGrid()" class="btn btn-default btn-sm">

"PolicyController"中加载网格数据的函数如下。将有其他 Angular 控制器调用自己的数据,并且主控制器中的网格定义应该以某种方式具有适当的列定义:

//PolicyController in policy.js
(function () {

angular.module('CRNApp');

PolicyController.$inject = ['$scope', '$http', '$uibModal', '$log'];

angular.module('CRNApp')
    .controller('PolicyController', PolicyController);

function PolicyController($scope, $http, $uibModal, $log) {

    $scope.loadPoliciesGrid = function () {
        $scope.$parent.loading = true;
        $http.get("/api/Policy")
            .then(function (responsePol) {
                $scope.$parent.policyData = responsePol.data;
                $scope.$parent.showGrid = 'policies';
                $scope.$parent.loading = false;
            })
            .catch(function (error) {
                console.log(error);
            });
    };

};
}());

网格模板绑定到 Index.cshtml 页面中的政策控制器,该页面作为子页面加载到主 _Layout.cshtml 页面中。这些模板中的每一个都应该将它们的数据绑定到相同的 UI-grid。:

@*Index.cshtml. Each template is bound to a different Angular controller*@
@{ViewBag.Title = "ICS Portal - Home Page";}
<div ng-include="'templates/policiesGrid.cshtml'" ng-controller="PolicyController"></div>
<div ng-include="'templates/processorGrid.cshtml'" ng-controller="ProcessorController"></div>
<div ng-include="'templates/usersGrid.cshtml'" ng-controller="UserController"></div>

网格标记容器在名为 policiesgrid.cshtml 的单独模板文件中定义。每个网格标记模板都在一个单独的文件中,但应该引用相同的 UI-网格定义:

<div style="margin-left: auto; margin-right: auto;">
    <div ui-if="policyList.data.length>0" ng-style="getPolicyTableHeight()"
     ui-grid-auto-resize ui-grid-empty-base-layer ui-grid-selection id="policyGrid"
     ui-grid="policyList" class="processorGrid" ng-show="showGrid == 'policies'"
     style="margin-left: auto; margin-right: auto;">
    </div>
</div>

您应该使用服务来获取您的数据,但无论如何,有一些方法可以实现您想要的(如 AngularJS 中的所有内容)。如果您不想重构您的代码,并且如果我理解正确的话,您需要控制器之间的基本通信。

最简单的方法是在控制器上使用网格设置观察器。

所以在设置了网格的控制器上执行如下操作:

$scope.$on('dataGridChangedMessage', (event, argumentsPassedToGrid) => {
    $scope.policyList.columnDefs = argumentsPassedToGrid.columnDefs;
    $scope.policyList.data = argumentsPassedToGrid.data;
    // you might neeed here to refresh the grid using API, something like: policyApi.grid.refresh();
    // or try to trigger digest cycle manually if it doesn't work with: $timeout($scope.$apply()); 
});

并且在您的其他控制器上,您将需要注入 $rootScope 并广播您的 data/columnDefs - 当您的 $http 承诺得到解决时:

$rootScope.$broadcast('dataGridChangedMessage', {
    columnDefs: [],  // this is your new column defs
    data: [] // this is your new data
};

所以你会 ui 显示数据的网格控制器和发送类似数据的控制器:

控制器 1 => 控制器 UI 网格 <= 控制器 2

尽管上面的方法可行,但更好的方法是:

  • 创建一个全新的服务,

  • 在控制器中注入服务UI GRID,

  • 删除那些控制器(在我的示例中为 controller1 和 controller2)

  • return 来自服务的 $http 承诺 - 或者甚至更好地设置 $resource 并将其用于不同的端点。

  • 从服务中获取不同的数据(在 UI GRID 控制器中)并且一旦承诺 已解决,您只需刷新网格数据(类似于上面的示例)

  • 所以只使用一个控制器和一项服务 - 不需要做 $broadcasts

如果您担心重复,您可以设置通用组件来显示您的网格数据,在服务和通用函数上设置 $resource - 这将采用两个或您可以在模板中设置的任意数量的参数。如果你正在使用组件(你应该使用)/指令,就像这样:

<my-ui-grid-generic-component url-key="some-name-1" url-params="{something: whatever}"></my-ui-grid-generic-component>

并且在服务中,您将使用键定义对象 - 像这样的端点:

let uigridDataAPIs = {
  'some-name-1': 'http://google.com',
  'some-name-2': 'http://google.com',
};

在主控制器中注入该服务后,您只需要在通用 component/directive 控制器上调用服务 $resource 的函数 - 类似于:

MyInjectedService.getResource($scope.urlKey, $scope.urlParams).then(resolve => { // logic to update grid - similar to first example });

您将需要设置通用 $http 函数或 return对该服务承诺的 $resource。不要忘记在 component/directive.

上设置 urlKey 和 urlParams 绑定

此外,如果您想学习更高级的做事方式,请查看:

我强烈推荐这个:https://github.com/toddmotto/angularjs-styleguide

https://github.com/angular-redux/ng-redux

https://rxjs-dev.firebaseapp.com/

请记住,以上所有内容只是为您指明未来的正确方向,我没有测试我写的任何东西 - 大多数都是假设的,我在项目中设置它的方式 ui-grid's,所以你需要填补空白,一旦你进入下一步并卡住 - post 一个新问题,有人会帮助。