Angular.js에서 다른 환경을 어떻게 구성합니까?


220

다른 환경에 대한 구성 변수 / 상수를 어떻게 관리합니까?

이것은 예가 될 수 있습니다.

내 나머지 API는 사용할 수 localhost:7080/myapi/있지만 Git 버전 제어에서 동일한 코드를 사용하는 친구는 Tomcat에 API를 배포했습니다 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에서 이런 종류의 것을 관리하는 방법을 모르겠습니까?

답변:


209

스레드에 약간 늦었지만 Grunt를 사용하는 경우에 큰 성공을 거두었습니다 grunt-ng-constant.

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하면 다음과 같은 config.js파일 이 생성됩니다.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' ) {
    ...
  }
}]);

10
오히려 퍼팅 이상 'ngconstant:development''serve'당신이 아래 시계의 설정에 넣어 경우 - 'gruntfile'tasks: ['ngconstant:development']당신이 다시 시작해야하지 않습니다 - grunt serve당신이 gruntfile에서 개발 변수를 업데이트 할 때.
보낸

10
: 대신 gruntfile.js에 상수를 추가, 당신은 다음과 같이 별도의 파일에 넣을 수 있습니다package: grunt.file.readJSON('development.json')
GUILHEM Soulas

3
grunt-ng-constant : github.com/werk85/grunt-ng-constant/issues/31 의 0.5 버전에서 Gruntfile.js에 대한 업데이트 된 구문이 있습니다 . 좋은 답변, 감사합니다!
pherris

10
gulp를 사용하는 사람들에게는 gulp-ng-constant가 있습니다.
Dheeraj Vepakomma

4
<script src = "scripts / config.js"> </ script>
Toni Gamez

75

한 가지 멋진 솔루션은 모든 환경 별 값을 별도의 각도 모듈로 분리하는 것입니다.

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

이제 더 멋진 것들에 대해 생각할 수 있습니다.

구성을 포함하는 모듈은 configuration.js로 분리 될 수 있으며 페이지에 포함됩니다.

이 스크립트는 별도의 파일을 git으로 검사하지 않는 한 각자가 쉽게 편집 할 수 있습니다. 그러나 별도의 파일에 있으면 구성을 확인하지 않는 것이 더 쉽습니다. 또한 로컬로 분기 할 수 있습니다.

이제 ANT 또는 Maven과 같은 빌드 시스템을 사용하는 경우 추가 단계에서 API_END_POINT 값에 대한 자리 표시자를 구현할 수 있습니다.이 자리 표시자는 빌드 타임 동안 특정 값으로 대체됩니다.

또는 당신은 당신이 configuration_a.js있고 configuration_b.js포함 할 백엔드에서 결정합니다.


30

들면 꿀꺽의 사용자 단숨에-ng에 상수는 결합 유용 단숨에-CONCAT , 이벤트 스트림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();
}));

gulp ng 상수로 종속성 배열을 제거하는 방법이 있습니까? 예를 들어 "ngAnimate"에서와 같이 상수에 의존하지 않습니다. 포함하지 않으면 빈 종속성 배열이 angular.module ( "my.module.config", [])로 표시되지만 출력을 angular.module ( "my.module.config")로 원합니다. gulp ng 상수에는 옵션이 없지만 grunt ng 상수 패키지에서는 deps : false를 전달할 수 있습니다. 어떤 도움?
Arun Gopalpuri

17

이를 달성하기 위해 AngularJS 환경 플러그인을 사용하는 것이 좋습니다 : 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');

도움이 되길 바랍니다.


1
개발과 생산을 어떻게 바꾸는가?
Mawg에 따르면 Monica Monica는

Juan Juan, 또는 @Mawg를 알아 낸 경우 SO에 대해 질문하기 전에 / Github에서 문제를 제기하십시오. angular-environment환경을 어떻게 감지합니까? 즉, 로컬 컴퓨터 / 웹 서버에서 각각 dev / prod임을 알기 위해 무엇을해야합니까?
StevieP

문서를 다시 읽으면 ... " envServiceProvider.check()... 지정된 도메인을 기반으로 적절한 환경을 자동으로 설정합니다". 따라서 현재 도메인을 감지하고 환경을 적절하게 설정합니다. 테스트 할 시간입니다.
StevieP

13

당신은 사용할 수 있습니다 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 엔드 포인트는 생산 및 개발이 다릅니다).


1
그래서 사람들은 종종 사람들이 지나치게 복잡한 것을 생각합니다. 간단한 사용법 window.location.host만으로 충분했습니다.
joseym

7

우리는 또한 이런 식으로 할 수 있습니다.

(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을 반환합니다.


5

좋은 질문!

한 가지 해결책은 config.xml 파일을 계속 사용하고 백엔드에서 생성 된 html로 api 엔드 포인트 정보를 제공하는 것입니다 (예 : php) :

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

아마도 예쁜 해결책은 아니지만 효과가 있습니다.

또 다른 해결책은 API_END_POINT프로덕션 환경에서와 같이 일정한 값 을 유지하고 해당 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');
});

안녕하세요 joakimbeng, 나는 요점을 설명하기 위해 PHP에서 사용하는 솔루션을 썼습니다. 우리는 순수한 RESTful 자바 백엔드로 순수한 자바 스크립트 클라이언트를 코딩하려고 노력하고 있으므로 php / js 믹스는 내 경우가 아니며 PHP로 쓸 때 항상 PHP와 js가 섞이지 않도록 노력합니다. 그러나 답변 주셔서 감사합니다. @kfis 응답 솔루션이 작동 할 수 있다고 생각합니다. 구성 모듈이 포함 된 버전 제어가 아닌 configuration.js 파일. 이 방법을 사용하면 필요한 경우 테스트 목적으로 다른 구성 모듈을 주입 /로드 할 수 있습니다. 고마워
rbarilani

@ hal9087 나는 믹싱 언어 부분에 대해 완전히 동의합니다. 모든 비용을 피해야합니다 :) configuration.js 솔루션도 좋아합니다. 유사한 것이 필요할 때 명심하십시오!
joakimbeng

4

스레드에 매우 늦었지만, 내가 Angular 이전에 사용한 기술은 JSON과 JS의 유연성을 활용하여 컬렉션 키를 동적으로 참조하고 환경의 믿을 수없는 사실 (호스트 서버 이름, 현재 브라우저 언어)을 사용하는 것입니다 JSON 데이터 구조 내에서 접미사 키 이름을 선택적으로 식별 / 선호하기위한 입력으로 사용됩니다.

이는 배포 환경 컨텍스트 (OP 당)뿐만 아니라 i18n 또는 동시에 필요한 (그리고 이상적으로) 단일 구성 매니페스트 내에서 복제없이 명확하게 읽을 수있는 임의의 컨텍스트 (예 : 언어)를 제공합니다.

10 라인에 대해 바닐라 JS

지나치게 단순하지만 전형적인 예 : JSON 형식의 속성 파일에있는 API 엔드 포인트 기본 URL은 호스트 서버도 (해치) 환경에 따라 다릅니다.

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

    };
};

Angular 및 pre-Angular 웹 사이트를 포함하는 구현에서 prefer () 함수를 포함하여 JSON을 자체 실행 JS 클로저 내에 배치하고 호스트 이름 및 언어 코드 (필요한 추가 임의 접미사 허용) :

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

사전 Angular 사이트는 이제 접미사 (@ 접미사 키 없음) window.app_props 를 참조하도록 축소 했습니다.

부트 스트랩 / 초기 단계 인 Angular 사이트는 단순히 삭제 된 props 객체를 $ rootScope에 복사하고 선택적으로 전역 / 창 범위에서 파기합니다.

app.constant('props',angular.copy(window.app_props || {})).run( function ($rootScope,props) { $rootScope.props = props; delete window.app_props;} );

이후 컨트롤러에 주입됩니다.

app.controller('CtrlApp',function($log,props){ ... } );

또는 뷰의 바인딩에서 참조됩니다.

<span>{{ props.help.blurb }} {{ props.help.email }}</span>

주의 사항? @ 문자는 유효한 JS / JSON 변수 / 키 이름이 아니지만 지금까지 허용됩니다. 그것이 거래를 깨뜨리는 사람이라면 "__"(이중 밑줄)과 같이 원하는 규칙을 사용하십시오.

이 기술은 서버 측에 적용 할 수 있으며 Java 또는 C #으로 이식 될 수 있지만 효율성 / 컴팩트 성은 다를 수 있습니다.

또는 함수 / 컨벤션이 프론트 엔드 컴파일 스크립트의 일부가 될 수 있으므로 전체 환경 / 모든 언어 JSON이 유선을 통해 전송되지 않습니다.

최신 정보

