Controller As, and dividing controllers for modularity – Angular lunch

This month at the St. Louis Angular Lunch (which we host), John Baur explained the “controller as” syntax, which has come to be the preferred and recommended approach. Afterward, he showed examples of dividing a long, complex controller into shorter, more modular controller-components. Here is a video – we are experimenting with the best way to record these lunch talks, hopefully the code is readable enough. We’ll do something better as we learn how.

Transcript

We have also transcribed the the talk to text, provided below. This is a rough, first-draft transcription, so any errors are probably in that proces./strong>

John: Alright, my name is John Baur. I’m an employee here at Oasis Digital. I’ve been working here a couple of years now. And been using Angular most of that time. Today I want to talk a little bit about the new Controller As feature. I say new, it was introduced around 1.2 but still not being used a lot. And then I spend a lot of time or most of the times talking about how to manage messy complex Controllers. When you first learn about Angular, one of the first things you’re taught is about Controllers and how to use this scope.

So you give your module a controller, then you inject the scope into that controller. Then you can put properties on the scope, and somehow mysteriously, they’re available over here in your HTML. So there’s a lot of magic behind it. Sorry I pulled up the wrong thing. But there you see, my message is put up there. So this works great and all but there is a lot of mystery behind it. Why do I have to put a scope on every controller I create? If I’m an outside developer who doesn’t know a whole lot about Angular and I come look at your code, I see that you’re using a message but it’s not really clear where that comes from unless I know about the scope over here.

There’s a new way to do it using Controller As which is it’s starting to become the preferred method around people who use the Angular. But controller really is just a JavaScript class so we can kind of use it as one instead of it injecting scope here. I’m just going to use this object. So and I’ll set this.message=Hello world. Let me change it just so we know we’re getting it updated.

So over here my HTML instead of just referring to the main controller, now we’re going to instantiate, or we’re going to create an Instances Controller. And we give it a variable name. Just make Controller As the main. So now I combine some main.message. Save. It didn’t work. Oh I knew that was going to happen as soon as I try this live. Oh because I didn’t save my controller, a minor technical hiccup. So it did work. I just didn’t save everything. So I think this is a lot better. For one, there’s no confusion on where this message came from. Message is property of Main which is an Instances Main Controller. If I go look in the code, I can see where it came from. The other thing and this is taught in a lot of Angular tutorials is the data roll. So if you start nesting Controllers and put a new div under here. Normally you wouldn’t know is this message supposed to refer to the 2nd Controller, to the Main Controller? Is it ambiguous? Using this, you would say 2nd Controller As and now you can easily read through the HTML and know exactly where that message is coming from. So that’s the quick and dirty.

One more thing I forgot. You’re not automatically injecting scope into a Controller just because you’re creating a Controller. You’re only injecting scope when you need it. So you could still use watch or broadcast, things like that. And then you can inject your scope. But it becomes more of a dependency when you need it like everything else. It’s not just a given “Oh create a Controller and you have to create. I inject you with scope.” So for those reasons, I think it’s a lot cleaner to use. We’ve shifted to this in our projects and I would encourage doing the same. Does anybody have any questions about this before we move on with it?

Audience: So in the case where you do need to do something with the scope, you go ahead and inject it when you assign all the properties to scope instead of this?

John: No I would still inject scope. Let’s say I need to watch something. Let me just do a scope.watch. And here’s where you would normally do say message and give it some sort of function. Obviously it’s not going to find message because it’s looking on the scope. So instead of that, you’ve given a function. So that’s how.

Audience: So that’s where it kind of goes in the opposite direction. Now this specific part of it is much uglier.

John: This is a lot uglier. If we go into depth about this, you probably wouldn’t use watches as much. You’d do more in the services and you don’t throw as many watches in there just because you can. You’re going to do more in the services and not necessarily watching the service but…

Audience: You still can use string for the watch but it has to be main.message. Which then–.

John: Oh you can do that?

Audience: Couples the contents of your controller to how things are defined in partial which are the HTML, which may or may not be good. But yeah that will work.

John: That will work? Okay. See and that’s where I think it starts getting a little messier because Main is actually declared out here and you’re preferring to it in here. And that will get even messier. That could be a talk for another day going into how do you scope in addition to this or in combination with this. But I think it also encourages you to use watch less which is a better way to use Angular as well.

Alright so now I’m kind of going on to the meat of the talk. So this is just a sample, small sample project. Let’s say you’re given the task to write a small application which just retrieves a list of projects. When I click on a project, I want it to give a name and description. That’s simple enough to do in Angular.

 

Here’s my Controller. Let me pull up HTML. So the HTML gets kind of long just because I’ve started a lot of good strips in there but really the meat of it is in fact the Angular Controller, we’ve got 35 lines, that’s not a big deal. Let’s say it never stops there. Then somebody comes and says well this is a great screen but we’d also like a little bit more functionality there. So we want to be able to add or remove people from this project. So again not a big change. It’s reasonable if somebody would ask for the same page. But now your Controller is 55 lines long. And your HTML gets even larger. Fifty-five lines, not that big for the JavaScript file but when you’re talking about a single controller, it’s starting to get long really. And especially since we’ve only added two real features here. You can imagine a lot more being added and as you keep adding more panels here, the controller grows more and more out of hand.

