在 AngularJS 中结合 CREATE 和 EDIT 控制器是一种好习惯吗?
Posted
技术标签:
【中文标题】在 AngularJS 中结合 CREATE 和 EDIT 控制器是一种好习惯吗?【英文标题】:Is it good practice to combine CREATE and EDIT controllers in AngularJS? 【发布时间】:2014-09-22 15:33:02 【问题描述】:CREATE 和 EDIT 控制器之间有很多重复的代码。 这些控制器可以合二为一,以最大限度地减少重复代码。
问题:我需要区分表单提交时使用哪种方法 - 例如 create() 或 edit()。
解决方案:例如,我可以添加$scope.mode
,如果用户单击“编辑”按钮,则设置$scope.mode='edit'
;如果用户单击“添加”按钮,则设置$scope.mode='add'
。
我可以使用服务来最大限度地减少重复代码,但仍然会有重复的代码。例如,在两个控制器中,我都有 cancel() 方法,它可以清除表单并将其隐藏。我可以将 clearForm() 和 hideForm() 存储在服务中,但是此代码将在两个控制器中重复:
$scope.cancel = function()
Service.clearForm();
Service.hideForm();
;
问题:
有没有什么好的做法可以减少重复代码?【问题讨论】:
除了提交用户输入时会发生什么之外,每个功能之间是否有任何区别?根据我的经验,这将是非常不寻常的。 【参考方案1】:是的。使用 1 个控制器。
这就是为什么使用 1 个控制器的原因
控制器的工作是支持视图。您的创建视图和编辑视图完全相同 - 只是一个具有预先填充的数据(编辑)而另一个没有(创建)。
此外,此视图的“目的”是让用户在表单中更改或输入新值。您唯一的区别应该是 reset() 之类的。但即使在那里,您也可以从一个空的模型对象开始,例如$scope.entity =
在 CREATE 的情况下,您将从 $scope.entity = $http.get()
开始。
2 个控制器的重复问题
使用 2 个不同的控制器和服务,您至少会产生以下重复:
$scope.cancel = function()
Service.cancel();
;
$scope.validate = function()
ValidtionSvc.validate();
.
.
.//other stuff similar
但问题是为什么即使像你所说的这种重复。
(此处为更新,因为上面是第一个问题的答案)
如何重复使用 1 个控制器?
有没有什么好的做法可以减少重复代码?
问题重新定义:是否有消除 CREATE 和 EDIT 表单中重复代码的良好做法?
据我所知,不存在正式的“最佳实践”来避免在这种特定情况下重复代码。但是我建议不要使用 mode=edit/create。在这种情况下,控制器的原因应该几乎没有区别,因为他们的工作纯粹是在用户交互时获取/更新模型。
以下是您在这种情况下会遇到的差异,以及如何使用 mode=create/edit 避免 if/then/else:
1) 使用现有值填充表单与创建的空表单。
要获取现有实体,您需要一些键/查询数据。如果存在这样的关键数据,您可以这样做
var masterEntity = ;
if(keyData)
masterEntity = MyEntityResourceFactory.getEntity(keyData);
$scope.entity = masterEntity;//for Create this would be
2) reset() 形式 应该很简单
$scope.reset = function()
$scope.entity = masterEntity;
3) 更新/创建
$http.post()//should not be different in today's world since we are treating PUT as POST
4) 验证 - 这是一个完美的重用 - 应该没有区别。
5) 初始/默认值
您可以使用 masterEntity = Defaults 代替 。
【讨论】:
非常感谢您的回答。使用 2 个控制器和服务时,我在应用程序中有完全相同的代码重复。 用第二部分“如何?”更新了我的答案【参考方案2】:将 CREATE 和 EDIT 控制器组合在 AngularJS?
根据我的经验,是的,在 99.9% 的情况下这是一个好主意。我通常通过 $routeProvider 解析功能将 formType 变量注入我的控制器。所以我会有以下内容:
$routeProvider
.when('/item/create',
templateUrl: '/app/item/itemForm.html',
controller: 'itemFormController',
resolve:
item: ['$route', 'itemRepository', function ($route, itemRepository)
return itemRepository.getNew();
],
formType: function () return Enums.FormType.CREATE;
,
)
.when('/item/edit/:itemId',
templateUrl: '/app/item/itemForm.html',
controller: 'itemFormController',
resolve:
item: ['$route', 'itemRepository', function ($route, itemRepository)
return itemRepository.get($route.current.params.itemId);
],
formType: function () return Enums.FormType.EDIT; ,
,
);
这样您就可以将实体和表单操作类型注入控制器。我也共享相同的模板,因此保存表单我可以依赖我的存储库/服务来确定要调用的 REST 端点,或者我可以根据注入的 formType 在控制器内部进行简单检查。
有没有什么好的做法可以减少重复代码?
我用来保持干燥的一些东西:
如果您在服务器 API 上保持通用约定,则可以使用基本工厂/存储库/类(无论您想调用什么)来进行数据访问。例如:
GET -> /resource?listQueryString // Return resource list
GET -> /resource/id // Return single resource
GET -> /resource/id/resourceview // Return display representation of resource
PUT -> /resource/id // Update existing resource
POST -> /resource/ // Create new resource
etc.
然后我们使用一个 AngularJs 工厂,它返回一个基础存储库类,我们称之为abstractRepository
。然后,对于每个资源,我为该特定资源创建一个具体的存储库,该资源在原型上继承自 abstractRepository,因此我从 abstractRepository 继承所有共享/基本功能,并将任何资源特定功能定义到具体存储库。这样绝大多数数据访问代码都可以在 abstractRepository 中定义。这是一个使用 Restangular 的示例:
abstractRepository
app.factory('abstractRepository', [function ()
function abstractRepository(restangular, route)
this.restangular = restangular;
this.route = route;
abstractRepository.prototype =
getList: function (params)
return this.restangular.all(this.route).getList(params);
,
get: function (id)
return this.restangular.one(this.route, id).get();
,
getView: function (id)
return this.restangular.one(this.route, id).one(this.route + 'view').get();
,
update: function (updatedResource)
return updatedResource.put();
,
create: function (newResource)
return this.restangular.all(this.route).post(newResource);
// etc.
;
abstractRepository.extend = function (repository)
repository.prototype = Object.create(abstractRepository.prototype);
repository.prototype.constructor = repository;
;
return abstractRepository;
]);
具体的仓库,我们以客户为例:
app.factory('customerRepository', ['Restangular', 'abstractRepository', function (restangular, abstractRepository)
function customerRepository()
abstractRepository.call(this, restangular, 'customers');
abstractRepository.extend(customerRepository);
return new customerRepository();
]);
如果您使用此基本存储库模式,您会发现大多数 CRUD 控制器也将共享许多公共代码,因此我通常创建一个基本 CRUD 控制器,我的控制器从该控制器继承。有些人不喜欢基本控制器的想法,但在我们的例子中,它也起到了作用。
【讨论】:
【参考方案3】:第一个问题的答案可能取决于具体情况。
如果两个控制器共享大量操作,并且只需要更改一两个函数的行为 - 为什么不呢!也许不是最优雅的解决方案,但嘿,不管用什么。
如果许多或所有控制器操作的行为将取决于'$scope.mode'...我会说要小心。这似乎是一条危险的道路。
在最小化控制器之间的代码复制方面,Angular 服务一直对我很有帮助。如果有“减少重复代码的良好做法”,我会说它是服务。它们对您的应用来说是全局的,可以毫无问题地注入到多个控制器中。
希望对您有所帮助!
【讨论】:
非常感谢您的回答。我更新了问题并在那里添加了我对服务的看法。以上是关于在 AngularJS 中结合 CREATE 和 EDIT 控制器是一种好习惯吗?的主要内容,如果未能解决你的问题,请参考以下文章
“未捕获的类型错误:在 angularJS/NodeJS 中使用条带支付方法 Stripe.charges.create() 时无法读取未定义的属性‘创建’”
将BootstrapJS和AngularJS结合使用以及为啥不用jQuery
结合 AngularJS 和 Twitter Bootstrap 的最佳方式