Providing Django template variables as constants to AngularJS
Over the past few weeks I’ve been using AngularJS to rewrite an enterprise-level configuration web UI containing 90 or so fields. Two-way binding was a natural fit and keeping the data model as a javascript object has made the code quite clean and concise. For a while I was struggling with a good way to handle constants in Angular, including variables coming from the server as Django template variables. This is what I came up with as a solution.
First, redefine the interpolation symbols since they conflict with Django’s:
# module.js
angular.module('app.services', []); angular.module('app', ['app.services'], function($interpolateProvider) { $interpolateProvider.startSymbol('{['); $interpolateProvider.endSymbol(']}'); } ); |
Then, create a Constants service which can be injected into controllers on demand. I like the factory() function as it allows you to create private variables as well as exposing a public API:
# services/constants.js
angular.module('app.services') .factory('Constants', function(DjangoConstants) { // define UI constants here var constants = { customSelect: "Custom", keyAudio: "audio" }; // pull in the django constants angular.extend(constants, DjangoConstants); return { get: function(key) { return constants[key]; }, // this is a handy way to make all constants available in your HTML // e.g. $scope.c = Constants.all() all: function() { return constants; } }; }); |
Finally, pass the django constants to the main module via the constant() method:
# snippet of config.html, a django partial
<script src='{{ STATIC_URL }}js/lib/angular/angular.js'></script> <script src="{{ STATIC_URL }}js/config/module.js"></script> <script type="text/javascript"> // inject some constants from django angular.module('app').constant("DjangoConstants", { serverName: '{{ server.host }}', csrfToken: '{{ csrf_token }}' }); </script> |
Usage:
# controllers/input.js
function InputCtrl($scope, Constants) { $scope.c = Constants.all(); $scope.audio = $scope.m.in[Constants.get("keyAudio")]; ... }); |
And voila! You now have a central place to access all constants, with zero global variables!
Trackbacks and Pingbacks
Comments are closed.
DougJune 28, 2012This was sooooo very hepflul but also raised more then a few questions in my mind that maybe you can address. I’m building a website and it has a similar type of structure to what your presentation does. There is a top menu bar that allows a content pane to be loaded into the ng-view directive. Here is where I’m running into complexity / undesireables. The sub pages are very complex and have a lot of angular / javascript / business logic in them. Putting all the controllers into one file seems wasteful because then essentially all logic for all pages is loaded in one load and upfront. If you include the controller / script in the subpages when ng-view loads (which it does correctly) you cannot use Chrome / Firefox to see the additionally loaded scripts. Which makes for harder development.Ideas / Suggestions?
Sounds like the routeProvider would be appropriate: http://docs.angularjs.org/api/ng.$routeProvider
Which basically just maps urls to controllers. Some sort of fiddle makes these things easier to discuss, too.
Thanks for the post. I’m starting out with Angular and Django myself. Just a question really, is that a reason why you decided to created a django constant directive as opposed to simply using ng-init?
Nai,
I hadn’t really explored
ng-init
at the time I devised this solution, but it looks like a decent alternative for using such constants in local templates. However, you wouldn’t be able to use those constants elsewhere in your app; say in a service, controller, or even another template.Here is my take on this problem (ultimately using providers) http://bahmutov.calepin.co/inject-valid-constants-into-angular.html