保持CRUD DRY与Restangular:指令,继承控制器或包装服务?

mko*_*lum 5 javascript crud angularjs restangular

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

我有很多API端点,每个我都希望显示一个项目列表,允许选择每个项目(以显示该项目的详细信息)并删除,以及向列表中添加项目.

我到目前为止尝试过的方法都是非常非干的,没有适当地挂钩到范围内,或者有点hacky ......

包裹Restangular

将Restangular包装到服务中,并使用addElementTransformer在返回的集合上设置其他方法似乎是一种不错的方法; 在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();
            }
        }
    }
])
Run Code Online (Sandbox Code Playgroud)

应用程序/ 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
    });
}]);
Run Code Online (Sandbox Code Playgroud)

应用程序/分音/ 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 />
Run Code Online (Sandbox Code Playgroud)

扩展基本控制器

我尝试的另一种方法是将模板连接到基本控制器中的服务,我的其他控制器继承.但是我在这里仍然有$ 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); 
        }
    };
});
Run Code Online (Sandbox Code Playgroud)

应用程序/ JS/controllers.js

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

    $scope.companies = Restangular.all('companies').getList();
}])
Run Code Online (Sandbox Code Playgroud)

应用程序/分音/ 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>
Run Code Online (Sandbox Code Playgroud)

指令

我对指令有点新意,但经过大量的研究后,似乎可能是最接近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)
            });
        },

    };
});
Run Code Online (Sandbox Code Playgroud)

应用程序/分音/ 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>
Run Code Online (Sandbox Code Playgroud)

基本上,我可以让一切工作,但似乎我要么做了很多重复的代码(这是完全错误的),或发送我的范围与我的所有电话(这似乎也非常错误),或初始化我的服务通过范围(不是我的控制器的最新解决方案,但似乎是实现这一目标的最可维护和最脆弱的方式.

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

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

cha*_*tfl 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)">
Run Code Online (Sandbox Code Playgroud)

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

.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); 
    }
})
Run Code Online (Sandbox Code Playgroud)

在指令中将是相同的函数

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); 
 }
Run Code Online (Sandbox Code Playgroud)

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

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

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

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