Является ли хорошей практикой комбинировать контроллеры CREATE и EDIT в AngularJS?
В контроллерах CREATE и EDIT много дублированных кодов.
Эти контроллеры можно объединить в один для минимизации повторяющегося кода.
Проблема: мне нужно отличить, какой метод использовать при отправке формы - create() или edit(), например.
Решение: я мог бы добавить $scope.mode
, например, и установить $scope.mode='edit'
, если пользователь нажал кнопку "EDIT" или установил $scope.mode='add'
, если пользователь нажал кнопку "ADD".
Я мог бы использовать службы для минимизации повторяющегося кода, но все равно будет дублированный код. Например, в обоих контроллерах у меня есть метод cancel(), который очищает форму и скрывает ее. Я мог бы хранить clearForm() и hideForm() в службе, но этот код будет дублироваться в обоих контроллерах:
$scope.cancel = function() {
Service.clearForm();
Service.hideForm();
};
Вопросы:
- Хорошо ли сочетать контроллеры CREATE и EDIT в AngularJS?
- Есть ли какие-либо хорошие методы для минимизации повторяющегося кода?
Ответы
Ответ 1
Да. Используйте 1 контроллер.
Вот почему вы используете 1 контроллер
Задача контроллера - поддерживать представление. Ваше представление создания и вид редактирования точно такие же - только у него есть данные, предварительно заполненные (редактировать), а другое - нет (создавать).
Кроме того, "цель" этого представления состоит в том, чтобы пользователь изменил или ввел новые значения в форму. Ваша единственная разница должна быть чем-то вроде reset(). Но даже там вы можете начать с пустого объекта модели, например. $scope.entity = {}
в случае CREATE, и вы начнете с $scope.entity = $http.get()
.
Проблема повторения с двумя контроллерами
С двумя разными контроллерами и службами вы понесете хотя бы следующее дублирование:
$scope.cancel = function() {
Service.cancel();
};
$scope.validate = function() {
ValidtionSvc.validate();
}
.
.
.//other stuff similar
но проблема в том, почему даже это дублирование, как вы заявили.
(UDATED здесь, так как выше был ответ на 1-й вопрос)
Как использовать 1 контроллер с повторением?
Есть ли какие-либо хорошие методы для минимизации повторяющегося кода?
Вопрос redefined: Есть ли хорошая практика устранения повторяющегося кода в формах CREATE и EDIT?
Никакой официальной "лучшей практики" не существует, насколько мне известно, чтобы избежать повторяющегося кода в этой конкретной ситуации. Однако я рекомендую использовать mode = edit/create. Причина в том, что для контроллеров в этой ситуации не должно быть почти никакой разницы, поскольку их работа заключается в том, чтобы просто получать/обновлять модель по мере взаимодействия пользователя.
Вот разница, с которой вы столкнетесь в этой ситуации, и как вы можете избежать, если /then/else с режимом = create/edit:
1) Заполнение формы существующими значениями против пустой формы для Create.
Для извлечения существующих объектов вам понадобятся некоторые данные ключа/запроса. Если такие ключевые данные присутствуют, вы можете сделать
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 world since we are treating PUT as POST
4) Валидация - это идеальное повторное использование - не должно быть различий.
5) Начальные/значения по умолчанию
Вы можете использовать masterEntity = Defaults вместо {}.
Ответ 2
Хорошо ли сочетать контроллеры CREATE и EDIT в AngularJS?
По моему опыту, да, это хорошая идея в 99,9% случаев. Обычно я добавляю переменную formType в свой контроллер через функцию разрешения $routeProvider. Поэтому у меня было бы что-то вроде следующего:
$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 для вызова, или я могу сделать простую проверку внутри контроллера в зависимости от того, какой тип формы был введен.
Есть ли какие-либо хорошие методы для минимизации повторяющегося кода?
Некоторые из вещей, которые я использую, чтобы держать вещи сухими:
Если вы придерживаетесь общего соглашения с API-интерфейсом вашего сервера, вы можете пройти очень длинный путь с базовым factory/репозиторием/классом (независимо от того, что вы хотите назвать) для доступа к данным. Например:
GET -> /{resource}?listQueryString // Return resource list
GET -> /{resource}/{id} // Return single resource
GET -> /{resource}/{id}/{resource}view // Return display representation of resource
PUT -> /{resource}/{id} // Update existing resource
POST -> /{resource}/ // Create new resource
etc.
Затем мы используем AngularJs factory, который возвращает базовый класс репозитория, позволяет называть его 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 всегда хорошо меня обслуживали, когда дело доходило до минимизации репликации кода между контроллерами. Если есть "хорошая практика для минимизации повторяющегося кода", я бы сказал, что это будут службы. Они являются глобальными для вашего приложения и могут быть введены в несколько контроллеров без проблем.
Я надеюсь, что это поможет!