Как настроить различные среды в Angular.Джей?


как вы управляете переменными/константами конфигурации для различных сред?

это может быть пример:

мой REST API доступен на localhost:7080/myapi/, но мой друг, который работает с тем же кодом под управлением версии Git, имеет API, развернутый на его Tomcat on localhost:8099/hisapi/.

предположим, что у нас есть что-то вроде этого :

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

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

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

как мне динамически вводить правильное значение конечной точки API, в зависимости от окружающая среда?

в PHP я обычно делаю такие вещи с config.username.xml файл, объединяющий файл базовой конфигурации (config.xml) с файлом конфигурации локальной среды, распознанным по имени пользователя. Но я не знаю, как управлять такими вещами в JavaScript?

10 216

10 ответов:

я немного опоздал к теме, но если вы используете грунт у меня был большой успех с grunt-ng-constant.

раздел config для ngconstant в своем Gruntfile.js выглядит так:

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'
    }
  }
}

задачи, которые используют ngconstant выглядеть

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'
]);

и grunt server создает на app/scripts/ вот такой

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

наконец, я объявляю зависимость от любых необходимых модулей это:

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

теперь мои константы могут быть введены зависимости, где это необходимо. Например,

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

одно классное решение может заключаться в разделении всех значений среды на отдельные угловые модули, от которых зависят все остальные модули:

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

тогда ваши модули, которым нужны эти записи, могут объявить зависимость от него:

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

теперь вы можете подумать о дальнейших интересных вещах:

модуль, который содержит конфигурацию можно разделить в конфигурацию.js, которые будут включены на Вашей странице.

этот скрипт может быть легко редактируется каждым из вас, если вы не проверяете этот отдельный файл в git. Но проще не проверять конфигурацию, если она находится в отдельном файле. Кроме того, вы можете разветвлять его локально.

теперь, если у вас есть система сборки, например ANT или Maven, ваши дальнейшие шаги могут заключаться в реализации некоторых заполнителей для значений API_END_POINT, которые будут заменены во время сборки вашими конкретными значениями.

или у вас есть свой configuration_a.js и configuration_b.js и принять решение в бэкэнд, который нужно включить.

на залпом пользователи gulp-ng-constant также полезно в сочетании с gulp-concat,event-stream и 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() { });
});

в моей папке конфигурации у меня есть эти файлы:

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

затем вы можете запустить gulp config --env development и это создаст что-то вроде этого:

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

у меня также есть эта спецификация:

beforeEach(module('app'));

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

для этого я предлагаю вам использовать плагин AngularJS Environment:https://www.npmjs.com/package/angular-environment

вот пример:

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();
});

и затем, вы можете вызвать переменные из ваших контроллеров, таких как это:

envService.read('apiUrl');

надеюсь, что это помогает.

вы могли бы использовать lvh.me:9000 чтобы получить доступ к приложению AngularJS, (lvh.me просто указывает на 127.0.0.1), а затем указать другую конечную точку, если lvh.me хозяин:

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/';
  }
});

и затем ввести службу конфигурации и использовать Configuration.API везде, где вам нужно получить доступ к API:

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

немного неуклюжий, но отлично работает для меня, хотя и в несколько иной ситуации (конечные точки API отличаются в производстве и разработке).

хороший вопрос!

одним из решений может быть продолжение использования вашей конфигурации.xml-файл и предоставить информацию о конечной точке api из бэкэнда в созданный html, например (пример в php):

<script type="text/javascript">
angular.module('YourApp').constant('API_END_POINT', '<?php echo $apiEndPointFromBackend; ?>');
</script>

может быть, не очень красивое решение, но это будет работать.

другое решение может быть сохранить API_END_POINT постоянное значение, как это должно быть в производстве, и только изменить hosts-файл, чтобы указать этот url-адрес для вашего локального api вместо этого.

или, может быть, решение с помощью localStorage для переопределений, например:

.factory('User',['$resource','API_END_POINT'],function($resource,API_END_POINT){
   var myApi = localStorage.get('myLocalApiOverride');
   return $resource((myApi || API_END_POINT) + 'user');
});

мы также могли бы сделать нечто подобное.

(function(){
    'use strict';

    angular.module('app').service('env', function env() {

        var _environments = {
            local: {
                host: 'localhost:3000',
                config: {
                    apiroot: 'http://localhost:3000'
                }
            },
            dev: {
                host: 'dev.com',
                config: {
                    apiroot: 'http://localhost:3000'
                }
            },
            test: {
                host: 'test.com',
                config: {
                    apiroot: 'http://localhost:3000'
                }
            },
            stage: {
                host: 'stage.com',
                config: {
                apiroot: 'staging'
                }
            },
            prod: {
                host: 'production.com',
                config: {
                    apiroot: 'production'
                }
            }
        },
        _environment;

        return {
            getEnvironment: function(){
                var host = window.location.host;
                if(_environment){
                    return _environment;
                }

                for(var environment in _environments){
                    if(typeof _environments[environment].host && _environments[environment].host == host){
                        _environment = environment;
                        return _environment;
                    }
                }

                return null;
            },
            get: function(property){
                return _environments[this.getEnvironment()].config[property];
            }
        }

    });

})();

и в controller/service, мы можем ввести зависимость и вызвать метод get со свойством для доступа.

(function() {
    'use strict';

    angular.module('app').service('apiService', apiService);

    apiService.$inject = ['configurations', '$q', '$http', 'env'];

    function apiService(config, $q, $http, env) {

        var service = {};
        /* **********APIs **************** */
        service.get = function() {
            return $http.get(env.get('apiroot') + '/api/yourservice');
        };

        return service;
    }

})();

