I have a requirement to list, edit and delete an entity. I have different views for each of this operations. I want to know if it is a good practice to use the same Angular just controller for these operations that works with each of the operation or should there be a separate controller for each?
Also if using same controller for these operations, is it possible to call different function when different views are loaded? So when user goes to the list view, a list method is called on page load and when he goes to the edit view, an edit method of the controller is called on edit view's load. I manage to achieve this by calling the methods using ngInit but apparently that is not recommended in v1.2 and should only be used with ngRepeat.
My question is similar to this one. Angular - Using one controller for many coherent views across multiple HTTP requests
However I also want to know if there is a way to call different initialisation methods of the same controller depending on the view the controller is used by.
A better approach could be to write a utility service which can be used across the controller. Use this service in your different controllers.
Your service will look something like this:
(function() {
'use strict';
// this function is strict...
angular
.module('myapp.services', [])
.service('Utility', function() {
var Utility = {};
Utility.edit = function(id, dataset) {
//perform edit related task here
};
Utility.delete = function(id, dataset) {
//perform edit related task here
};
return Utility;
})
}());
I have got my answer here: Using same controller for all CRUD operations (Rails-alike)
Apparently it is a good practice to use a different controller for each view, and it shouldn't work as a service. This is quite different for someone coming from MVC/WebAPI into angular.
Related
This is somewhat a follow up on my "Is it bad practice for an angular directive to request data" Q.
My Q Is where would be the appropriate place to keep application data?
for example - information about the current user such as his name and his roles in the app?
differrent areas (on the screen) and components will depend on this data (e.g - side bar will want to know if the curentUser.isAnAdmin and a helloUser Directive would like to know the currentUser.name
Does this mean that the currentUser should be placed on the $rootScope?
and what should be the trigger for retrieving the initial data for the currentUser and for refreshing this information?
I was thinking of having several ngControllers responsible for setting up this data on the scope of the same html node as that of the ngApp, but found out that it is not possible to have multiple ngControllers on a single HTML Item.
I am now thinking of having multiple services with methods that get a scope object and assign the data they are responsible to onto that scope.
It would allow me to separate code for currentUser from code for someOtherSharedAppData into two different services and call both of them from the applications's main controller thus assiging the data to the scope associated with the top-most element in the app - does that make sense?
In fact you asked two questions here:
Where to store and manipulate data?
When and how should I use the $rootScope (compared to $scope)?
1)
I will refer to this article:
Whenever data and methods need to be reusable I would write a service like this for example.
angular.module('modelDemo').service("myModel", [function() {
this.list = [what, ever, items, you, have];
this.property= null;
this.setProperty = function(value) {
this.property= value;
};
}]);
Note, that I'm not passing the $scope as you considered. Instead I would inject the service in my controller and bind the $scope variables like this:
$scope.list = myModel.list;
If you need, you can even bind to the full model
$scope.myModel = myModel;
myModel.setPropery(value)
Got the idea? This way all model changes will be directly available to the corresponging view
{{myModel.property}}
ng-repeat="item in myModel.list"
ng-click="myModel.setProperty(item)"
Conclusion: Yes, you should have different services for your user model and your someOtherSharedAppData models.
2)
I will refer to this SO Question.
In short: If you have data that should be available in many views, it is OK to bind your (service) model to $rootScope variables. As you can see in the mentioned discussion there are also other opinions but I think the conclusion is: It depends on the structure and needs of your application.
I've come across many articles that shows how to separate business logic from controller and keep them in separate layers. As for angular, we add all the logic in our services,factories etc.
But I've come across the following line of code
angular.module('myApp').controller(function($scope,$userService) {
$scope.users = $userService.get('/users');
$scope.add = function() {
// do something
};
});
and people still argue that we are still adding logic in our controllers. If that's true, then what will be the best way to initialize data in my controller so that I can avoid having logic in my controllers OR any best practices that can help me achieve those.
P.S I'm requesting suggestions strictly for Angular.
There is definitely "logic" in a controller, but the logic should be limited to defining the ViewModel and changing it by reacting to events from the View and from the Model.
The logic deals with the state of the app, or a portion of the app's view for which the controller is authoritative for.
The logic that should not be in the controller has to do with backend knowledge, manipulation of the model's data, manipulation of the View / DOM, business logic that doesn't directly relates to how the data is staged for presentation.
Your example is fine, except for the "/users" part, which could benefit from being abstracted away in the service.
In the code example given, you could make use of a resource (https://docs.angularjs.org/api/ngResource/service/$resource) to abstract the URL detail. The code might then look as follows:
angular.module('myApp').controller(function($scope,Users) {
$scope.users = Users.query();
$scope.add = function() {
// do something
};
});
I'm assuming that the the add function is linked to an ng-click event with the template, in which case that looks ok. If in place of // do something there was a a lot of code, that could perhaps be moved into a Service.
Taken from the AngularJS Docs:
Controllers are "classes" or "constructor functions" that are
responsible for providing the application behavior that supports the
declarative markup in the template.
As a rule of thumb, if it's not application behavior e.g. updating the model, handling a click event etc then abstract into a service.
i am writing a code in angularjs which loads the controller file as per session demands
i have a separate controller files for admin(let say ctrl1) and for staffs (ctrl2) and one common(ctrlComm) file which can be use any among them,now i want to call function of ctrl1 inside ctrlComm, i do not want to write or paste the file code again,how can i do this.Here is my code.
ctrl1:
customerSupport.controller('studentSheet',['$scope',function($scope){
$scope.searchStudent=function(studentid){
angService.getfromRemote('students/'+studentid)
.success(function(response){
if(response.success){
...
}
})
}
}])
ctrlComm
activities.controller('usersActivites',['$scope',function(){
$scope.studentdetail=function(studentid){
console.log("how can i call the searchStudent if ctrl1 here ??");
$scope.searchStudent(studentid);
}
}])
Sharing a function is a clear sign you need a service.
Angular services are substitutable objects that are wired together using dependency injection (DI). You can use services to organize and share code across your app.
I believe you already have angService in place for this. Inject this service in usersActivites and you are good to go. You can tweak your service to get student details directly. Create some function there like angService.searchStudent and from service return appropriate response.
All of the controllers in my app share a dependency on a data provider type service I created. This service consists of functions to retrieve various bits of data, and almost all of these methods allow for an optional parameter. The ability to enter in this optional parameter is role based. The problem is my controllers are now full of code similar to:
// Initializing controller.
dataservice.getRole().then(function(role) { $scope.isAdmin = role.isAdmin; });
// After a button press or some other event.
if($scope.isAdmin) {
dataservice.getData($scope.param1, $scope.param2, $scope.optionalText);
} else {
dataservice.getData($scope.param1, $scope.param2);
}
It seems a code smell to me that I have to keep repeating this code throughout the controllers, but I can't think of a way to construct my controllers where I don't have to.
That's how I've realized that: Controller Inheritance.
Basically:
Create your service (factory) with your Base Controller logic and
return the Constructor (example)
Use $injector.invoke in your derived Class (to implement your
base controller) in order to enrich the actual controller $scope
(example)
There's no an official "angular way" to implement this features, but I hope the angular team will work on it
Here my app.js code :
Ext.Loader.setConfig({
enabled: true
});
Ext.application({
name: 'KP',
appFolder: 'scripts/app',
controllers: [
'login.Login'
],
views: [
'login.Login'
],
launch: function () {
var view = Ext.widget('login')
}
});
If I want to use some others views, controllers, models and stores in my application, should I define them in app.js? (like this : controllers[....all of my controllers.....])
Are there other way to get to work init function in my controllers? Thanks!
There are many ways...
Following some basics first:
All controllers that are listed within the controllers array of the application controller get instantiated at startup (application get initialized with the onReady event. Also the init() and onLaunch() methods of the listed controllers get called. See the linked API for details when this occurs). Now each instantiated controller initialize its stores,views and models (creates getter for all and over that creates instances of each store while overwriting the storeId and append them to the Ext.StoreMgr). Note that each controller will contains his own models, stores and views.
The simplest way to receive a controller that is not listed in the application controller is by using a reference of the application controller and calling getController(name)
It is Important to know that while using that way the receive a controller instance the getter method will not call the init() or onLaunch() method for the invoked controller, it will just try to get the controller or create it. You should also note the API for those methods in these controllers are no longer correct. You need to set an init bool to these controllers and check it on the reference that the getController(name) method hands back to you. If it is undefined/false you call the init() / onLaunch() by yourself and set it after that. I use these technique by myself to reduce intial load for bigger application. I guess you don't really need this for just a handful of controllers.
Update
Due to some changes in the 4.1.x release it is no longer required to init the controller
manually. This is now done for us by the getController method
You can define as many controllers as you want. To implement hierarchy you can define views and stores related to certain controller within it.
For example:
controller/
artists.js (inside: artistsView1.js, artistsView2.js, artistsStore.js)
paintings.js (inside: paintingsView1.js, paintingsStore.js)
All of your views and stores used in controllers would be loaded and initializadduring Applocation load.
The controllers you define in your application are loaded before the application launch() is called. As each controller is loaded its models, views and stores (MVS) are also loaded.
Although you can define the MVSs in your application, I'd recommend that each controller defines its related MVSs, as it is possible for the programmer to load controllers (and their related MVSs) upon request rather than by default. It is also a good practice from reusability point of view (so if you ever to reuse the controller, you know what MVSs come with it).
You may want to include some views in the application if, say, they are used without controllers, like some sort of a custom window you display if there was some error in the system.