5

我对 AngularJS 比较陌生,我一直在尝试各种方法来使用 Restangular 保持我的 AngularJS CRUD 方法干燥,所以我很感谢社区的任何建议。

我有很多 API 端点,对于每个我想显示一个项目列表,允许选择每个项目(以显示该项目的详细信息)和删除,以及将项目添加到列表中。

到目前为止,我尝试过的每一种方法要么非常不干燥,要么没有正确连接到 $scope,要么有点 hacky...

包装角

将 Restangular 包装到服务中,并使用 addElementTransformer 在返回的集合上设置其他方法似乎是一种不错的方法;和方法很干净,很容易从模板中实现,并在 promise 解决时自动更新列表selectadd但是,因为该delete方法实际上是从元素范围(集合的子对象)中调用的,所以在删除后更新列表需要非常可疑地将 $scope 注入服务 - 我能想到的最好的方法如下:

(注意:我通过仅说明单个 API 端点/控制器/模板使示例更易于阅读)

应用程序/js/services.js

function ListTransformer(collection) {
    collection.selected = {}
    collection.assignedTo = {}
    collection.assignedName = ""

    collection.add = function(item) {
        collection.post(item)
        .then(function(response){
            collection.push(response);
        })
    };
    collection.delete = function(item, scope) {
        item.remove()
        .then(function(response){
            console.log(item)
            console.log(collection)
            collection.assignedTo[collection.assignedName] = _.without(collection, item);
        })
    };
    collection.select = function(item) {
        this.selected = item;
    };
    collection.assignTo = function(scope, name) {
        scope[name] = collection;
        collection.assignedTo = scope
        collection.assignedName = name
    };
    return collection;
};

angular.module('myApp.services', ['restangular'])
.factory('Company', ['Restangular',
    function(Restangular){
        var restAngular = Restangular.withConfig(function(Configurer) {
                Configurer.addElementTransformer('companies', true, ListTransformer);
        });

        var _companyService = restAngular.all('companies');

        return {
            getList: function() {
                return _companyService.getList();
            }
        }
    }
])

应用程序/js/controllers.js

angular.module('myApp.controllers', [])
.controller('CompanyCtrl', ['$scope', 'Company', function($scope, Company) {

    Company.getList()
    .then(function(companies) {
        companies.assignTo($scope, 'companies'); // This seems nasty
    });
}]);

应用程序/partials/companies.html

<ul>
    <li ng-repeat="company in companies">
      <a href="" ng-click="companies.select(company)">{{company.name}}</a> <a href="" ng-click="clients.delete(client)">DEL</a>
    </li>
</ul>
<hr>
{{companies.selected.name}}<br />
{{companies.selected.city}}<br />

扩展基本控制器

我尝试的另一种方法是将模板连接到基本控制器中的服务,我的其他控制器继承了该服务。但是我在这里仍然有 $scope 问题,最终需要从模板中传递范围,这似乎不正确:

(编辑到只是delete方法)

应用程序/js/services.js

angular.module('myApp.services', ['restangular'])
.factory('ListController', function() {
    return {
        delete: function(scope, item, list) {
            item.remove();
            scope.$parent[list] = _.without(scope.$parent[list], item); 
        }
    };
});

应用程序/js/controllers.js

.controller('CompanyCtrl', ['$scope', 'ListController', 'Restangular', function($scope, ListController, Restangular) {
    angular.extend($scope, ListController);

    $scope.companies = Restangular.all('companies').getList();
}])

应用程序/partials/companies.html

<ul>
    <li ng-repeat="company in companies">
      <a href="" ng-click="companies.select(company)">{{company.name}}</a> <a href="" ng-click="delete(this, companies, company)">DEL</a>
    </li>
</ul>

指令

我对指令有点陌生,但经过大量研究后,似乎它可能是处理这个问题的最AngularJS方法,所以我潜入了。我已经做了很多实验,但请原谅任何明显的错误;基本上问题仍然是,虽然我可以从列表中删除项目,除非我将所有变量传递给指令,否则我无法访问列表最初分配给的 $scope 上的属性,因此它不会在视图中更新。

