Angular controllers: 1 per view - does it make sense? - angularjs

I have been thinking about this lately. Up until now I have always subscribed to John Papa's recommendation which can be seen here:
https://github.com/johnpapa/angular-styleguide#controllers
Define a controller for a view, and try not to reuse the controller for other views. Instead, move reusable logic to factories and keep the controller simple and focused on its view.
He gives a reason that I don't understand but for me it was primarily motivated by ease-of-work/maintainability. Basically, in working on large applications, it really sucks to come upon a bloated controller that is responsible for several disparate views. If a developer wants to clean up the controller, he/she has to go to each view and determine what is being used and why (so that methods can be consolidated if they are very similar) which is a big job. Typically (especially if they are under time constraints), they choose to just add whatever functionality they need to the model and to "come back to it later" which is how it got bloated in the first place. Also, I have always used the controller/directive as an indication of whether logic is repeated in many views or not (i.e. if another developer walks up to a controller that I have written he/she can be sure that I have only used it with one view because otherwise it would be directive).
This is a similar problem to the endpoint-problem which basically has people adding endpoints on an as-need basis and the eventually due to new people not knowing about old endpoints or simple forgetfulness the API gets super bloated and repetitive.
However, as I said, recently I have been thinking that this 1-1 controller to view relationship really works against the entire MVC pattern because it couples the model to the view and destroys the separation of concerns. I mean, as long as a controller stays focused (i.e. we have an EditUserCtrl whose job it is to edit a user etc.) then why shouldn't two views be able to use that controller? I mean if the business decides it needs a new view in another place that has the same function, why shouldn't I just write a new view and hook it up to the old controller? I guess what I am saying is that I am having trouble reconciling a convention that goes against the fundamentals of a framework.
Would love to hear others thoughts on this. Thanks in advance.

Like you said yourself. If a controller is responsible for more than one view, any developer can walk up to it, change something in the hopes of adding new functionality to view A and not even knowing it's breaking view B. I think the primarily goal would be to keep controllers clean and neat. That way, it's best to have 2 controllers almost identicals, but with only 2~3 line of codes than to have files being referenced by a numerous number of other files that nobody has control over it.
At one hand, you have to change 2 controllers if something drastically changes in view A and view B, but if it's a complex task, you'll probably have the controllers calling some sort of Gateway and that might mean that you won't have to change any of the controllers at all (good outcome).
On the other hand, if you have 10 different views repeating the same 2~3 lines of code, maybe it's time to abstract the controller functionality.

It doesn't couple the model because the it's best practice to have the business logic in services and view logic in directives , and have there be no real logic in the controllers. So you can one controller per view, but that controller should be very skinny. Personally, I like to only use controllers to assign and communicate variables between services and directives. If you want to reuse controller code, what's stopping you from either putting that logic in directives and services?
Almost none of the controllers I write (and for my job I write a large enterprise application in angular) have logic in them. Granted sometimes logic in controllers is unavoidable, but try to keep as little as possible there.
In this way, the controller stays focused. For a large application, with, say, 10 pages, it's better for both usability and readability to have small controllers for each page instead of one giant controller with a mix of variables, and when you want to re-use methods and functions, put them in services. If you find yourself having three pages with a form or button that has the exact same logic, then that might be better served with a custom directive.

Related

AngularJS: controller vs service

