Why can't I inject a controller into a controller? - angularjs

My application has a multi-tab order wizard. It has one Controller called OrderController. I decided I wanted to have one controller per tab, and for OrderController to manage all the tabs and navigation.
OrderController contained a static array of tab information (title, partial, etc). I decided that each tab should supply its own information to OrderController instead.
I made a 'tab 1' controller and a function that would return the tab information to OrderController. I injected 'tab 1' into OrderController but this failed with a 'no provider' error. I did some googling and found various ways a controller could talk to a controller. None involved a direct injection like I was attempting.
I tried a tab-specific Factory instead. (Maybe not a bad idea anyway since the information being provided is static.) This worked fine.
Sorry for the long-winded post.
But why can't I inject one controller into another, technically? Angularjs was having no part of it. Why is this a bad idea, logically?

Because Controllers don't really exist that way. They're the OO equivalent of a "template" for a set of behaviors and variables. They aren't created until they're actually used somewhere. The injector can only give you things that exist globally, and controllers aren't global.
A Factory (or if you don't need special creation behavior, a Service is the same thing but a little shorter to write) is exactly this pattern. It is a global singleton object (exactly one instance exists at all times) so the injector can find it and give it to you.
It's really just because there's no pattern for Angular to inject "Controller 4". Controllers can have 0..N instances at any time. How would it know which one you want? Just because you aren't planning to have more than one of this component doesn't mean you couldn't or others wouldn't. It's just not the injector's job.

Related

Should I call parent directive controller or create service

Let's say there is a "conversation" directive, made of "message" directives.
The message directive has a button to delete it.
The conversation controller has a method to remove a message from its list.
In Angular documentation, they say we can use "require" to access a parent controller from a directive controller.
And here, the answer suggests to use require too, and to create a service only if you can't use require because the 2 directives are not related.
Isn't it a bad practice to call a controller from another controller ?
I thought service was typically used to share information between controllers.
Don't you think it would be better to create a service with the deleteMessage method, and inject this service in the message controller ?
I'm personally not a fan of calling a function on the parent or root or stuff like that, as its coupled in a kind of implicit way. I prefer to use a service as you mentioned or injecting the concrete function(s) into your directive.
Below are some pros and cons for both approaches. Please note they are based on my personal experience (and I'm not an expert).
Services
Services are singletons, meaning there is only one instance in your whole application. This can have several advantages, if you need them:
it's always there (you don't need to create it explicitly)
it can cache data which can be usefull, e.g. to maintain the state of a view/page
it can be used to exchange data between different components
it can easily be injected where ever you need it
This can also lead to some disadvantages though:
if different components share the same service, they might mess up each others data
components (i.e. directives) will depend on that service, leaving the user of the directive no flexibility in how he wants to use that directive (e.g. what should happen when a user clicks a button). Meaning that basically the concrete functionality is "hard-coded" in to the directive.
I personally like to use services, if they have a direct link to the directive (and to nothing else) or do not store any state, for example if they only contain simple helper functions.
If in your application you have one conversation list, then I would consider implementing that in a service that exposes functions like addMessage, deleteMessage, sortBy, etc.. Then it can be injected in to any component which needs access to that (central) list.
Passing functions as arguments
On the other hand, if you have a message-directive, you might want to use it for other kind of messages as well..? That is messages which are not from the conversation but maybe from a mailbox (just a stupid example ;)). Then I wouldn't couple the service and the directive. You could inject the deleteMessage-function, which in one case would remove it from the conversation and in the other case remove it from the mailbox.
I think both approaches are valid, but always depend on the scenario and how the components are shared/reused over your application.
The second approach might be nicer from a "coupling"-perspective, but might get complicated in case of lots of parameters and when passing parameters over several levels of components (e.g. to child-child-child directives).
The first approach is easy to implment and can have several advantages, but results in the component being directly coupled to that service, lowering its reusability.
Do you really need to put the delete button in the message directive ?
Since the delete button is to suppress the message from the conversation controller list, I'd rather put the button in the conversation view.
That being said, if you really want to have the delete button on the messagedirective, I would simply pass the delete method as an argument in your directive. When doing this you don't have to worry about which controller defined the function, you simply call it from your child directive (and if you want the parent controller to execute it, simply pass a binded function to your directive).

Splitting functionality across controllers that must interact