우리는이 기술의 사용을 키에 여러 접미사를 허용하고, 컬렉션을 강제로 사용하지 않도록하고 (여전히 원하는만큼 깊게 할 수 있음) 선호하는 접미사의 순서를 따르도록 진화했습니다.

예 (또한 작동하는 jsFiddle 참조 ) :

var o = { 'a':'apple', 'a@dev':'apple-dev', 'a@fr':'pomme',
          'b':'banana', 'b@fr':'banane', 'b@dev&fr':'banane-dev',
          'c':{ 'o':'c-dot-oh', 'o@fr':'c-point-oh' }, 'c@dev': { 'o':'c-dot-oh-dev', 'o@fr':'c-point-oh-dev' } };

/*1*/ prefer(o,'dev');        // { a:'apple-dev', b:'banana',     c:{o:'c-dot-oh-dev'}   }
/*2*/ prefer(o,'fr');         // { a:'pomme',     b:'banane',     c:{o:'c-point-oh'}     }
/*3*/ prefer(o,'dev,fr');     // { a:'apple-dev', b:'banane-dev', c:{o:'c-point-oh-dev'} }
/*4*/ prefer(o,['fr','dev']); // { a:'pomme',     b:'banane-dev', c:{o:'c-point-oh-dev'} }
/*5*/ prefer(o);              // { a:'apple',     b:'banana',     c:{o:'c-dot-oh'}       }

1/2 (기본 사용법)은 '@dev'키를 선호하고 다른 모든 접미사 키는 버립니다.

3 은 '@fr'보다 '@dev'를 선호하고 다른 모든 것보다 '@ dev & fr'을 선호합니다

4 (3과 동일하지만 '@dev'보다 '@fr'을 선호 함)

5 선호하는 접미사 없음, 모든 접미사 속성 삭제

각 접미사 속성의 점수를 매기고 속성을 반복하고 더 높은 점수의 접미사를 찾을 때 접미사 속성의 값을 접미사가없는 속성으로 승격시켜이를 수행합니다.

이 버전에서는 딥 카피에 대한 JSON 의존성을 제거하고 깊이에서 스코어링 라운드에서 살아남는 객체로만 되풀이하는 등 일부 버전의 효율성 :

function prefer(obj,suf) {
    function pr(o,s) {
        for (var p in o) {
            if (!o.hasOwnProperty(p) || !p.split('@')[1] || p.split('@@')[1] ) continue; // ignore: proto-prop OR not-suffixed OR temp prop score
            var b = p.split('@')[0]; // base prop name
            if(!!!o['@@'+b]) o['@@'+b] = 0; // +score placeholder
            var ps = p.split('@')[1].split('&'); // array of property suffixes
            var sc = 0; var v = 0; // reset (running)score and value
            while(ps.length) {
                // suffix value: index(of found suffix in prefs)^10
                v = Math.floor(Math.pow(10,s.indexOf(ps.pop())));
                if(!v) { sc = 0; break; } // found suf NOT in prefs, zero score (delete later)
                sc += v;
            }
            if(sc > o['@@'+b]) { o['@@'+b] = sc; o[b] = o[p]; } // hi-score! promote to base prop
            delete o[p];
        }
        for (var p in o) if(p.split('@@')[1]) delete o[p]; // remove scores
        for (var p in o) if(typeof o[p] === 'object') pr(o[p],s); // recurse surviving objs
    }
    if( typeof obj !== 'object' ) return; // validate
    suf = ( (suf || suf === 0 ) && ( suf.length || suf === parseFloat(suf) ) ? suf.toString().split(',') : []); // array|string|number|comma-separated-string -> array-of-strings
    pr(obj,suf.reverse());
}


-8

질문 과 답변을 보셨습니까 ?

다음과 같이 앱에 전역 적으로 유효한 값을 설정할 수 있습니다.

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

그런 다음 서비스에서 사용하십시오. 이 코드를 config.js 파일로 옮기고 페이지로드 나 다른 편리한 순간에 실행할 수 있습니다.


7
누군가 이것이 왜 그렇게 나쁜 대답인지 설명해 주시겠습니까? 그것은 엄청나게
축소

5
이것은 지옥처럼 오래되었지만, 왜 다운 보트를 추측해야한다면 환경 특정 구성의 문제를 해결하지 못하기 때문에 .value ()를 사용하여 오래된 앱에서 전역 값을 설정하는 것이 좋습니다. env 또는 원래 질문 매개 변수에 따라 무엇을 사용하는지에 대한 언급은 없습니다.
coblr
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.