应用程序/js/directives.js

angular.module('myApp.directives', [])
.directive('delete', function() {
    return {
        restrict: 'E',
        replace: true,
        template: '<button>Delete</button>',
        link: function (scope, element, attrs) {
            element.bind('click', function() {
                var item = scope[attrs.item];
                item.remove();
                scope.$parent[attrs.from] = _.without(scope.$parent[attrs.from], item)
            });
        },

    };
});

应用程序/partials/companies.html

<ul>
    <li ng-repeat="company in companies">
      <a href="" ng-click="companies.select(company)">{{company.name}}</a> <delete item="company" from="companies">
    </li>
</ul>

基本上,我可以让一切正常工作,但似乎我要么必须做很多重复的代码(这是完全错误的),要么将我的范围与我的所有调用一起发送(这似乎也非常错误),或者初始化我的服务通过传递范围(对于我的控制器来说不是最整洁的解决方案,但似乎是实现这一目标的最可维护和最不脆弱的方式。

当然,我可以删除该功能,或将其移至详细视图或复选框/批量操作功能,但我全神贯注于问题并有兴趣找出最佳解决方案 :)

抱歉,如果我遗漏了一些明显的东西!

4

1 回答 1

3

从指令开始我只是记下笔记:

最大的学习曲线是范围继承。一般的经验法则是,如果您不scope向从指令返回的对象添加属性,则该指令会继承它的父范围。

您为删除按钮显示的指令因此具有相同的范围,CompanyCtrl因为您尚未在指令内部定义范围对象。

这意味着您可以在任一位置编写删除函数并在删除按钮上使用它。远离创建自己的点击处理程序element.bind('click')。主要原因是当您在其中操作范围项目时,您通常必须调用 scope.$apply() 以让角度知道范围已从外部事件更改。使用ng-click,因为它直接绑定到范围。对于您的删除功能:

瘦身属性。现在只是一个标签,它有一个自定义模板和一个角度范围绑定点击处理程序

<!-- company object comes from ng-repeat so can pass it right into a click handler as param-->
<delete ng-click="deleteCompany(company)">

可以在指令或控制器中编写此函数...因为它们共享相同的范围。

.controller('CompanyCtrl', function($scope){
    $scope.deleteCompany=function(company){
        /* I've never used restangular...using native methods*/
        /* call my AJAX service and on resolve*/
        var idx= $scope.companies.indexOf(comapny);
        /* remove from array*/
        $scope.companies.splice(idx,1); 
    }
})

在指令中将是相同的功能

link:function(scope,elem,attrs){
      /* call my AJAX service and on resolve*/
    var idx= scope.companies.indexOf(comapny);
    /* remove from array*/
    scope.companies.splice(idx,1); 
 }

我看到您试图通过将一个控制器注入另一个控制器来在控制器之间共享数据。第一个思路是让他们共享服务,在需要时与指令类似,例如指令的深度嵌套。

将数据存储在服务中,将服务注入需要它的控制器/指令中,只要所有数据都存储为对象而不是原语(字符串或布尔变量),这些对象就可以在应用程序中的任何位置引用并共享原型继承并被无论范围嵌套如何,从任何地方修改,角度对象监视将在引用该对象的任何地方更改 DOM。

这是我之前为想要从表单提交视图移动到具有另一个控制器的另一个视图并跨控制器传递数据的人玩的一个小演示。它基本上获取表单数据,将其传递给共享服务,进行模拟 AJAX 调用,将表单数据存储在服务中,然后更改路径以初始化新模板和控制器。由于数据存储在服务中,当第二个视图控制器需要它时它就准备好了。可能会帮助您想象我在说什么……也许不是。还展示了自动角度表单验证是如何启动的

通过服务演示切换视图/控制器传递数据

于 2013-11-04T03:59:41.227 回答