Is it good practice to combine CREATE and EDIT controllers in AngularJS? Is it good practice to combine CREATE and EDIT controllers in AngularJS? angularjs angularjs

Is it good practice to combine CREATE and EDIT controllers in AngularJS?


Yes. Use 1 controller.

Here is the reason why use 1 controller

The job of the controller is to support the View. Your create view and the edit view is exactly same - just that one has data pre-populated (edit) and another does not (create).Moreover the "purpose" of this View is to have the user change or enter new values in the form. Your only difference should be something like reset(). But even there you could start with an empty model object e.g. $scope.entity = {} in case of CREATE and you will start with $scope.entity = $http.get().

Repetition Problem with 2 Controllers

With 2 different controllers and services you are going to incur at least the following duplication:

$scope.cancel = function() {    Service.cancel();};$scope.validate = function() {   ValidtionSvc.validate();}...//other stuff similar

but the problem is why even this duplication like you stated.

(UDATED here onwards since above was the answer to the 1st question)

How to use 1 controller with repetition ?

Is there any good practices to minimize repetitive code?

Question redefined: Is there a good practice of eliminating repetitive code in CREATE and EDIT forms ?

No formal 'best practice' exist to my knowledge to avoid repetitive code in this specific situation. However I am advising against mode=edit/create. The reason being for controllers in this situation there should be almost no difference since their job is to purely to fetch/update the model as the user interacts.

Here are the difference you will encounter in this situation and how you can avoid if/then/else with mode=create/edit:

1) Populating the form with existing values vs. empty form for Create.

To fetch a existing entities you need some key/query data. If such key data is present you could do

var masterEntity = {};if(keyData) {   masterEntity = MyEntityResourceFactory.getEntity(keyData);} $scope.entity = masterEntity;//for Create this would be {}

2) reset() form should be simply

   $scope.reset = function() {      $scope.entity = masterEntity;   }

3) Update/Create

$http.post()//should not be different in today's world since we are treating PUT as POST

4) Validation - this is a perfect reuse - there should be no differences.

5) Initial / Default Values

You can use masterEntity = Defaults instead of {}.


Is it good practice to combine CREATE and EDIT controllers in AngularJS?

In my experience, yes it is a good idea for 99.9% of the time. I typically inject a formType variable into my controller via the $routeProvider resolve feature. So I would have something like the following:

$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; },        },    });

That way you get your entity and type of form action injected into the controller. I also share the same templates, so saving a form I can either rely on my repository/service to determine what REST endpoint to call, or I can do a simple check inside the controller depending on what formType was injected.

Is there any good practices to minimize repetitive code?

Some of the things I'm using to keep things DRY:

If you keep a common convention on your server API you can go a very long way with a base factory/repository/class (whatever you want to call it) for data access. For instance:

GET  -> /{resource}?listQueryString     // Return resource listGET  -> /{resource}/{id}                // Return single resourceGET  -> /{resource}/{id}/{resource}view // Return display representation of resourcePUT  -> /{resource}/{id}                // Update existing resourcePOST -> /{resource}/                    // Create new resourceetc.

We then use a AngularJs factory that returns a base repository class, lets call it abstractRepository. Then for each resource I create a concrete repository for that specific resource that prototypically inherits from abstractRepository, so I inherit all the shared/base features from abstractRepository and define any resource specific features to the concrete repository. This way the vast majority of data access code can be defined in the abstractRepository. Here's an example using 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;}]);

Concrete repository, let's use customer as an example:

app.factory('customerRepository', ['Restangular', 'abstractRepository', function (restangular, abstractRepository) {    function customerRepository() {        abstractRepository.call(this, restangular, 'customers');    }    abstractRepository.extend(customerRepository);    return new customerRepository();}]);

What you'll find if you use this base repository pattern is that most of your CRUD controllers will also share a lot of common code, so I typically create a base CRUD controller that my controllers inherit from. Some people dont like the idea of a base controller, but in our case it has served as well.


The answer to your first question probably depends on the specific circumstances.

If the two controllers share a substantial amount of operations, and the behavior of just one or two functions needs to be altered - why not! Maybe not the most elegant solution but hey, whatever works.

If the behavior of many or all controller operations is going to depend on '$scope.mode'...I'd say be careful. That seems like a dangerous path.

Angular services have always served me well when it comes to minimizing code replication between controllers. If there is a "good practice to minimizing repetitive code," I would say it would be services. They are global to your app and can be injected into multiple controllers without issue.

I hope that helps!