This is really a misuse of the controller. What controllers are made to do is to control a single view. And when we kind of look at the screen. I mean this is one layer functionality. This is a separate piece of functionality and this is a separate piece. So really it’s preferable if we give each their own Controller. But you know we started with the small apps, then threw Controllers in it. And now we need to start dividing that up. So the first thing I want to do is start dividing this HTML. So I’ve got everything in one HTML file but I’m going to have three pieces of code I’d like to work on each one of them separately into HTML. So I’m going to grab the— I can’t find it here, the panel that does the details. I’m going to strip it out into a separate file.

The panel that does the people assignment, I’ll strip that out into a different file. I’ve already done this in a different step of my branch here, so you don’t have to watch it all. But if you look now I haven’t done anything with the JavaScript code yet. I just made these two lines down here.

ng-include. It’s going to pull in HTML from a different template. I’d pull that up. Everything still works the same. It’s just grabbing the HTML, putting it straight in there, using the same Controller. Nothing’s changed yet. So obviously what I need to do now is start splitting these controllers up. So the first thing I’m going to do is pull up one of these templates. And I’ve already said I want to give this template some controller.

This is details, so I’m just going to call this projectDetailController as pdc. Now this is still working because it’s a child of our parent controller. But we don’t want it to be a child. We don’t want it to depend on the parent. Sorry. So I want to be able to inject this controller somewhere else. Maybe I want to strip this into a directive later. So anywhere I’m using, I’m preferring the PLC. I don’t want to do that anymore. This is the first step to stripping everything out is to find out where I’m still referring to that Parent Controller and start peeling it into its own Controller. So I can see that I’m going to need access to the project. And I’m going to need the save project function. Sorry here. I get a little confused sometimes. I got tabs on the top and bottom and I want to switch in between each. So I can’t just take project out of the controller and put it in the other because right now project list controller is the only one that knows about my project. So the way I’m going I need something to share which project is selected between all my controllers. I’m going to do that with the service. Coding this is a lot tougher than I thought it was going to be. So I’m just going to just switch to the next branch and show you what I’ve done.

So I’ve created this new service called the Project Service and this is where I’m all starting my project. So back on my Project List Controller. Before I had a getProject, I have a getProject function that what it did was set project a certain project. Just a second. Let me go back. I should explain this first. So getProject is a function on the main list. So when I click on a project, I got two lines here. Normally it would go out into a server call but I don’t have a server set-up. Just passing it to the project and setting that to the project on the floor in which all the controllers can access. But once it starts splitting up the services, I can’t do that anymore. So that’s where I moved it down into the service. So I’m doing the exact same thing but I’m doing it in service. And now I had a simple getter function from this project that returns whenever we select it. So I can set get project to the function and then this stop project is now a reference to this getter function.

Right on my HTML, instead of referencing project ID. Project is now a function, so I just do a function call. Go up to the service, skip the project, bring it back and show it on the screen. I’ve done the same thing in my project detail. I haven’t done it yet. Sorry. I just want to prove that I put all this stuff into a service and my main controller still works. So we can see that I’ve wired this up.

This is still part of the List Controller. This header up here. And I could see that it’s working. I just haven’t wired up these two controllers yet. The other thing I’ve done and just move to another. Add project. You can see here, it’s like an add a new one. And we’re going to move that to the service. So the next thing to do is start wiring up these details.

So I created this new Project Detail Controller which matches the ng-controller on my details HTML. This is very simple because all it’s handling is at the top, name and description a little Save button. So it’s kind of what we went through before. I went through each of these and figured out that I need a project and I need save projects.

Well the same way I referencing the project is a function call. I did that here so this top project is a reference to selective project function on the service and save project is just a reference to the same on the service. So I do believe the save function from the list controller to the service the way a controller can use it. And now we can see that details and description work just as well as they did before. Once you start peeling things out like that, it becomes really simple to do the next step. We’re going to do the exact same thing. For the people controller. I’m going to try coding this one to see how it works. So I’m going to make a controller project people control. I know I’m going to need to inject project service into it because we’re sharing a project as controller as. So if I pull up my people and see I’m going to need this adverse function. I’m going to need a list of available people. I need access to the project obviously and I need to remove function. So that’s where I need to go into my app and start peeling those things out.

First thing we’re going to do. I didn’t really go over this if anybody doesn’t understand why we did part of that equals to this. So that’s the one we start going into. In here, when I’m inside a function inside of this. Inside here, if I’m referring to this .project list it gets a little foggy on what this actually refers to. Does it refer to the controller? I think in this instance it would actually refer to the function. So I used the that variable. So I know I’m talking about this as it applies to the controller.

