⬆️ ⬇️

AngularJS - splitting an application into modules and loading components using RequireJS

Using AngularJS paired with RequireJS is a fairly popular approach to developing web applications recently. And one of the main issues is the structure of the application. There is a well-known seed for such an application tnajdek / angular-requirejs-seed , but this does not suit me, because as the functionality of the application increases, this structure will simply be clogged with a bunch of files, there will be no logical separation of the scripts and it will be difficult enough to manage them.



The goal was to create an application with a modular and flexible architecture (well, rather, just splitting the application is not logical parts), with a simple and understandable description of the dependencies between parts of the application and reduce the dependence of the code on the structure of the application.



Module



In this case, it is a logically separate part of the application, which includes a set of components:







')

Problem



When using RequrieJS, application files are most often connected as follows:



require('modules/foo/controller/foo-controller.js'); require('modules/foo/service/foo-service.js'); require('modules/foo/directive/foo-controller.js'); require('text!modules/foo/templates/foo.html'); require('modules/bar/directive/bar-controller.js'); 




There are obvious disadvantages:





Decision



RequireJS plugins for loading module components were written.



For example, there is such an application structure (by the way, very similar to the bundle structure in Symfony2):

 app
    | -modules
    |  | -menu
    |  |  | -controller
    |  |  |  | -menu-controller.js
    |  |  | -menu.js    
    |  |    
    |  | -user
    |  | -controllers
    |  |  | -profile.js
    |  | -resources
    |  |  | -configs
    |  |  |  | -main.js
    |  |  |
    |  |  | -templates
    |  |  |  | -user-profile.html
    |  |  | -directives
    |  |  | -user-menu 
    |  |  | -user-menu.js
    |  |  | -user-menu.html
    |  | -src
    |  |  | -providers
    |  |  |  | -profile-information.js 
    |  |  | -factory
    |  |  | -guest.js
    |  | -user.js
    |
    | -application.js
    | -boot.js




In this case, we have 2 modules: user and menu . The files /app/modules/menu/menu.js and /app/modules/user/user.js are scripts with initialization of angularJS modules. Everything else - I think it is clear.



Now you need to set the configuration for the connection of all components. This is done using requirejs.config :



 requirejs.config({ baseUrl: '/application', paths: { 'text': '../bower_components/requirejs-text/text', // Structure plugins: 'base': '../bower_components/requirejs-angular-loader/src/base', 'template': '../bower_components/requirejs-angular-loader/src/template', 'controller': '../bower_components/requirejs-angular-loader/src/controller', 'service': '../bower_components/requirejs-angular-loader/src/service', 'module': '../bower_components/requirejs-angular-loader/src/module', 'config': '../bower_components/requirejs-angular-loader/src/config', 'directive': '../bower_components/requirejs-angular-loader/src/directive', 'filter': '../bower_components/requirejs-angular-loader/src/filter' }, structure: { prefix: 'modules/{module}', module: { path: '/{module}' }, template: { path: '/resources/views/{template}', }, controller: { path: '/controllers/{controller}' }, service: { path: '/src/{service}' }, config: { path: '/resources/configs/{config}' }, directive: { path: '/resources/directives/{directive}/{directive}' }, filter: { path: '/resources/filters/{filter}' } } }); 




All paths of each component are defined within the module. The field structure.prefix is the path to the module root, after baseUrl .



Now, if we want to include the file /app/modules/user/user.js :

1. /app.js :

 require('module!user') 




2. /app/modules/user/controllers/profile.js :

 require('module!@') 


Within the framework of one module - the name of the module can be omitted; the '@' symbol is enough. Thus, if you have to rename the module, you will not need to change the code.



Now, if we want to include the file /app/modules/user/controllers/profile.js from:

1. /app.js :

 require('controller!user:profile') 


Before the colon - the name of the module, after the colon - the name of the controller.



2. /app/modules/user/user.js :

 require('controller!profile') 


Within the framework of one module - the name of the module can be omitted; it is enough to indicate only the name of the controller. Also, if the controller lies one level lower, then it is possible to connect like this:

 require('controller!additional/path/to/profile') 




Similarly for all other components.



Result



It turned out a very flexible structure of the application with the support of code separation into modules and with minimal code dependence on the project structure even if you have to transfer any component from one module to another - then you will not have to change almost anything. And the excess code also became less.



Also, there are no problems when cutting back the project. In the test application there is an example of an assembled project in the / build folder and the Gruntfile for the build, but there is nothing ordinary in it.



References:





We use this approach in a large corporate application, the support and development of this approach will be supported and developed.

Source: https://habr.com/ru/post/216469/



All Articles