Using the same controller for all CRUD operations (Rails-like)

I have an angular controller that retrieves a resource on creation:

angular.module('adminApp') .controller('PropertiesCtrl', function ($log, $scope, Property, $location) { $scope.properties = Property.query() }); 

Now I want to add logic to the controller to create a Property resource:

 angular.module('adminApp') .controller('PropertiesCtrl', function ($log, $scope, Property, $location) { $scope.properties = Property.query() $scope.create = function(){ //logic to create }; }); 

However, when I am in the form for creating a Property, an unnecessary call arises to get all the properties first. How can I avoid this?


Potential solutions?

  • I could create a separate controller specifically to create a property that will not receive properties. However, it would be easier to encapsulate all CRUD operations for one resource under one controller.
  • I could create a function to retrieve all the properties. However, my index page directly uses "properties." First I would need to get the data calling some method, and then using the data (somehow?)
+6
source share
3 answers

My reaction is that it seems that you are trying to use the controller as a service, and how you are trying to use many functions in one controller.

So, there are two main things you should think about. First of all, it is quite important to create a controller that has only one specific goal. Note that this is not the same as using the controller only once, you can use the same controller in several different places, if you have a function that should appear in several places. It just means that the controller does not have to do several things at once.

Take the photo gallery as an example. Although you can create one controller that receives all the photos, allows you to add new photos and allows you to edit and delete existing photos in one, this would be a bad idea. What if you decide that adding a photo can also be done from another Page X page? If you are using reuse of the same controller, you must also request a gallery from the server and configure controls for things that you are not going to on this page.

If instead you made one controller that is only responsible for receiving content, a separate controller for adding new photos, another for editing, etc., then this will be easy. You would just implement the creator controller on β€œpage X,” and you don't have to worry about accidentally starting up more than you wanted. You can precisely implement the desired function on the page and only this function. It also allows your controllers to be small, easy to read and quick to edit / correct errors, so it wins / wins!

Secondly, if you want to collect all your CRUD resources into one object (which I would also like to do), they should not be in the controller, they should be in the service. So you have one PhotoAPI that provides the functions CREATE, READ, UPDATE and DELETE. Then your index controller simply calls the READ function, your controller creates the CREATE function, etc. The controllers determine what functions and data are available there, but the logic is in the combined service. Thus, you can combine your resources so that they can be easily found without creating problems with the integrated controller.

So something like:

 app.service('PhotoAPIService', [ function() { this.READ = function() { // Read logic } this.CREATE = function() { // Create logic } }]); app.controller('PhotoIndexController', [ '$scope', 'PhotoAPIService', function($scope, PhotoAPIService) { $scope.photos = PhotoAPIService.READ(<data>); }]); app.controller('PhotoCreateController', [ '$scope', 'PhotoAPIService', function($scope, PhotoAPIService) { $scope.createPhoto = PhotoAPIService.CREATE; }]); 
+16
source

From your question (and from your SO tags), I see that you want to create Rails-like controllers in AngularJS. Since both structures (Rails and AngularJS) have a similar MVC principle, this is actually quite easy to accomplish.

Both frameworks allow you to specify different routes for using the same controller.

In Rails, the usual methods / actions / actions / action / action / editing / killing pointers are out of the box (using the padding). These actions are mapped to different, well-established HTTP routes and methods by default .

CRUD / Rails Route List enter image description here

Now, in AngularJS applications (or in all SPA systems) you only need a subset of these routes, since client-side routing only understands GET requests:

CRU / Route List in AngularJS enter image description here

AngularJS does not initially provide a forest mechanism that will generate all your CRUD routes for you. Nevertheless, it provides you with at least two different ways to connect your CRUD / List routes using one controller.

Option 1 (using $location.path() )

Using the location.path () method, you can structure your PhotosCtrl to perform different actions depending on the location path.

Routes:

 app.config( [ '$routeProvider', function ($routeProvider) { $routeProvider .when('/photos', { templateUrl: 'photos/index.html', controller: 'PhotosCtrl' }) .when('/photos/new', { templateUrl: 'photos/new.html', controller: 'PhotosCtrl' }) .when('/photos/:id', { templateUrl: 'photos/show.html', controller: 'PhotosCtrl' }) .when('/photos/:id/edit', { templateUrl: 'photos/edit.html', controller: 'PhotosCtrl' }); } ] ); 

controller:

 app.controller('PhotosCtrl', [ '$scope', 'Photos', // --> Photos $resource with custom '$remove' instance method '$location', '$routeParams', function($scope, Photos, $location, $routeParams){ if($location.path() === '/photos'){ // logic for listing photos $scope.photos = Photos.query(); } if($location.path() === '/photos/new'){ // logic for creating a new photo $scope.photo = new Photos(); } if(/\/photos\/\d*/.test($location.path())){ // eg /photos/44 // logic for displaying a specific photo $scope.photo = Photos.get({id: $routeParams.id}); } if(/\/photos\/\d*\/edit/.test($location.path())){ // eg /photos/44/edit // logic for editing a specific photo $scope.photo = Photos.get({id: $routeParams.id}); } // Method shared between 'show' and 'edit' actions $scope.remove = function(){ $scope.photo.$remove(); } // Method shared between 'new' and 'edit' actions $scope.save = function(){ $scope.photo.$save(); } } ]); 

These four ifs make the controller look a little messy, but when replacing 4 different controllers with one, several conventions are inevitable.

Option 2 (use allow property)

This parameter uses the resolve property of the route configuration object to create a different "action identifier" for different routes.

Routes:

 app.config( [ '$routeProvider', function ($routeProvider) { $routeProvider .when('/photos', { templateUrl: 'photos/index.html', controller: 'PhotosCtrl', resolve: { action: function(){return 'list';} } }) .when('/photos/new', { templateUrl: 'photos/new.html', controller: 'PhotosCtrl', resolve: { action: function(){return 'new';} } }) .when('/photos/:id', { templateUrl: 'photos/show.html', controller: 'PhotosCtrl', resolve: { action: function(){return 'show';} } }) .when('/photos/:id/edit', { templateUrl: 'photos/edit.html', controller: 'PhotosCtrl', resolve: { action: function(){return 'edit';} } }); } ] ); 

controller:

 app.controller('PhotosCtrl', [ '$scope', 'Photos', '$routeParams', 'action' function($scope, Photos, $routeParams, action){ if(action === 'list'){ // logic for listing photos $scope.photos = Photos.query(); } if(action === 'new'){ // logic for creating a new photo $scope.photo = new Photos(); } if(action === 'show') // logic fordisplaying a specfiic photo $scope.photo = Photos.get({id: $routeParams.id}); } if(action === 'edit') // logic for editing a specfic photo $scope.photo = Photos.get({id: $routeParams.id}); } // Method shared between 'show' and 'edit' actions $scope.remove = function(){ $scope.photo.$remove(); } // Method shared between 'new' and 'edit' actions $scope.save = function(){ $scope.photo.$save(); } } ]); 

Both methods require the use of some conditional expressions in your controller, but the second method is at least readable, because the exact action is allowed inside the routing mechanism, which distracts some logic from your busy controller.

Of course, in any real application you will probably have many more methods defined inside the controller, in which case your controller may become completely unreadable. These examples use a simple instance of $ resource ( Phones ), which relies on the simple RESTfull API (Rails?). But, when your presentation logic becomes complex, you probably want to use Angular / factory services to abstract some of the code in your controllers.

+7
source

It’s great to have several types (and controllers) using the same resource ... This is not a bad design.

If you need more than 1 resource to complete all CRUD operations, this will be a problem.

Go with your first decision. 1 controller per view. This is a resource that regroups all CRUD operations, not one controller.

0
source

Source: https://habr.com/ru/post/952464/


All Articles