So anyway, I’d set up the people control. I set up projects so it references to the project and service. I know I need this. The list of people isn’t needed on the list. So I can just cut that out. It’s only going to be needed in this Controller, I can throw it in here. The same with the add remove person. I mean this is a very simple example but it really doesn’t get much harder than this when you’re dividing up controllers. Obviously I’d only need to add a person and remove a person within the context of this people controller so I can move them right in here. Now is there anything else that I missed? I go right to here, I need to use the controller. And then given these different names, it’s not only more enigmatic but I can see that I don’t want to use plc anywhere. So if I had any instances of plc, that’s something I need to change. So I’ve moved available people in my new controller. I moved that person. Project is now function that gets to the projects and the service. And let’s see if this works.

Audience: [inaudible]

John: Huh?

Audience: Should it save on the other bug?

John: Probably. Thank you. Oh HRRP is not defined because I would have run into this if I saw it in the last one. Since I moved in HTTP…

Audience: There is the other controller. There on line 31.

John: There is an HTTP here. So I need to inject HTTP into this controller. It’s all our back-up.

So the good part of this, let me pull up my most recent branch because I know it has all the codes that I want. And now this is completed. So then the best part of this is now when I’d look at this, it’s very easy to manage. I have a small project list controller about 18 lines long. The project detail there’s not much to it. I only got two lines there. If I need to add anything to the screen, which is very likely they’re going to want more than this, but it’s divided up nice and neat, same with the people controller. We’ve got about 20 lines there. My service isn’t that long either. But really I would like to take the service a bit farther and so right now I’m using this to do everything that’s shared between the controllers as well as all the server calls from here too. Its only purpose is to communicate between the controllers, then I have another service that’s responsible for communicating with the server. Just doing things like that, giving each thing its own responsibility makes it easier to add to and easier to manage in the future.

Audience: So why don’t you go into the work of splitting things up and display it, how do you feel about moving to actually separate source files?

John: That’s another thing that I think it depends on how much you have in here. Right now, this is 73-line file. It’s not big of a deal to manage. But yeah once this gets bigger, I would definitely consider moving them into different source files as well. And in addition I don’t like having HTTP calls at my controllers. A controller should really be code-related to your view and showing how things about the view we shouldn’t care about communicating with the server. So like I said before and I would peel this off into a different service, so this would be even smaller. I feel like I went through a lot of that pretty fast. It doesn’t make sense. There’s no questions about it. Obviously like I said before this is a small project, a very small avocation so it’s pretty easy to go through. But you can see if I had seven or eight views on the screen, how that could have been a beast to tackle . So the correct way to do it is to start out by writing this way. And each time you say hey this functionality really isn’t typed to showing you a list, just start with a new controller. More discussion, questions on this?

Audience: About the bar that equals this? A nice side of the views in arrow functions is that for the most part the need for that goes away. So if we look for example at line 30 up there, where you have to use that. no way around it. But if you were using arrow functions for the anonymous function, you guys know what’s going on, then you wouldn’t have to because it doesn’t introduce the new value for this.

John: Yeah that’s something I have to try out.

Audience: Yeah I kind of said I was hoping. I don’t even like…

Audience: So we noticed, when we started doing it this way, that we haven’t been able to dodge just sort of running, in our sense, ghost style almost never having to take a look at the JavaScript, But this Controller As approach, and sort of all the luminaries that you should be using, it is right in your face all the time unless you hook up ES6. It’s sort of like upgrading one thing, now I really want to upgrade the other thing so I can get back the conciseness I had.

John: Okay well…

Audience: How’s your total line count compare? Because it’s like as you tore them apart, that kind of add a lot of code and they were wiring back together. And so for this chunk of functionality, it’d be really interesting to compare the total line count before and after.

John: For this one, I think we actually added about 20 lines of code.

Audience: About 20 lines. How many lines before and after?

John: Out of at 50 or so, it went to about 70 or so. But

Audience: 40%, 40% increase to wire them back up.

John: It is but that’s only for this size. I don’t think it’s going to maintain 40%.

Audience: The thing is if this chunk was bigger, then the wiring would necessarily got to be bigger.

John: Right. You’re still going to do a couple lines of code for each wiring up.

Audience: So it might only cost 20% code size on a more realistic project.

John: Right, you’re right. But we’ve been talking about…

Audience: But do you feel like it’s worth it? Because I’m never sure if it’s worth it. Every time I change codes, I’m never sure if I like it better than…

John: I do just because of the modularity point. I think even if it’s a 20% increase, maintaining a thousand lines of code in one controller or 1,200 lines of codes split up into pieces.

Audience: It’s worth it.

John: It’s worth it.

Audience: I guess I’m thinking too is if you had more controllers on top of this and use that one service and like the code is going to… And 20 lines now might be much bigger over three controllers.

John: That was my presentation, and now I think, we’d wanted to open it up for more of just a…

Published by

Kyle Cordes

The technical principal of Oasis Digital, Kyle Cordes drives our technology and architecture choices. Kyle gives presentations and talks at user groups and other events.