At the moment I have an app that has a sidebar, and the sidebar loads different html templates using ng-include
based on what operation the user chooses to do. It's a map related app, so for example, if the user selects the 'Add Leg' button it will load the add_leg.html
template into the sidebar using ng-include
:
// The Javascript code:
$scope.addLeg = function() {
$scope.sidebar = true;
$scope.sidebar_template = '/partials/legs/add_leg.html';
}
// Simplified example of HTML:
<html>
<div ng-app="MyApp" ng-controller="MainCtrl">
<a ng-click="addLeg()">Add Leg</a>
<a ng-click="addRoute()">Add Route</a>
<a ng-click="editLeg()">Edit Leg</a>
<a ng-click="editRoute()">Edit Route</a>
...
<div id="sidebar" ng-class="{'sidebar-hidden': sidebar == false}">
<div ng-include src="sidebar_template"></div>
</div>
<google-map></google-map>
</div>
This is all well and good and works as desired, but at the moment my app is only using one controller (MainCtrl
in js/controllers.js
) and it's starting to get really cluttered. I've got a lot of methods now because the apps functionality is expanding. I'd like to split my controller up into multiple controllers whilst retaining this sidebar functionality.
I want to have MainCtrl
as the main controller that controls the loading of the sidebar template (by changing the sidebar_template
variable that points to the file destination), and I want it to control some of the global map related methods (like fetching suburb names from coordinates, etc).
I've tried to split it like so:
controllers/js/main.js - MainCtrl
controllers/js/legs.js - LegCtrl
controllers/js/routes.js - RouteCtrl
I want the LegCtrl
and RouteCtrl
to inherit the MainCtrl
so I can access its scope and methods, that's all fine. But now the problem is how do I dynamically load the controller onto the sidebar div based on what functionality is required. Originally all of my methods were in MainCtrl
, and that's on the wrapper div that surrounds the sidebar div (see above again for an example), so it wasn't a problem.
For example, say I press the 'Add Leg' button, it's going to need to call addLeg
in LegCtrl
, but LegCtrl
isn't loaded on the app, so it doesn't have access to the method. I could keep addLeg()
inside the MainCtrl
, and have it change the sidebar_template
variable to load the template, but nothing in the template will work because it is calling methods from inside the LegCtrl
now.
I need to somehow dynamically load the controller on the sidebar's ng-include
div, something like this perhaps:
// MainCtrl
$scope.addLeg = function() {
$scope.required_controller = LegCtrl;
$scope.sidebar = true;
$scope.sidebar_template = '/partials/legs/add_leg.html';
LegCtrl.addLeg();
}
<div id="sidebar" ng-class="{'sidebar-hidden': sidebar == false}">
<div ng-include src="sidebar_template" ng-controller="{{required_controller}}"></div>
</div>
In the non-working example above you can see a possible solution I've thought of, but I need LegCtrl
to be the actual controller function, not an object (for ng-controller
to work). I also need some way to call addLeg
on the LegCtrl
from the MainCtrl.addLeg
(perhaps using broadcast?).
If anyone can point me in the right direction that'd be great. Sorry for the huge post, it needed a bit of explaining to make it coherent. Hopefully it makes sense. Thanks.
Update: I think I've found a solution using a service to act as the navigation control (it will load the relevant templates and broadcast an event to the new controller being dynamically loaded to tell it what function to execute):
http://plnkr.co/edit/Tjyn1OiVvToNntPBoH58?p=preview
Just trying to test this idea out now, but the broadcast .on
doesn't work. I think it's because the broadcast fires before the template loads. How can I fix this? Is this a good solution for what I'm doing?
If i have understood you correctly what you can try would be to create a template view specifically to create a new leg.
From the main controller implement some logic to show the template
$scope.addLeg=function() {
$scope.showAddLeg=true;
}
The AddLeg template view would load the controller and hence provide a mechanism to actually add new leg. The template would look like
<script type="text/ng-template" class="template" id="addLegTemplate">
<div ng-controller='LegsController'>
<!--html for adding a new leg-->
</div>
</script>
You can include this template inside you main html using ng-if + ng-include.
<div ng-if='showAddLeg'><div ng-include='addLegTemplate'/></div>
Basically you can create multiple view and bind to same controller (but instance would differ). In this case the LegsController
can be binded to multiple views to support the complete functionality.
I like this data driven scenario, just manipulate the rows in the templates:
$scope.templates = [
{ name: 'include1.html', url: 'partials/include1.html' }
];
$scope.addTemplate = function(name, url) {
$scope.templates.push({ name: name, url: url });
};
and the markup:
<div ng-repeat="template in templates" ng-include="template.url"></div>
To add or remove views, just modify the templates et voila! If you need more controller code, then you can include a link to the script in the include. I guess there may be complications with binding to data in the partial view itself, but $compile should resolve those.
I've forked your plunkr demo into one that I believe does what you're looking for: http://plnkr.co/edit/whsjBT?p=preview
This demonstrates an event being broadcast from one controller to another AFTER the 2nd controller (LegCtrl
in our example here) is loaded via ng-include
and passing it data.
I used $includeContentLoaded
event from ng-include
to delay broadcasting the event until angular reports that add_leg.html
is loaded. Also I think there were some issues with how you were using $broadcast()
... it's first parameter should be the same as the one used in $on()
in LegCtrl
Another idea to consider is simply using your Navigation
service itself to share state between the controllers if that's appropriate in your scenario.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With