如何组织和减少 AngularJS 控制器大小?

How to organize and reduce AngularJS controller size?

我正在构建我的第一个 AngularJS 应用程序,并且正在努力弄清楚如何将我的代码组织成 services/factories 和指令。我的控制器代码越来越长,我知道我一定是做错了什么。

最初我的应用程序有三个控制器:"BuildingTypeController"、"AddressController" 和 "LayoutController"。我使用 ng-controller 指令将每个控制器附加到我的每个字段集。但是我遇到了问题:

  1. 我需要将一个控制器的数据传递给另一个控制器,以便能够在 $scope.$watch 代码中使用。
  2. 当我需要在我的 Get...$http.get 方法中将一个控制器的数据传递给另一个控制器时(在下面的代码中替换为硬编码值)。

然后我回去把我所有的属性和函数都放在一个大的 "FormController" 中。这给了我正在寻找的功能,但我知道这不可能是组织所有这些代码的正确方法。

如果有人可以就如何将长控制器构造成更小的部分提供任何建议,我将不胜感激。谢谢!

编辑:此代码中的硬编码数据集是为了让 fiddle 工作而创建的——它们将被数据库调用取代,不应被纳入单个控制器的 "length" .

var app = angular.module('formBuilder', []);

app.controller('FormController', ['$http', '$scope', function ($http, $scope) {
    
    //This could really be thought of as "BuildingTypeController"
    var form = this;

    form.state = "";
    form.states = [];

    form.buildingType = "";
    form.buildingTypes = [];

    form.GetStates = function (buildingType) {
        //This will be replaced with $http.get later
        if (buildingType == null)
        {
          form.states = ["AZ","CT","NY"];   
        }
        if (buildingType == "Single Story") {
            form.states = ["AZ","CT"];
        }
        if (buildingType == "Mansion") {
            form.states = ["CT"];
        }
        if (buildingType == "Apartment") {
            form.states = ["CT","NY"];
        }
        if (buildingType == "Sky Scraper") {
            form.states = ["NY"];
        }
        
    };

    form.GetBuildingTypes = function (state) {
        //This will be replaced with $http.get later
        if (state == null)
        {
          form.buildingTypes = ["Single Story","Mansion","Apartment","Sky Scraper"];   
        }
        if (state == "AZ") {
            form.buildingTypes = ["Single Story"];
        }
        if (state == "CT") {
            form.buildingTypes = ["Single Story", "Mansion","Apartment"];
        }
        if (state == "NY") {
            form.buildingTypes = ["Sky Scraper", "Apartment"];
        }
    };

    //initializations
    form.GetStates();
    form.GetBuildingTypes();

    //This could really be thought of as "AddressController"
    form.addressId = "";
    form.addresses = [];

    form.GetAddresses = function (form) {
        //This will be replaced with $http.get later
        if (form.state == "AZ" && form.buildingType == "Single Story") {
            form.addresses = [{
                addressId: 1,
                description: "123 Grove Ave"
            },{
                addressId: 2,
                description: "2352 High Court"
            }];
        }
        if (form.state == "CT" && form.buildingType == "Single Story") {
            form.addresses = [{
                addressId: 3,
                description: "1515 Lark Ave"
            },{
                addressId: 4,
                description: "2 Front St"
            }];
        }
        if (form.state == "CT" && form.buildingType == "Mansion") {
            form.addresses = [{
                addressId: 5,
                description: "6 Waterfront Dr"
            }]
        }
        if (form.state == "CT" && form.buildingType == "Apartment") {
            form.addresses = [{
                addressId: 6,
                description: "13 Center St"
            },{
                addressId: 7,
                description: "5985 Elizabeth Court "
            }]
        }
        if (form.state == "NY" && form.buildingType == "Sky Scraper") {
            form.addresses = [{
                addressId: 8,
                description: "13245 12th Ave"
            },{
                addressId: 9,
                description: "345 Park Ave"
            }]
        }
        
        if (form.state == "NY" && form.buildingType == "Apartment") {
            form.addresses = [{
                addressId: 10,
                description: "6668 115th St"
            },{
                addressId: 11,
                description: "2839 3rd Ave"
            }]
        }

    };

    form.performAction = function (expr) {
        return function () {
            if (expr == "GetAddresses") {
                form.GetAddresses($scope.form);
            }
            if (expr == "GetLayouts") {
                form.GetLayouts($scope.form);
            }
        };
    };

    $scope.$watch('form.state', form.performAction('GetAddresses'));
    $scope.$watch('form.buildingType', form.performAction('GetAddresses'));

    
    //This could really be thought of as "LayoutController"
    form.layout = {};
    form.layouts = [];

    form.GetLayouts = function (form) {
        if (form.addressId == 1)
         form.layouts = ["A", "B", "C", "D"];
        if (form.addressId == 2)
         form.layouts = ["B", "C", "D"];
        if (form.addressId == 3)
         form.layouts = ["A", "D"];
        if (form.addressId == 4)
         form.layouts = ["A", "D"];
        if (form.addressId == 5)
         form.layouts = ["A"];
        if (form.addressId == 6)
         form.layouts = ["D"];
        if (form.addressId == 7)
         form.layouts = ["C", "D"];
        if (form.addressId == 8)
         form.layouts = ["A", "D"];
        if (form.addressId == 9)
         form.layouts = ["A", "B"];
        if (form.addressId == 10)
         form.layouts = ["B", "C", "D"];
        if (form.addressId == 11)
         form.layouts = ["C", "D"];
    };

    $scope.$watch('form.addressId', form.performAction('GetLayouts'));

}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="formBuilder">
    <form name="createForm" class="form-horizontal" ng-controller="FormController as form" novalidate>
        <fieldset>
            <legend>Building Type</legend>
            <select class="form-control" ng-model="form.state" ng-options="state for state in form.states" ng-change="form.GetBuildingTypes(form.state)">
                <option value="">Select a state</option>
            </select>
            <select class="form-control" ng-model="form.buildingType" ng-options="buildingType for buildingType in form.buildingTypes" ng-change="form.GetStates(form.buildingType)">
                <option value="">Select a building type</option>
            </select>
        </fieldset>
        
        <fieldset>
            <legend>Specific Address</legend>
            <select class="form-control" ng-model="form.addressId" ng-options="obj.addressId as obj.description for obj in form.addresses">
                <option value="">Select an address</option>
            </select>
        </fieldset>
        
        <fieldset>
            <legend>Select Room</legend>
            <select class="form-control" ng-model="form.layout" ng-options="layout for layout in form.layouts">
                <option value="">Select a room</option>
            </select>
        </fieldset>
    </form>
</div>

减少控制器中行数的一种方法是将一些硬编码数据集提取到静态 JavaScript 文件中。任何您认为在控制器中可能很重的可以抽象出来的东西都可能是一个需要改进的地方。

一个例子是创建一个全局数据变量,您可以在其中访问该页面上的变量。

(function(global){
  global.data = {
    names: ['Jane', 'John', 'Mary', 'Michael', 'Ryan', 'Rachel']
  };
})(this);

这是控制器中的用法示例。

(function(global){
  app.controller('MainController', function($log){
    this.message = "Hello, world!";
    //Usage of the global variable here.
    this.names = global.data.names;
    this.click = function(){
      global.handlers.onclick(this);
    };
  });
})(this);

下面是视图中的用法示例。

<!DOCTYPE html>
<html ng-app="TestModule">

  <head>
    <link rel="stylesheet" href="style.css">
    <!-- Reference to Angular on CDN -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.5/angular.min.js"></script>
    <!-- Reference to external data sets -->
    <script src="data.js"></script>
    <!-- Reference to external event handlers -->
    <script src="handlers.js"></script>
    <!-- Angular module init -->
    <script src="module.js"></script>
    <!-- Controller init -->
    <script src="maincontroller.js"></script>
  </head>

  <body>
    <div ng-controller="MainController as main">
      <h3>{{ main.message }}</h3>
      <div>
        <h2>Names from the global data.js file:</h2>
        <h3 ng-repeat="n in main.names">{{ n }}</h3>
      </div>
      <div>
        <input type="button" ng-click="main.click()" value="Click me!" />
      </div>
    </div>
  </body>

</html>

请参阅我的 plunker 以获取工作示例:

http://plnkr.co/edit/seCQiNpYwKbgjkngdCFz?p=preview

这是要做的三件基本事情:

  1. 在新文件中提取数据

  2. 使用服务让您的控制器不知道数据是如何传输的 检索到

  3. (不太基本)使用 UI 路由器将您的应用拆分为多个状态

这是一个excellent resource for going into more details

您可以使用任何装饰器、库或其他语言。例如,我编写了自己的库来优化 AngularJS 和 TypeScript 的工作。

https://github.com/aleksey-pastuhov/AngularJS-Typed-Core

P.S.: 示例现在不工作,但我会尽快修复它。