繁体   English   中英

AngularJS - 使用controllerAs语法和服务属性进行DRY双向数据绑定

[英]AngularJS - DRY two-way data-binding using controllerAs syntax and service properties

我偶然发现了一个应该是常见且明显的问题,但我似乎无法绕过它。

我正在研究一个小型原型应用程序。 我的后端开发人员在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;
    });
}]);

这通常有效,但现在我的控制器语法变得笨拙:

<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编程风格。

谢谢你的任何提示!

我有一种感觉,你想要更多,但这对我来说至少是干的:

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变量,这将删除单层间接问题。 但是,控制器确实具有它的优点,特别是如果您使用嵌套控制器并希望清楚使用哪个控制器。 $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',

with user.profile.nameng-repeat='friend in user.friends'等。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM