Em resumo, toda aplicação centralizadora deve possuir um arquivo de configuração responsável por gerenciar o mapeamento de rotas do AngularJS para atender as necessidades da aplicação. As rotas estáticas devem ser adicionadas diretamente ao $stateProvider, as demais views da aplicação irão ser carregadas por exceção através do 'otherwise' do $urlRouterProvider.
Utilizando como base a aplicação de referencia, temos esta configuração definida no arquivo config-states.js. Esta configuração tem alta dependência da estrutura de pastas definida para as aplicações.
Por padrão, temos 2 modos de adicionar uma página a aplicação:
Estático
As páginas estáticas normalmente estão relacionas a aplicação de centralizadora. Normalmente páginas de configuração, informações e outras. Estas páginas podem ser adicionadas diretamente ao $stateProvider do AngularJS por não possuírem estados.
appStateConfig.$inject = ['$stateProvider', '$urlRouterProvider']; function appStateConfig($stateProvider, $urlRouterProvider) { index.stateProvider = $stateProvider; // Views padrões da aplicação que não possuem 'states'. $stateProvider.state( 'about', { url: '/html-app/about', templateUrl: '/html-app/html/about.html' } ).state( 'blank', { url: '/', templateUrl: '/html-app/html/blank.html' } ); ... } // appStateConfig index.config(appStateConfig);
Dinâmico
Ao contrário das páginas estáticas as views dinâmicas possuem diferentes estados e confiam fortemente na estrutura definida para as aplicações. No caso da aplicação de referencia a chamada das views pode ser realizada a outros contextos, permitindo assim segmentar as aplicações. Entretanto, cada segmento da aplicação deverá seguir a estrutura padrão para que seja possível a interação das aplicações convencionais com a centralizadora. Por convenção neste exemplo utilizamos a seguinte especificação:
- <contexto do produto>/<contexto da aplicação>/<contexto da view>/html/<view>/<view>.js
Para possibilitar esta estruturação e carregamento dinâmico, foi utilizado o AngularJS UI Router para permitir sejam feitas views com sub-view. Sendo necessário sobrescrever o $urlRouterProvider.otherwise para contemplar as requisições com base no contexto da aplicação e view.
appStateConfig.$inject = ['$stateProvider', '$urlRouterProvider']; function appStateConfig($stateProvider, $urlRouterProvider) { index.stateProvider = $stateProvider; ... $urlRouterProvider.otherwise(function ($injector, $location) { var view = ''; var contexto = ''; var templateurl = ''; var strs = $location.path().split('/'); if (strs.length > 3) { contexto = strs[1] + '/' + strs[2]; view = contexto + '/' + strs[3]; templateurl = '/' + strs[1] + '/' + strs[2] + '/html/' + strs[3] + '/' + strs[3]; } ... if (view !== "") { requirejs([templateurl + '.js'], function () { var state = $injector.get('$state'); var urlRouter = $injector.get('$urlRouter'); if (state.get(view) == null) { index.stateProvider.state(view, { abstract: true, template: '<ui-view/>' }); index.stateProvider.state(view + '.start', { url: view, templateUrl: templateurl + '.html' }); } if (strs.length > 3) { urlRouter.sync(); } else { state.go(view + '.start'); } }); } else { return '/'; } }); } // appStateConfig index.config(appStateConfig);
Quanto as aplicações convencionais, para cada view é preciso definir um arquivo descritor da hierarquia de estados (<view>.js). Por padrão o framework procura por um 'state' do tipo '.start' para iniciar a view caso não seja especificado um 'state' na própria chamada:
define(['index', '/thf/html-sample/html/country/country-services.js'], function(index) { // Inicializa os states da aplicação. index.stateProvider // Estado pai, a hierarquia de states é feita através do '.', e todo estado novo // tem que ter um estado pai, que nesse caso é abstrato. Este status precisa // apenas de uma template com o elemento <ui-view> para conter os estados filhos. .state('thf/html-sample/country', { abstract: true, template: '<ui-view/>' }) // Estado inicial da tela deve ser o estado pai com o sufixo '.start' este estado // será ativado automaticamente quando a tela for carregada. // // A URL deve ser compatível com a tela inicial. // // No estado também definimos o controller usado na template do estado, e definimos // o nome do controller em 'controllerAs' para ser utilizado na view. // também definimos a template ou templateUrl com o HTML da tela da view. .state('thf/html-sample/country.start', { url:'/thf/html-sample/country/', controller:'framework.country-list.Control', controllerAs: 'controller', templateUrl:'/thf/html-sample/html/country/country.list.html' }) // Note que outros estados também são filhos com sufixos conforme o objetivo da tela, // assim como o padrão da URL, controller e template. .state('thf/html-sample/country.detail', { url:'/thf/html-sample/country/detail/:id', controller:'framework.country-detail.Control', controllerAs: 'controller', templateUrl:'/thf/html-sample/html/country/country.detail.html' }) .state('thf/html-sample/country.edit', { url: '/thf/html-sample/country/edit/:id', controller: 'framework.country-edit.Control', controllerAs: 'controller', templateUrl: '/thf/html-sample/html/country/country.edit.html' }) .state('thf/html-sample/country.new', { url: '/thf/html-sample/country/new', controller: 'framework.country-edit.Control', controllerAs: 'controller', templateUrl: '/thf/html-sample/html/country/country.edit.html' }); });
Observação: Com a utilização do RequireJS é possível separar o carregamento dos estados para a view dos serviços para a mesma. Portanto, ao definir o descritor de estados podemos solicitar que o mesmo carregue os arquivos de serviços para a view (country-services.js), caso o mesmo já não tenha sido carregado, para isto basta adicionar o caminho completo do arquivo na definição de dependências do RequireJS conforme exemplo acima.