Как настроить различные среды в 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 ответов:
я немного опоздал к теме, но если вы используете грунт у меня был большой успех с
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 файл и выполнить его на странице загрузки или другой удобный момент.