Búsqueda de sitios web

javascript - ¿Cómo configuro diferentes entornos en Angular.js?


¿Cómo gestionas las variables/constantes de configuración para diferentes entornos?

Esto podría ser un ejemplo:

Mi API REST es accesible en localhost:7080/myapi/, pero mi amigo que trabaja en el mismo código bajo control de versiones Git tiene la API desplegada en su Tomcat en localhost:8099/hisapi/.

Suponiendo que tengamos algo como esto:

angular
    .module('app', ['ngResource'])

    .constant('API_END_POINT','<local_end_point>')

    .factory('User', function($resource, API_END_POINT) {
        return $resource(API_END_POINT + 'user');
    });

¿Cómo puedo inyectar dinámicamente el valor correcto del endpoint de la API, dependiendo del entorno?

En PHP normalmente hago este tipo de cosas con un archivo config.username.xml, combinando el archivo de configuración básico (config.xml) con el archivo de configuración del entorno local reconocido por el nombre del usuario. Pero no sé cómo manejar este tipo de cosas en JavaScript.

Respuesta #1

Llego un poco tarde al hilo, pero si estás usando Grunt, he tenido mucho éxito con grunt-ng-constant.

La sección de configuración para ngconstant en mi Gruntfile.js se ve así

ngconstant: {
  options: {
    name: 'config',
    wrap: '"use strict";\n\n{%= __ngModule %}',
    space: '  '
  },
  development: {
    options: {
      dest: '<%= yeoman.app %>/scripts/config.js'
    },
    constants: {
      ENV: 'development'
    }
  },
  production: {
    options: {
      dest: '<%= yeoman.dist %>/scripts/config.js'
    },
    constants: {
      ENV: 'production'
    }
  }
}

Las tareas que usan ngconstant se ven así

grunt.registerTask('server', function (target) {
  if (target === 'dist') {
    return grunt.task.run([
      'build',
      'open',
      'connect:dist:keepalive'
    ]);
  }

  grunt.task.run([
    'clean:server',
    'ngconstant:development',
    'concurrent:server',
    'connect:livereload',
    'open',
    'watch'
  ]);
});

grunt.registerTask('build', [
  'clean:dist',
  'ngconstant:production',
  'useminPrepare',
  'concurrent:dist',
  'concat',
  'copy',
  'cdnify',
  'ngmin',
  'cssmin',
  'uglify',
  'rev',
  'usemin'
]);

Así que ejecutar grunt server generará un archivo config.js en app/scripts/ que se ve así

"use strict";
angular.module("config", []).constant("ENV", "development");

Finalmente, declaro la dependencia de los módulos que lo necesiten:

// the 'config' dependency is generated via grunt
var app = angular.module('myApp', [ 'config' ]);

Ahora mis constantes pueden ser inyectadas como dependencias donde se necesiten. Por ejemplo,

app.controller('MyController', ['ENV', function( ENV ) {
  if( ENV === 'production' ) {
    ...
  }
}]);

Respuesta #2

Una solución interesante podría ser separar todos los valores específicos del entorno en algún módulo angular separado, del que dependan todos los demás módulos:

angular.module('configuration', [])
       .constant('API_END_POINT','123456')
       .constant('HOST','localhost');

Entonces tus módulos que necesitan esas entradas pueden declarar una dependencia de ellas:

angular.module('services',['configuration'])
       .factory('User',['$resource','API_END_POINT'],function($resource,API_END_POINT){
           return $resource(API_END_POINT + 'user');
       });

Ahora podrías pensar en cosas geniales adicionales:

El módulo, que contiene la configuración, puede ser separado en configuration.js, que será incluido en tu página.

Este guion puede ser editado fácilmente por cada uno de ustedes, siempre y cuando no suban este archivo separado a git. Pero es más fácil no subir la configuración si está en un archivo separado. Además, podrían hacer una rama localmente.

Ahora, si tienes un sistema de construcción, como ANT o Maven, tus siguientes pasos podrían ser implementar algunos marcadores de posición para los valores API_END_POINT, que serán reemplazados durante el tiempo de construcción, con tus valores específicos.

O tienes tu configuration_a.js y configuration_b.js y decides en el backend cuál incluir.

Respuesta #3

Para los usuarios de Gulp, gulp-ng-constant también es útil combinado con gulp-concat, event-stream y yargs.

var concat = require('gulp-concat'),
    es = require('event-stream'),
    gulp = require('gulp'),
    ngConstant = require('gulp-ng-constant'),
    argv = require('yargs').argv;

var enviroment = argv.env || 'development';

gulp.task('config', function () {
  var config = gulp.src('config/' + enviroment + '.json')
    .pipe(ngConstant({name: 'app.config'}));
  var scripts = gulp.src('js/*');
  return es.merge(config, scripts)
    .pipe(concat('app.js'))
    .pipe(gulp.dest('app/dist'))
    .on('error', function() { });
});

En mi carpeta de configuración tengo estos archivos:

ls -l config
total 8
-rw-r--r--+ 1 .. ci.json
-rw-r--r--+ 1 .. development.json
-rw-r--r--+ 1 .. production.json

Luego puedes ejecutar gulp config --env development y eso creará algo como esto:

angular.module("app.config", [])
.constant("foo", "bar")
.constant("ngConstant", true);

También tengo esta especificación:

beforeEach(module('app'));

it('loads the config', inject(function(config) {
  expect(config).toBeTruthy();
}));

Respuesta #4

Para lograr eso, te sugiero usar el complemento AngularJS Environment: https://www.npmjs.com/package/angular-environment

Aquí hay un ejemplo:

angular.module('yourApp', ['environment']).
config(function(envServiceProvider) {
    // set the domains and variables for each environment 
    envServiceProvider.config({
        domains: {
            development: ['localhost', 'dev.local'],
            production: ['acme.com', 'acme.net', 'acme.org']
            // anotherStage: ['domain1', 'domain2'], 
            // anotherStage: ['domain1', 'domain2'] 
        },
        vars: {
            development: {
                apiUrl: '//localhost/api',
                staticUrl: '//localhost/static'
                // antoherCustomVar: 'lorem', 
                // antoherCustomVar: 'ipsum' 
            },
            production: {
                apiUrl: '//api.acme.com/v2',
                staticUrl: '//static.acme.com'
                // antoherCustomVar: 'lorem', 
                // antoherCustomVar: 'ipsum' 
            }
            // anotherStage: { 
            //  customVar: 'lorem', 
            //  customVar: 'ipsum' 
            // } 
        }
    });

    // run the environment check, so the comprobation is made 
    // before controllers and services are built 
    envServiceProvider.check();
});

Y luego, puedes llamar a las variables desde tus controladores de esta manera:

envService.read('apiUrl');

Espero que ayude.

Respuesta #5

Podrías usar lvh.me:9000 para acceder a tu aplicación AngularJS, (lvh.me simplemente apunta a 127.0.0.1) y luego especificar un endpoint diferente si lvh.me es el host:

app.service("Configuration", function() {
  if (window.location.host.match(/lvh\.me/)) {
    return this.API = 'http://localhost\\:7080/myapi/';
  } else {
    return this.API = 'http://localhost\\:8099/hisapi/';
  }
});

Y luego inyecta el servicio de Configuración y usa Configuration.API donde necesites acceder a la API:

$resource(Configuration.API + '/endpoint/:id', {
  id: '@id'
});

Un poco torpe, pero funciona bien para mí, aunque en una situación ligeramente diferente (los endpoints de la API difieren en producción y desarrollo).

Artículos relacionados