【问题标题】:AngularJS - DRY two-way data-binding using controllerAs syntax and service propertiesAngularJS - 使用 controllerAs 语法和服务属性的 DRY 双向数据绑定
【发布时间】:2015-06-18 10:14:04
【问题描述】:

我偶然发现了一个应该很常见且显而易见的问题,但我似乎无法理解它。

我正在开发一个小型原型应用程序。我的后端开发人员在 JSON 对象中为我提供了配置文件数据。比方说,它看起来像这样:

profile = {Name: 'John', Email: 'john@mail.com', DOB: '1980-11-03'}

我在多个位置都需要这些值,而且我也不想将后端 http 调用放在控制器中,所以我创建了一个服务来处理这个:

angular.module('app', [])
.service('ProfileService', ['$http', function ($http) {
    var service = this;

    service.Name = null;
    service.Email = null;
    service.DOB = null;

    service.getProfile = function () {
        return $http.get('/profile').then(function (response) {
                service.Name = response.data.Name;
                service.Email = response.data.Email;
                service.DOB = response.data.DOB;
                return true;
            });
    };

    return service;
}])
.controller('ProfileCtr', ['ProfileService', function (service) {
    var vm = this;

    service.getProfile().then(function () {
        vm.Name = service.Name;
        vm.Email = service.Email;
        vm.DOB = service.DOB;
    });
}]);

这个解决方案有很多问题:

  1. 由于配置文件数据由原语组成,直接绑定到服务属性不会自动同步数据。
  2. 更重要的是,它打破了 DRY 概念,因为我已经在至少 3 个不同的地方(数据库架构、getProfile() 和控制器中)编写了数据声明。

一种解决方案是添加一个间接层并在服务中创建一个对象:

angular.module('app', [])
.service('ProfileService', ['$http', function ($http) {
    var service = this;

    service.profile = {};

    service.getProfile = function () {
        return $http.get('/profile').then(function (response) {
                for (key in response.data) {
                    service.profile[key] = response.data[key];
                };
                return true;
            });
    };

    return service;
}])
.controller('ProfileCtr', ['ProfileService', function (service) {
    var vm = this;

    service.getProfile().then(function () {
        vm.profile = service.profile;
    });
}]);

这通常有效,但现在我得到了笨拙的 controllerAs 语法:

<div ng-controller="ProfileCtr as ctr">
    <h1> {{ ctr.profile.Name }}</h1>
    <p> Email: {{ ctr.profile.Email }} <br /> DOB: {{ ctr.profile.DOB }}</p>
</div>

我想知道是否有一种方法可以同时满足我的要求:干净的 HTML {{ ctr.Name }} 语法 一种 DRY 编程风格。

感谢任何提示!

【问题讨论】:

标签: javascript angularjs dry


【解决方案1】:

我感觉你想要的不止这些,但对我来说这至少是 DRY:

angular.module('app', [])
.service('ProfileService', ['$http', function ($http) {
    var service = this;
    service.getProfile = function () {
        return $http.get('/profile').then(function (response) {
                return response.data;
            });
    };

    return service;
}])
.controller('ProfileCtr', ['ProfileService', function (ProfileService) {
    var vm = this;

    ProfileService.getProfile().then(function (profile) {
        vm.profile= profile;
    });
}]);

服务获取数据。您也可以在此处添加缓存功能。控制器使用该服务来获取数据。没有重复的代码。

我喜欢使用$scope 变量,它可以消除一层间接问题。但是,controllerAs 确实有它的优势,特别是如果您使用的是嵌套控制器并且想要明确您使用的是哪个控制器。并且$scope 标识符将在版本 2 中被移除。

对这部分 html 使用指令而不是控制器应该使您的代码更易于阅读和重用。也建议做好升级到版本2的准备。

然后:

app.directive('isolateScopeWithControllerAs', function () {

  var controller = ['ProfileService', function (ProfileService) {

    var vm = this;

    ProfileService.getProfile().then(function (profile) {
        vm.profile= profile;
    });

  }];    

  return {
      restrict: 'EA', //Default for 1.3+
      controller: controller,
      controllerAs: 'vm',
      bindToController: true, //required in 1.3+ with controllerAs
      templateUrl: // path to template
  };
});

那么你的 HTML 仍然给你:

    <h1> {{ vm.profile.Name }}</h1>
    <p> Email: {{ vm.profile.Email }} <br /> DOB: {{ vm.profile.DOB }}</p>

如果您将指令用于多个对象,ProfileCtr as vm 将得到更多使用。例如,如果你有一个用户指令,那么你可以有:

controllerAs: 'user',

user.profile.nameng-repeat='friend in user.friends'

【讨论】:

  • 在这种情况下,我仍然得到一层间接{{ ctr.profile.Name }}语法,这将使HTML模板难以阅读。
  • 我已经更新了答案,希望能对此有所帮助。
  • 我不确定我是否理解正确。你说得对,我想避免$scope。但是,我想避免使用user.profile.name,因为这会给我很多情况下命名最终会像ng-controller="profileCtr as profile"&gt; {{ profile.profile.name }} 这样的情况。另外,我不确定如何创建指令会派上用场,因为我已经可以使用上面的 angular-routes 或语法来获得它。
  • 指令让你重用 html 并摆脱 divs inside divs inside 问题。它们正在作为标准构建到 HTML 中。以及在 Angular 的第 2 版中将没有控制器,因此该应用程序在时机成熟时升级将是一件痛苦的事情。我觉得您命名的混乱来自于将控制器命名为与控制器中的对象相同的名称。即使像{{ profileCtrl.profile.name }} 这样的东西也会更容易阅读。它并不完美,但我相信这些问题将在 Angular 版本 2 中得到解决。
猜你喜欢
  • 2015-02-06
  • 1970-01-01
  • 1970-01-01
  • 2015-11-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-23
  • 1970-01-01
  • 2014-02-21
相关资源
最近更新 更多