I have read a couple of posts regarding proper usage of angularjs entities: services, factories, controllers and directives.
My particular concern is a comparison of a controller and a service. None of the posts though told me what is that a controller can do what service cannot and vice versa.
Can this be listed or is it just a matter of being canonical in angular's usage?
Controllers are typically used to be bound with a view. Controllers manage a view's life cycle, and should be thought of as View Controllers. A new controller will be created for each instance of a view, meaning that if you navigate away from a certain view, and then back again - or if you have more than once instance of a certain view, a new controller will be created each time.
Services are typically used as the business logic of your application. Services are similar to singletons in the sense that they are created once, and the instance is maintained throughout the entire life cycle of your application. It is a good place to put your logical functions which many views or components will require, and also hold global cache which needs to be accessed throughout multiple areas in your application.
controllers - responsibilities: initialize the view, mediate interaction between view/scope and services. It has dependencies on the view and model, but is more concerned with the view and making it work.
services - responsibilities: provides business services that is not dependent on the view or the controller. Its primary concern is delivering services, regardless of the consumer (controller/view/other services).
I'm not convinced if persistence factors into the differences.
As per the AngularJS documentation, https://docs.angularjs.org/guide/concepts
Controllers are to do with view related business logic. Services, on the other hand, are to do with reusable business logic independent of the views.
In addition to what have been said above. Controllers may also hold the logic of your application while the application isn't so big. But as you application grows you would need to move the logic to use services(like factory). This would allow variables and functions needed around your application to be easily accessible.

Using parent scope as a service

I have an index page with a controller scoped to <body>
I'm finding it very convenient to store objects on the body controller that need to be accessed by multiple views.
My question is, is there anything wrong with doing this? Traditionally, I store such objects in a service, but sometimes I just have random things that i want to persist throughout the session and i'm finding the body controller fits them quite nice. Want to make sure I understand the best practice there, why i might not do that, or why it is actually a fine idea?
The Angular Documentation on Understanding Scopes, says the following:
Nesting controllers using ng-controller results in normal prototypal
inheritance, just like ng-include and ng-switch, so the same
techniques apply. However, "it is considered bad form for two
controllers to share information via $scope inheritance" --
http://onehungrymind.com/angularjs-sticky-notes-pt-1-architecture/ A
service should be used to share data between controllers instead.
I would argue that using a service is easier to test and it is separation of concerns. Also, depending on the number of variables and their names, it will become quite confusing to keep track of the right variables. If you use a service on the other hand, some IDEs (e.g. Webstorm) will be able access the variables with auto-completion.
I would say that in general this is a very bad practice because it goes against one of the golden principles of angularjs, which is to keep the structure and the behavior separated, we can do that thanks to the fact that dependency injection is at the core of angularjs architecture.
In general doing what you are suggesting will make your code to be more difficult to test and reuse, and in the long run more difficult to maintain too.

What is the real purpose of ViewModel in MVVM?

