Wednesday, April 13, 2016

Project setup in Angular 1.x

Since 6 months now I have been working on an Angular 1.x project. In the start I have been struggling with the setup of the project, but I found a good way to start with the Angular 1 Style guide of John Papa. Combined with an module loader I optimized this setup.

Below you will find an example how to structure your application. The examples I will provide will use es6, but the concept behind it can easily be used with module loaders like commonJS and requireJS. In these cases just the imports of the files and the export of the file it self is handle a bit different.

Structure

/app
   /feature
       /subFeature
            component.js
            directive.js
            constants.js
            index.js
            subFeature.module.js
       controller.js
       component.js
       service.js
       feature.module.js
       feature.routes.js
       index.js
   myApp.js
   myApp.module.js
   index.js

As you can see in every folder structure I follow the same pattern. I have the following files:

  • index.js
    • The entry point of the folder
  • *.module.js
    • The definition of the angular module
  • *.routes.js
    • The routing definitions
  • other files
    • Controllers
    • Directives
    • Components
    • Services
    • constants

    • ….

*.module.js

The *.module.js file is used to define the angular module. In here we import angular and all the modules on which the module should depend on. If we are adding internal dependencies, we can do this by importing the index file of that module. This index file will init the module and expose the name of the module. (example: feature.module.js)

The module it self is exported so we can use it when we want to add services, controllers, components, … to it.

subFeature.module.js

   1: import angular from "angular";
   2: // import external dependencies necessary in the module
   3:  
   4: export default angular.module("myApp.feature.subFeature", []);

feature.module.js

   1: import angular from "angular";
   2: import subFeatureModule from "./subFeature/index";
   3: // import external dependencies necessary in the module.
   4:  
   5: export default angular.module("myApp.feature", [subFeatureModule]);

myApp.module.js

   1: import angular from "angular";
   2: import featureModule from "./feature/index";
   3: import "angular-route";
   4: // import external dependencies necessary in the module.
   5:  
   6: export default angular.module("myApp", [featureModule, "ngRoute"]);

*.routes.js

The *.routers.js is used to define the routing in your app for the feature. If you are using the new component router, this isn’t necessary because the routes will be defined inside the components.

   1: import FeatureModule from "./feature.module";
   2:  
   3: FeatureModule.config(function($routeProvider, $locationProvider) {
   4:   $routeProvider
   5:   .when('/Feature', {
   6:     templateUrl: 'controller.html',
   7:     controller: 'Controller'
   8:   });
   9: });

index.js

The index.js file is the entry file for the folder. In here we import all the components, controllers, services, … we want to add to the module. We also import the *.module.js file so we can expose the module name. This way we can easily use it to add the module as a dependency in another module.

The name of this file is irrelevant, you can also use something like init. The reason I use index.js is because this is the default entry point of a folder in webpack.

subfeature/index.js

   1: import "./component";
   2: import "./constant";
   3: import "./directive"
   4: import SubFeatureModule from "./subFeature.module";
   5:  
   6: export default SubFeatureModule.name;

feature/index.js

   1: import "./component"
   2: import "./controller";
   3: import "./service";
   4: import "./feature.routes";
   5: import featureModule from "./feature.module";
   6:  
   7: export default featureModule.name;

app/index.js

   1: import MyAppModule from "./myApp.module";
   2:  
   3: export default MyAppModule.name;

Other files

All the other files like (services, controllers, directives, constants, …) will import the *.module.js file. Every thing else necessary to define the service, controller, directive, … will also be present in this file. This way everything is present on a single place.

subFeature/component.js

   1: import SubFeatureModule from "./subFeature.module";
   2: // other imports
   3:  
   4: ComponentController.$inject = [];
   5:  
   6: SubFeatureModule.component("component", {
   7:     template: "<div></div>",
   8:     controller: ComponentController,
   9: })
  10:  
  11: function ComponentController(){}

subFeature/constant.js

   1: import SubFeatureModule from "./subFeature.module";
   2: // other imports
   3:  
   4: SubFeatureModule.constant("constant", {
   5:     "key": value
   6: })

Conclusion

By defining the component in the *.module.js file we have a single point where we can find the definition of the module. By exposing the module we also create a single point where we keep the name of the module. You no longer need to use the name of the module if you want to add a service/controller/… to it. This eliminates typo’s in the module names.

The index.js provides a single entry point for the module. All the services/controllers/… you want to add to the module should be added in here. Also we import the *.module.js file. This gives us the ability to expose the name of the module. These names can then be use to be added as dependency without having to know it string value. Again this eliminates typo’s in the module names.

In all the other files the definition and resolving of the dependencies can be put together. This downsizes the chance on errors and dependency mismatches

2 comments:

  1. That looks really great. Thank you for posting those materials I can see here. It would be really cool if you'll enlarge this description.

    ReplyDelete
  2. a good and informative article, I'm sure that you could write a separate blog on this topic!

    ReplyDelete