We are using Angularjs and ui-router. We generally have a layout of each page that utilizes views. We have a filter view, sort view, and pagination view; as well as display views that can be swapped in and out.
Logically when changes are made we need to any of theses controllers we need to update the displayData as appropriate. Changes to filter should run the filterMethod, but also need to run sort and then pagination logic afterwards, while changes to sort should run just pagination after, making a clear order of operations for when each controller needs to it's update.
My problem comes when I consider that in some cases we may not want to utilize all 3 controllers. We may want filtering, but not pagination for example.
We are having trouble finding a clean way to make these controllers 'just work', so that we can plug in whichever control we want in uirouter and have them function. The problem is mostly one of scoping. If I do the obvious thing, and have each controller define their own updateData method when changes are made to it, I run into scoping problems if I want them call the next controller's update afterwords. The filter controller can't call sort because the two controllers don't share a scope. I can use broadcasts, but what if I want a filter and a pagination controller, but not a sort? How do I ensure that sort runs before pagination if both are present, but if sort controller doesn't exist pagination knows to run after filter?
I could instead move everything up to my top level controller, and then things just work. However then I end up with a controller that feels like it's doing way to much, It's cleaner to have one controller for each type of control if possible.
We have other approaches we could use, but they feel like their making pretty strong presumptions about our controller scheme. If I later added some fourth controller I would have to modify everything because each controller is really hard coded very explicitly with presumptions about how the other's run.
This seems like a common issue. Is there a a best practice or convenient technology for handling splitting of functionality across controllers?
Hoist the data, not the display logic. There are only two ways to share data between controllers cleanly: a service, and a parent controller.
If what you were sharing was data (eg: displayData), I might suggest a service object, but sounds more like application state (eg: orderBy), so I think these "settings" should live on a parent controller.

CakePHP multiple controllers using same method

I have a method (the function in the controller, am I terming that correctly?) and view that I want to use in every controller on my site. Is there a way to make the method global across all controllers and the view .ctp file generic as well? I'd rather not have to copy-paste it everywhere.
This seems like something that should be obvious, so if I'm just searching for the wrong terms, let me know.
Thanks
Shared/Common Controller Code:
What you've described is a "Component":
Components are packages of logic that are shared between controllers.
If you find yourself wanting to copy and paste things between
controllers, you might consider wrapping some functionality in a
component.
See: http://book.cakephp.org/2.0/en/controllers/components.html
Shared/Common View Code:
As far as the View is concerned, there are a few options. If you want the entire view, you can just specify which view to render: $this->render('TestView/index');
Or, if you want a small chunk of code, you can try an Element.
All together:
If you find yourself creating a lot of the different "parts" (View, Controller/Component, Model/Behavior)...etc, all for the same general purposes (ie cropping a photo), you could think about creating a Plugin.
Side note:
Side note: Usually, I've heard the functions in Controllers referred to as "actions", and the functions in Models called "methods". They're all really methods (a function within a class/object), but - that's how they're commonly referred to.
You can put the method in AppController and make only one view.
You will use $this->render('/myview.ctp');

Representing Angular Scopes and Controllers on Sequence Diagrams

Does anyone have a recommended approach for creating sequence diagrams that include Angular scopes (or, though I don't need it in my own use case, both Angular scopes and directives)?
It may sound like an odd question, but I'm wondering if it is appropriate to use an approach like this:
User -> View -> $scope -> Controller -> Service
Since the $scope's operations were placed onto it by the controller, it leads to some redundant entries since almost everything that's called on the scope will be delegated through to the controller. I'd be tempted to leave the $scope out of the diagram altogether, but I want to show how certain state that the $scope is managing is being updated.
I hope this makes sense; just looking for patterns and practices that would help visualize control flow better in Angular-centric applications.
The scope is just the "glue" between the view and the controller, the medium between the two of them. So, in my opinion, it shouldn't be represented in a diagram. I think you should describe how the view communicates with the controller in terms of the controller methods as if there wasn't nothing between them.

Help with cake controllers

We had an outsourced engineer work on a quick feature DELETING items listed in our database. He says that the code is difficult because the "controller" is missing. Is there a pre-loaded controller for every function like that in cake, or is it weird that he is expecting a controller to be there for a feature we didn't have yet.
There is a generic AppController, but that's more of an abstract class in practice (you generally derive your other controllers from that).
It's not that weird at all that he's expecting a controller -- after all, you won't be able to call methods in the models (which is how I'm guessing you're doing delete) unless you have a point of control to call them from. In this case, the point of control is the controller.
So you can just create a controller. Here's a template to start from:
class SomeController extends AppController {
function delete() {
$this->Some->delete();
}
}
Then access /somes/delete (remember, URLs are generally /controller/action).
Now, he could be talking about the Cake Bake CLI app. That will take your DB tables, and walk you through an initial basic setup for your app. Generally it creates a basic skeleton for CRUD actions.
Either way, you need to create a controller (manually, or via Bake).
When you use the Cake bake function, it'll create all the controllers for you. When you don't use it, you'll need to create them manually. It makes no sense to make all the controllers at the begin, just make them when you really gonna write them would be good.
If you do not have a controller in CakePHP when you visit a page (http://www.youraddress.com/Newfeature) you receive a missing controller error:
Error: NewfeatureController could not be found.
Error: Create the class NewfeatureController below in file: app\controllers\newfeature_controller.php
You cannot get or delete data from the database without controllers - Understanding Model-View-Controller. You do not need the controller only for static pages in CakePHP.

Resources