I had a talk with teamlead about this topic and from his point of view we can just use bindings and commandings omitting ViewModel, because we can test UI behaviour without VM using Automation or our own developed mechanisms of UI testing (based on automated clicks on Views). So, if there are no real benefits, why should I spawn "redundant" entities? Besides, automated integration tests look much more indicative than VM tests. So, it seems that we can mix VMs and Models.
update:
I agree that mixing VMs and Models brings into single .cs a data model and rules of data transformation for representing it in a View. But if this is only one benefit - I don't want to create a VM for each View.
So what pros of VM do you know?
The VM is the logic behind your UI. combining the UI code with the logic ends up in a mess, at least from my experience. Your view should define what you see - buttons, menus etc. Your VM is in charge of the binding and handling events caused by the user.
Edit:
Not wanting to create a VM for each view doesn't sound like a SW-oriented reason. Doing so will leave your view clean of logic and your model free to be the connecting layer between the core layer and your app layer.
I like the following example referring to the model and its role (why it shouldn't be combined with the VM): imagine you're developing some Android app using Google maps. Google maps are your core. Then one fine day you really need the option to, say, color parts of the map in pink, bright pink. An email to Google asking for colorPink(Map)will probably get you nowhere. That's where your model steps in and provides you the map wrapper that you need to define your pinky method.
Without a separate model, you'd have to go through every VM that uses map and update it.
So, the view has a role, the model has a role, the VM is the logic between those.
Edit 2:
There are some cases where there's no need of a model layer - I tended to disagree at first but was convinced after a discussion: In relatively small applications, the model can end up being a redundant wrapper with no added functionality over the core. In such cases, you can use the core objects directly.
What is he binding to? Whatever he is binding to is effectively a view model, whether you call it that or not. If it also doubles as a data model, then you're violating the Single Responsibility Principal (SRP). Essentially, the problem here is that you're lumping together code that is servicing different parts of the application architecture, which will lead to a convoluted mess.
UI Testing is a pain not just because you need to accomodate for changes in the View which can occur many times but also because UI tends to interfere with its own testing. Depending on the tests needed you'll need to keep track of focus, resize and possibly other (mouse) events which can be extremely hard to make it a representative test.
It is much easier to scope the test to the logic in the ViewModel and leave the UI testing to humans. The human testers do not need to test the logic; their only consern should be the UI.
By breaking down a ViewModel into a hierarchy of ViewModels you might be able to reuse a ViewModel multiple times. There is no rule that states that you should have a ViewModel for each View. (After a release or two I end up there but that's besides the point :) )
It depends on the nature of your Model - sometimes they are simple and can serve as both like you are suggesting. However, depending on the framework, you'll need to dirty up your model with some PropertyChanged event code which can get messy and distracting. In more complex cases, they serve as a mediator between your the view and the model. Let's suppose you're creating a client app that monitors a remote process or database entries - creating View Model's for these entities let's you surface your data in a way that is convenient to bind to for a UI framework (but is silly in a DB framework), encapsulate operations as commands, perform validation, etc etc.

Does sharing one instance of ViewModel among several Views have any merits?

One of my collegues offers quite a strange approach where VMs are used like singletons and each is attached simultaneously to several views.
I see no pros for such weirdness besides a kind of data sharing instead of caching at data access layer.
I've never seen this in practice but don't want to reject the ideas just because of that.
P.S. I speak about sharing one instance, not about applying differen views to one VM.
Thanks.
I suppose this approach could be useful if the views were to be kept in sync and made to look identical. Otherwise, I agree that it's confusing.
The only time I've seen singleton view models used in the real world are when you have a view that is also single instance, meaning that only one copy of the view is allowed to be open at any given time. In that case, there is a performance benefit because the view model doesn't have to be recreated every time the view is re-opened.
I'm not sure I would necessarily call it a "singleton" view model, but I like to share view model instances between multiple view controls in some cases. For example, this can be quite useful in a master/detail scenario where changes you make to the details may change the way the master part looks visually. For example, a list/tree view with an editing panel beside it that shows details of the selected item. Sure, you could do this kind of thing by passing messages between two VMs, but that seems more likely to add additional code than VM re-use.
Where I wouldn't recommend having a completely singleton VM is if you need some kind of master/detail scenario where the detail editing is modal, as in a dialog you open to make changes. There you are going to want to encapsulate the edit in a separate instance to make cancel support easier. An application-global (e.g. static) singleton implementation just makes that kind of thing much messier.
In theory you must have a View Model for each view, but I think that in some cases may be useful use tha same view model for different views. For instance, supose you are displaying a User in differents applicationĀ“s places and you want that when the User.Name changed in an applicationĀ“s place, also in all other places where the User is showed, the User.Name changed too. For this notification issues, it is better have only one view model, then using the INotifyPropertyChanged interface all views will be notified. I think that this is what your collegue would may want, but also is not good make an over use of this, because would increase the complexity of the application, and/or may bring some unexpected behaviors.

Code separation in cakePHP

I am developing an application in cakePHP which has two sets of methods :
1) Methods which are called from the mobile phone to process payments
2) Methods which handle the website logic.
Now, the problem is that both the methods handling the mobile calls and the website logic are present in the same controller. This has made my code very difficult to manage.
Does anyone have any suggestions on how I can separate my code.
Thanks a lot
In the MVC architecture of CakePHP, each controller should be handling the transactions relating to one specific thing.
From the sounds of it, you should be separating your code into two controllers - one for payment, one for website logic
You could have more than one controller per model which would be the way I would go if there is no commonality across the two sub-applications, although if the number of models involved is significant this could result in many controller files.
On the other hand, if the number of methods is relatively limited, I would prefix the method names and group them together within one controller. I usually do this with ajax methods.
Most important is to be comfortable with whichever way you do it - you will probably be the one that maintains it!

Resources