Method Sharing in Angular
What started as me saying "let's give Angular a try" has now turned into an experiment that has been going on for a few years now. And being this far in, I still enjoy it.
Since first diving in, I have adopted some solid design principles. Many of these them can be found in @toddmotto's Angular Styleguide for teams or in @John_Papa's Angular Styleguide. Todd and John are both masters with Angular. Their style guides are both great for large teams. They have many similarities, as well as some differences. You have to read them both and pick the one that makes the most sense to you.
Today, however, I wanted to share one of my own. It has to do with sharing methods between parent and children scopes. I don't usually get worked-up enough to share my thoughts about how code ought to be written. However, today was different, and I decided to write this one down.
It isn't really fair for me to describe this as "It has something to do with sharing code between parent and children scopes", because I don't think that you should ever do this. Unfortunately, it is something that I have seen too often, as Angular makes it very easy to do. Let me explain a bit more about what I mean.
SAD PANDA
Consider you have a controller with two children controllers inside of it.
In the example here, you can see a parent controller with two children controllers inside of it. The parent controller's scope has a handleClick method on it. Because the children scopes inherit from the parent scope, both of them (ChildAController and ChildBController) can call $scope.handleClick(), as they inherit from the parent. In the above example, when the button from each child controller is clicked, the handler that will handle those two events is located inside the parent controller.
While the above example is trivial, as controllers get bigger and more complex, this example quickly becomes non-trivial. While maintaining code, when you see an event handler in your angular view, you begin to look for the handler on your view's controller. When you don't see it there, you have to walk up the scope inheritance to find where the method actually resides. This is not the worst thing ever, but not cool. But then, if you need to make any changes to the handler on the parent scope, the change will affect all other controllers that depend on the same method. Your code gets ugly quickly. Beginning with the first deviation of functionality, your code will get uglier and uglier. And this ugliness is kludgy, at best. You have to do silly things like if(checkForSomethingThatOnlyExistsOnChildAController()) and else if(checkForSomethingThatOnlyExistsOnChildBController()). That kind of stuff is complete rubbish. If you have ever done it, smack yourself in the face, right NOW. (smacked myself)
LET'S TRY AGAIN
There are better ways. If you have some functionality that you want to share with two different controllers, the way to share it is NOT by adding the method to the parent scope, as shown above. The way to share code between two controllers is by putting that code into a service. Each controller will have their own event handler, and they will each call the service to get the shared code that is housed there.
Let me rewrite the above code to show you what I mean.
So, what we end up with is each child controller has their own click handler. It isn't inherited from their parent anymore. #yay Additionally, we have pulled the reusable code out into a service, making it easier to share/reuse. #yay Further, our click handlers aren't coupled anymore. They are free to do their own specific things without clobbering one another. #yay
CONCLUSION
When we are tackling a problem and building a new app, or a new section of an existing app, we don't always know what the end-product will look like. Thus it is impossible to always forsee what you will need to do and make perfect design decisions with our code. However, by following some design principles, and using a solid style guide, we will make our life easier. As well as the lives of those who will maintain the code after we write it. Writing code this way will improve your codes readability, make it easier to maintain, and improve the reusability of your code.
This is my take on sharing methods between controllers. My take on sharing variables between controllers is slightly less strict. I will post about it another day. The TLDR is don't do it, unless it absolutely make sense. And when you do it, talk to the variable directly. Refer to it as $scope.$parent.foo instead of $scope.foo. Your code will be easier-to-read/more-predictable if you refer to things by their fully qualified name. However, this topic also deserves it's own post.
Communications Assistant at Education Ecosystem
9 年Aaron Frost thanks for sharing your knowledge it is this kind of information that needs to be shared more widely than some of the things that are popular on the Internet on this topic. Do you stream yourself at work? I'd love to expand my knowledge. Are you on .livecoding.tv?
I'm going on several years as well... do you have any merit badges left? I never got one :'(
Building cool stuff | Technology | Product | Google | Amazon | ISB
10 年Another simple thing that helps prevent this is the controllerAs syntax, which prevents unintentional (or intentional!) inheriting of scope methods based on the prototype chain. The combination of that along with this kind of methodology, is sweet sweet success in my opinion :D
Founder at Web2Bee
10 年This is the kind of refactoring any developper should take the time to do, as soon as some code is going to get duplicated, because it's simple and quickly done. In general, I spend more time refactoring existing code, as the project evolve, than writing new code. Keeping a code DRY on a daily basis is easy, whereas having to do it after weeks or month of evolutions is a nightmare. Thanks for sharing your thoughts.
Front End Engineer at Accenture Federal
10 年Such a simple & easy pattern to follow but it's so important for large teams and rapidly changing code. Services are easily the most well understood way to share code and I think the first example you have is how it starts in a new or small project. Once the app grows and one or two items need sharing it never gets refactored Thanks for sharing your sharing style Aaron.