I have here a simple webapp for displaying different D3 demonstrations:
The idea is - the user can select one of many modules in the control bar at the top, and the corresponding controls for that module will be displayed.
The image generated by each module will be displayed in the main area below the control bar.
Essentially - I want to be able to create each module independently, each with their own set of controls and drawing function.
To achieve this, I've created a ng-template script for each module's controller, and a corresponding controller and service for each module.
I then dynamically compile the template when the module is selected - using the technique outlined here.
HTML and Templates
<body ng-app="myapp" ng-controller="myctrl">
<div id="header" class = "flex-right" >
<div class="flex-down" >
<input type="button" class="btn btn-info" ng-repeat="m in modules"
ng-click="switchModule(m)" value="{{m.name}}"></input>
</div>
<div class ="flex-right" >
<dynamic-template template = "currentModule.template"/>
</div>
</div>
<div id="main">
</div>
<script type="text/ng-template" id="SpaceInvaders.html">
<div ng-controller = "SpaceInvadersCtrl" class ="flex-right">
<div class = "slider-container">
<slider
ng-model = "speed"
min = "1"
max = "10"
value = "5"
on-stop-slide = "update()"
orientation ="vertical"/>
</div>
</div>
</script>
<script type="text/ng-template" id="Waves.html">
<button class = "btn">foo</button>
<button class ="btn"> bar</button>
waves (more controls here).
<!--more controller stuff here-->
</script>
</body>
Main Controller
(function(){
"use strict"
var app = angular.module("myapp",['ui.bootstrap', 'ui.bootstrap-slider']);
app.controller("myctrl", function($scope){
$scope.modules = [{
name: "Space Invaders", template: 'SpaceInvaders.html'
},
{
name: "Waves", template: 'Waves.html'
}
];
$scope.switchModule = function(m){
$scope.$broadcast("dj:moduleswitch");
$scope.currentModule = m;
}
$scope.currentModule = $scope.modules[0];
});
}());
Dynamic Template directive
(function() {
"use strict"
angular.module("myapp").directive('dynamicTemplate', function($compile, $templateCache, $timeout) {
var linker = function(scope, element, attrs) {
scope.$on("dj:moduleswitch", function(){
//timeout to make the template compile
//after variables have updated
$timeout(function(){
element.html($templateCache.get(scope.template));
$compile(element.contents())(scope);
});
});
element.html($templateCache.get(scope.template));
$compile(element.contents())(scope);
};
return {
restrict : "E",
link : linker,
scope : {
template : '='
}
};
})
}());
Example module service and controller
(function() {
"use strict"
angular.module("myapp").service("SpaceInvaders", function() {
this.execute = function(speed) {
d3.select("#main").selectAll("*").remove();
//snip for brevity -
//but all logic pertaining to actually drawing the thing
});
})
.controller("SpaceInvadersCtrl", function($scope, SpaceInvaders){
$scope.speed = 5;
$scope.update =function(){
SpaceInvaders.execute($scope.speed);
};
});
;
}());
My Questions
The main thing I'm concerned about is the technique I've used for the solution - declaring a template, passing and passing it to the directive for runtime compile.
The implementation of the directive seems a little messy - with that
$broadcast
required from the main controller, and the$timeout
required to get the compilation to occur after selected module has changed.How do I name broadcasts?
In my main controller, I need to know the names of corresponding templates for each module.
In each of my d3 modules, they'll all need to know the id of the drawing area -
#main
.