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

时间:2014-03-10 04:19:39

标签: angularjs

警告:相当长的问题

我是角色新手,我已经完成了一些教程和示例,例如angularjs网站上的official tutorial。将数据导入应用程序并在控制器之间共享数据的推荐方法似乎非常清楚。您创建一个由所有控制器共享的服务,该服务向服务器发出JSON格式数据的异步请求。

这在理论上是很好的,并且在仅显示一个控制器的非常简单的示例中,或者当控制器不共享依赖于共享数据的逻辑时,似乎很好。以下是基于年收入和税收的简单预算申请示例:

创建依赖ngResource的应用:

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

NetIncomeCtrl 控制器处理收入和税项,并计算净收入:

app.controller('NetIncomeCtrl', function ($scope, BudgetData) {
    var categoryTotal = function (category) {
        var total = 0;
        angular.forEach(category.transactions, function (transaction) {
            total += transaction.amount;
        });
        return total;
    };

    $scope.model = BudgetData.get(function (model) {
        $scope.totalIncome = categoryTotal(model.income);
        $scope.totalTaxes = categoryTotal(model.taxes);
    });

    $scope.netIncome = function () {
        return $scope.totalIncome - $scope.totalTaxes;
    };
});
使用$resource从服务器检索JSON数据的

BudgetData 服务:

app.factory('BudgetData', function ($resource) {
    return $resource('data/budget.json');
});

budget.json 包含从服务器返回的JSON数据:

{
    "income": {
        "transactions": [
            {
                "description": "Employment",
                "amount": 45000
            },
            {
                "description": "Investments",
                "amount": 5000
            }
        ]
    }, 
    "taxes": {
        "transactions": [
            {
                "description": "State",
                "amount": 5000
            },
            {
                "description": "Federal",
                "amount": 10000
            }
        ]
    },
}

然后在我的屏幕上,我有两个转发器显示收入和税项(您可以编辑),然后计算并显示净收入。

这很有用,是我在教程中看到的标准方法。但是,如果我只添加一个依赖于某些相同数据和逻辑的控制器,它就会开始解开:

ExpensesCtrl 控制器的费用最终会计算盈余(净收入 - 费用):

app.controller('ExpensesCtrl', function ($scope, BudgetData) {
    var categoryTotal = function (category) {
        var total = 0;
        angular.forEach(category.transactions, function (transaction) {
            total += transaction.amount;
        });
        return total;
    };

    $scope.model = BudgetData.get(function (model) {
        $scope.totalIncome = categoryTotal(model.income);
        $scope.totalTaxes = categoryTotal(model.taxes);
        $scope.totalExpenses = categoryTotal(model.expenses);
    });

    $scope.netIncome = function () {
        return $scope.totalIncome - $scope.totalTaxes;
    };

    $scope.surplus = function () {
        return $scope.netIncome() - $scope.totalExpenses;
    };
});

budget.json 会添加费用数据:

"expenses": {
    "transactions": [
        {
            "description": "Mortgage",
            "amount": 12000
        },
        {
            "description": "Car Payments",
            "amount": 3600
        }
    ]
}

然后在屏幕的另一部分,我有一个使用此控制器的部分,并使用转发器显示费用项目,然后重新显示净收入,最后显示产生的盈余。

此示例有效,但有几个问题(问题):

1)在这个例子中,我设法将大部分控制器的逻辑保留在回调函数之外,但所有控制器都在回调中启动,因为一切都取决于正在加载的模型。我知道这是javascript的本质,但angular应该减少对所有这些回调的需求,而这似乎并不干净。我能够从回调中获取一些逻辑的唯一原因是因为角度在引擎盖下用魔法对象取代模型直到模型被加载。由于这种“魔力”并不直观,因此很难判断您的代码是否能按预期工作。

人们是否有一致的方式处理这个问题?我真的不想要一些精心设计的解决方案,使这个101介绍应用程序变得非常复杂。是否有一种简单而标准的方法来重构此代码以避免如此多的回调并使代码更直观?

2)我有一堆重复的逻辑。 categoryTotalnetIncome逻辑应该只在一个地方,并在控制器之间共享。这些控制器用于完全独立的屏幕部分,因此它们不能使用范围继承。即使使用范围继承也可能存在问题,因为即使加载了自己的模型,子控制器的范围也不能依赖于父范围的加载模型。

categoryTotal逻辑只是一个不直接与模型绑定的辅助函数,因此我不知道将通用辅助函数放在角度中的位置。对于直接依赖于模型且需要在范围内的netIncome,可以将其添加到服务或模型中。但是,服务应该只关注从服务器检索模型,模型应该只包含数据,并尽可能地贫乏。而且,似乎这种逻辑属于控制器。有没有一种标准的方法可以解决这个问题?

3)每次调用服务来获取数据时,它都会向服务器发出http请求,即使每次数据都相同。应该在第一个请求之后缓存模型,因此只有一个请求。

我意识到我可以在服务中处理这个问题。我可以将模型存储为局部变量,然后在发出请求之前检查该变量以查看数据是否已存在。我读到的任何教程都没有提到这一点,这似乎很奇怪。另外,我正在寻找一种处理它的标准“角度”方式。

抱歉这篇令人难以置信的长篇文章。我真的想把这个应用程序保持在101介绍级别,如果可能的话,不要进入真正复杂的领域。我也希望有一种标准的“棱角分明”的方式来解决我尚未遇到的这些问题。

提前致谢!

1 个答案:

答案 0 :(得分:3)

我就是这样做的。您创建一个处理数据的服务,如果它被更改,它会向您的控制器广播消息。它将获得可以使用BudgetData.data获得的初始数据。如果有人更改数据

.service("BudgetData", function($http, $rootScope) {
  var this_ = this, data;

  $http.get('wikiArticles/categories', function(response) {
    this_.set(response.data);
  }

  this.get = function() {
    return data;
  }

  this.set = function(data_) {
    data = data_;
    $rootScope.$broadcast('event:data-change');
  }

});

在您的控制器中,您只需要监听事件,它将相应地更新您的范围变量。您可以在任意数量的控制器中使用它。

$rootScope.$on('event:data-change', function() {
  $scope.data = BudgetData.get();
}

$scope.update = function(d) {
  BudgetData.set(d);
}