Thursday, January 2, 2014

I figured out how to abstract the AngularJS Seed Project to an AMD shape!

I did not do it alone. I had help from a coworker. Starting here I got rid of the np-app call at the HTML tag which would have cased the application to break by attempting to load Angular too soon. This is vital. I put require.js in the js folder and referenced it like so in the token HTML page:

<!doctype html>
<html lang="en">
<head>
   <meta charset="utf-8">
   <title>My AngularJS App</title>
   <link rel="stylesheet" href="css/app.css"/>
</head>
<body>
   <ul class="menu">
      <li><a href="#/view1">view1</a></li>
      <li><a href="#/view2">view2</a></li>
   </ul>
   <div ng-view></div>
   <div>Angular seed app: v<span app-version></span></div>
   <script src="js/require.js" data-main="js/main.js"></script>
</body>
</html>

 
 

Notice above that all of the scripts tags are now gone (save for the new one). This is the desired effect. Alright, so what is js/main.js then? That is a configuration file for shimming angular.js and angular-route.js and for making sure that angular.js loads before app.js loads. (Note that I copied the "angular" folder out of the "lib" folder and into the "js" folder before just destroying the "lib" folder in the name of easier organization.) Below the deps will specify an array of modules to load and the callback function will only load after the deps at which point the application comes to life. I was missing this critical piece before. I was so close, yet so very far away...

(function() {
   "use strict";
   require.config({
      baseUrl: "js",
      shim: {
         "angular": { exports: 'angular' },
         "angular-route": { deps: ['angular'] }
      },
      paths: {
         "angular": "angular/angular",
         "angular-route": "angular/angular-route"
      },
      deps: ["angular", "app"],
      callback: function(angular) {
         angular.bootstrap(document, ['myApp']);
      }
   });
})();

 
 

Here is how app.js has changed up! A module name such as ngRoute for angular-route.js is not random. I opened up angular-route.js and looked for the name of a module inside. None of the module names are random or invented or attempts to match existing naming conventions. Do not get in that mindset as if trying to apply AMD module conventions to Angular modules. The referenced module names correspond to actual Angular modules kept in the specified files.

define(["angular",
   
/* filters, directives, services, controllers */
   "filters",
   "services",
   "directives",
   "controllers",
   
/* submodules */
   "angular-route"
], function(angular, filters, services, directives, controllers) {
   "use strict";
   var app = angular.module('myApp', ['ngRoute', 'myApp.filters', 'myApp.services',
         'myApp.directives', 'myApp.controllers']).
   config(['$routeProvider', function($routeProvider) {
      $routeProvider.when('/view1', {templateUrl: 'partials/partial1.html', controller: 'MyCtrl1'});
      $routeProvider.when('/view2', {templateUrl: 'partials/partial2.html', controller: 'MyCtrl2'});
      $routeProvider.otherwise({redirectTo: '/view1'});
   }]);
   return app;
});

 
 

The only thing left to mention is that filters.js, services.js, directives.js, and controllers.js all blew up unable to find "angular" until I wrapped their guts in AMD module signatures as seen here:

  1. define(["angular",
    ], function(angular) {
       'use strict';
       angular.module('myApp.filters', []).
          filter('interpolate', ['version', function(version) {
             return function(text) {
                return String(text).replace(/\%VERSION\%/mg, version);
             }
          }]);
    });
     
  2. define(["angular",
    ], function(angular) {
       'use strict';
       angular.module('myApp.services', []).
          value('version', '0.1');
    });
     
  3. define(["angular",
    ], function(angular) {
       'use strict';
       angular.module('myApp.directives', []).
          directive('appVersion', ['version', function(version) {
             return function(scope, elm, attrs) {
                elm.text(version);
             };
          }]);
    });
     
  4. define(["angular",
    ], function(angular) {
       'use strict';
       angular.module('myApp.controllers', []).
          controller('MyCtrl1', [function() {
          }])
          .controller('MyCtrl2', [function() {
          }]);
    });

No comments:

Post a Comment