$http.get(env.get('apiroot') вернет url-адрес на основе среды хоста.

очень поздно к потоку, но метод, который я использовал, предварительно угловой, заключается в том, чтобы воспользоваться JSON и гибкостью JS для динамической ссылки на ключи коллекции и использовать неотъемлемые факты среды (имя хост-сервера, текущий язык браузера и т. д.) в качестве входных данных для выборочного различения / предпочтения суффиксных имен ключей в структуре данных JSON.

это обеспечивает не просто контекст развертывания среды (на OP), но любой произвольный контекст (например, язык), чтобы обеспечить i18n или любое другое отклонение, необходимое одновременно и (в идеале) в пределах одного манифеста конфигурации, без дублирования и читабельно очевидного.

ПРИМЕРНО В 10 СТРОКАХ VANILLA JS

чрезмерно упрощенный, но классический пример: базовый URL-адрес конечной точки API в файле свойств в формате JSON, который зависит от среды, где (natch) хост-сервер также будет отличаться:

    ...
    'svcs': {
        'VER': '2.3',
        'API@localhost': 'http://localhost:9090/',
        'API@www.uat.productionwebsite.com': 'https://www.uat.productionwebsite.com:9090/res/',
        'API@www.productionwebsite.com': 'https://www.productionwebsite.com:9090/api/res/'
    },
    ...

ключом к функции дискриминации является просто имя хоста сервера в запрос.

это, естественно, может быть объединено с дополнительным ключом на основе языковых настроек пользователя:

    ...
    'app': {
        'NAME': 'Ferry Reservations',
        'NAME@fr': 'Réservations de ferry',
        'NAME@de': 'Fähren Reservierungen'
    },
    ...

область дискриминации / предпочтения может быть ограничена отдельными ключами (как указано выше), где" базовый " ключ перезаписывается только в том случае, если есть соответствующий ключ+суффикс для входов в функцию - или вся структура, и эта структура сама рекурсивно анализируется для соответствия дискриминации / предпочтения суффиксы:

    'help': {
        'BLURB': 'This pre-production environment is not supported. Contact Development Team with questions.',
        'PHONE': '808-867-5309',
        'EMAIL': 'coder.jen@lostnumber.com'
    },
    'help@www.productionwebsite.com': {
        'BLURB': 'Please contact Customer Service Center',
        'BLURB@fr': 'S\'il vous plaît communiquer avec notre Centre de service à la clientèle',
        'BLURB@de': 'Bitte kontaktieren Sie unseren Kundendienst!!1!',
        'PHONE': '1-800-CUS-TOMR',
        'EMAIL': 'customer.service@productionwebsite.com'
    },

Итак, если посетитель сайта производства имеет немецкий язык ( de) настройка языковых предпочтений, приведенная выше конфигурация будет свернута:

    'help': {
        'BLURB': 'Bitte kontaktieren Sie unseren Kundendienst!!1!',
        'PHONE': '1-800-CUS-TOMR',
        'EMAIL': 'customer.service@productionwebsite.com'
    },

как выглядит такая магическая функция предпочтения / дискриминации JSON-перезаписи? Не много:

// prefer(object,suffix|[suffixes]) by/par/durch storsoc
// prefer({ a: 'apple', a@env: 'banana', b: 'carrot' },'env') -> { a: 'banana', b: 'carrot' }
function prefer(o,sufs) {
    for (var key in o) {
        if (!o.hasOwnProperty(key)) continue; // skip non-instance props
        if(key.split('@')[1]) { // suffixed!
            // replace root prop with the suffixed prop if among prefs
            if(o[key] && sufs.indexOf(key.split('@')[1]) > -1) o[key.split('@')[0]] = JSON.parse(JSON.stringify(o[key]));

            // and nuke the suffixed prop to tidy up
            delete o[key];

            // continue with root key ...
            key = key.split('@')[0];
        }

        // ... in case it's a collection itself, recurse it!
        if(o[key] && typeof o[key] === 'object') prefer(o[key],sufs);

    };
};

в наших реализациях, которые включают угловые и предугловые веб-сайты, мы просто загружаем конфигурацию намного раньше других вызовов ресурсов размещение JSON в самоисполняющемся закрытии JS, включая функцию prefer (), и подаваемые основные свойства имени хоста и кода языка (и принимает любые дополнительные произвольные суффиксы, которые вам могут понадобиться):

(function(prefs){ var props = {
    'svcs': {
        'VER': '2.3',
        'API@localhost': 'http://localhost:9090/',
        'API@www.uat.productionwebsite.com': 'https://www.uat.productionwebsite.com:9090/res/',
        'API@www.productionwebsite.com': 'https://www.productionwebsite.com:9090/api/res/'
    },
    ...
    /* yadda yadda moar JSON und bisque */

    function prefer(o,sufs) {
        // body of prefer function, broken for e.g.
    };

    // convert string and comma-separated-string to array .. and process it
    prefs = [].concat( ( prefs.split ? prefs.split(',') : prefs ) || []);
    prefer(props,prefs);
    window.app_props = JSON.parse(JSON.stringify(props));
})([location.hostname, ((window.navigator.userLanguage || window.navigator.language).split('-')[0])  ] );

предварительно угловой сайт теперь будет иметь свернутый (без @ суффиксных ключей)

Если вы используете поздний завтрак плагин Constangular позволяет управлять переменными для различных сред.

вы видели это вопрос и ответ на него?

вы можете установить глобально допустимое значение для вашего приложения следующим образом:

app.value('key', 'value');

а затем использовать его в своих услугах. Вы можете переместить этот код в конфигурацию.js файл и выполнить его на странице загрузки или другой удобный момент.