it-swarm.com.de

Wie konfiguriere ich verschiedene Umgebungen in Angular.js?

Wie verwalten Sie Konfigurationsvariablen/Konstanten für verschiedene Umgebungen?

Dies könnte ein Beispiel sein:

Meine restliche API ist unter localhost:7080/myapi/ Erreichbar, aber mein Freund, der mit demselben Code unter Git-Versionskontrolle arbeitet, hat die API auf seinem Tomcat unter localhost:8099/hisapi/ Bereitgestellt.

Angenommen, wir haben so etwas:

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

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

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

Wie füge ich den korrekten Wert des API-Endpunkts je nach Umgebung dynamisch ein?

In PHP) mache ich solche Dinge normalerweise mit einer config.username.xml - Datei, wobei die Basiskonfigurationsdatei (config.xml) mit der Konfigurationsdatei der lokalen Umgebung zusammengeführt wird, die durch den Namen der Datei erkannt wird Aber ich weiß nicht, wie man so etwas in JavaScript verwaltet?

218
rbarilani

Ich bin etwas spät dran, aber wenn du Grunzen verwendest , hatte ich großen Erfolg mit grunt-ng-constant .

Der Konfigurationsabschnitt für ngconstant in meinem Gruntfile.js sieht aus wie

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

Die Aufgaben, die ngconstant verwenden, sehen so aus

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

Also läuft grunt server erzeugt ein config.js Datei in app/scripts/ das sieht aus wie

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

Schließlich erkläre ich die Abhängigkeit von den Modulen, die sie benötigen:

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

Jetzt können meine Konstanten bei Bedarf in Abhängigkeit gesetzt werden. Z.B.,

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

Eine coole Lösung könnte sein, alle umgebungsspezifischen Werte in ein separates angular Modul zu unterteilen, von dem alle anderen Module abhängen:

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

Dann können Ihre Module, die diese Einträge benötigen, eine Abhängigkeit davon deklarieren:

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

Jetzt könntest du über weitere coole Sachen nachdenken:

Das Modul, das die Konfiguration enthält, kann in configuration.js unterteilt werden, die auf Ihrer Seite enthalten sein werden.

Dieses Skript kann einfach von jedem von Ihnen bearbeitet werden, solange Sie diese separate Datei nicht in git einchecken. Es ist jedoch einfacher, die Konfiguration nicht einzuchecken, wenn sie sich in einer separaten Datei befindet. Sie können es auch lokal verzweigen.

Wenn Sie nun über ein Build-System wie ANT oder Maven verfügen, könnten Sie als weitere Schritte einige Platzhalter für die Werte API_END_POINT implementieren, die während der Build-Zeit durch Ihre spezifischen Werte ersetzt werden.

Oder du hast dein configuration_a.js und configuration_b.js und entscheide dich im Backend, welches mit einbezogen werden soll.

75
kfis

Für Gulp Benutzer ist gulp-ng-constant auch nützlich in Kombination mit gulp-concat , event-stream und 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() { });
});

In meinem Konfigurationsordner habe ich diese Dateien:

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

Dann kannst du gulp config --env development und das wird ungefähr so ​​aussehen:

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

Ich habe auch diese Spezifikation:

beforeEach(module('app'));

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

Um dies zu erreichen, empfehle ich Ihnen, das AngularJS Environment Plugin zu verwenden: https://www.npmjs.com/package/angular-environment

Hier ist ein Beispiel:

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

Und dann können Sie die Variablen von Ihren Controllern wie folgt aufrufen:

envService.read('apiUrl');

Ich hoffe es hilft.

Du könntest benutzen lvh.me:9000, um auf Ihre AngularJS-App zuzugreifen, (lvh.me zeigt nur auf 127.0.0.1) und gibt dann einen anderen Endpunkt an, wenn lvh.me ist der Gastgeber:

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

Und dann injiziere den Konfigurationsdienst und benutze Configuration.API überall dort, wo Sie auf die API zugreifen müssen:

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

Ein bisschen klobig, funktioniert aber gut für mich, wenn auch in einer etwas anderen Situation (API-Endpunkte unterscheiden sich in Produktion und Entwicklung).

13
Jure Triglav

Wir könnten auch so etwas machen.

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

    });

})();

Und in Ihrem controller/service Können wir die Abhängigkeit einfügen und die Methode get mit der Eigenschaft aufrufen, auf die zugegriffen werden soll.

(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') würde die URL basierend auf der Host-Umgebung zurückgeben.

5
Thalaivar

Gute Frage!

Eine Lösung könnte darin bestehen, die config.xml-Datei weiter zu verwenden und API-Endpunktinformationen aus dem Backend in Ihrem generierten HTML-Code wie folgt bereitzustellen (Beispiel in PHP):

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

Vielleicht keine schöne Lösung, aber es würde funktionieren.

Eine andere Lösung könnte sein, die API_END_POINT konstanter Wert, wie er in der Produktion sein sollte, und ändern Sie stattdessen nur Ihre hosts-Datei, um diese URL auf Ihre lokale API zu verweisen.

Oder vielleicht eine Lösung mit localStorage für Überschreibungen, wie folgt:

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

Sehr spät zum Thema, aber eine Technik, die ich vor Angular angewendet habe, ist, JSON und die Flexibilität von JS zu nutzen, um dynamisch auf Erfassungsschlüssel zu verweisen und unveräußerliche Fakten der Umgebung zu verwenden (Host-Servername, aktuelle Browsersprache) usw.) als Eingaben zur selektiven Unterscheidung/Bevorzugung von Schlüsselnamen mit Suffix innerhalb einer JSON-Datenstruktur.

Dies bietet nicht nur Kontext für die Implementierungsumgebung (pro OP), sondern auch einen beliebigen Kontext (wie z. B. Sprache), um i18n oder eine andere erforderliche Varianz gleichzeitig und (idealerweise) innerhalb eines einzelnen Konfigurationsmanifests ohne Duplizierung und lesbar offensichtlich bereitzustellen.

IN ÜBER 10 LINIEN Vanilla JS

Übermäßig vereinfachtes, aber klassisches Beispiel: Eine API-Endpunkt-Basis-URL in einer JSON-formatierten Eigenschaftendatei, die je nach Umgebung unterschiedlich ist, wobei (natch) der Host-Server ebenfalls unterschiedlich ist:

    ...
    'svcs': {
        'VER': '2.3',
        '[email protected]': 'http://localhost:9090/',
        '[email protected]': 'https://www.uat.productionwebsite.com:9090/res/',
        '[email protected]': 'https://www.productionwebsite.com:9090/api/res/'
    },
    ...

Ein Schlüssel für die Unterscheidungsfunktion ist einfach der Server-Hostname in der Anforderung.

Dies kann natürlich mit einem zusätzlichen Schlüssel kombiniert werden, der auf den Spracheinstellungen des Benutzers basiert:

    ...
    'app': {
        'NAME': 'Ferry Reservations',
        '[email protected]': 'Réservations de ferry',
        '[email protected]': 'Fähren Reservierungen'
    },
    ...

Der Bereich der Unterscheidung/Präferenz kann auf einzelne Schlüssel (wie oben) beschränkt werden, wobei der "Basis" -Schlüssel nur überschrieben wird, wenn für die Eingaben zur Funktion ein passender Schlüssel + Suffix vorhanden ist - oder eine gesamte Struktur und diese Struktur selbst rekursiv nach übereinstimmenden Diskriminierungs-/Präferenzsuffixen analysiert:

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

Wenn ein Benutzer, der die Produktionswebsite besucht, die Spracheinstellung Deutsch ( de hat, würde die obige Konfiguration folgendermaßen aussehen:

    'help': {
        'BLURB': 'Bitte kontaktieren Sie unseren Kundendienst!!1!',
        'PHONE': '1-800-CUS-TOMR',
        'EMAIL': '[email protected]'
    },

Wie sieht eine solche magische Präferenz-/Diskriminierungs-JSON-Umschreibefunktion aus? Nicht viel:

// prefer(object,suffix|[suffixes]) by/par/durch storsoc
// prefer({ a: 'Apple', [email protected]: '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);

    };
};

In unseren Implementierungen mit Angular und Pre-Angular-Websites bootstrap) ist die Konfiguration anderen Ressourcenaufrufen weit voraus, indem der JSON-Code in einem Self-Angular-Code platziert wird. Ausführen des JS-Abschlusses, einschließlich der prefer () -Funktion, und Eingeben der grundlegenden Eigenschaften von Hostname und Sprachcode (und Akzeptieren eventuell erforderlicher zusätzlicher beliebiger Suffixe):

(function(prefs){ var props = {
    'svcs': {
        'VER': '2.3',
        '[email protected]': 'http://localhost:9090/',
        '[email protected]': 'https://www.uat.productionwebsite.com:9090/res/',
        '[email protected]': '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])  ] );

Eine Pre-Angular-Site verfügt jetzt über ein reduziertes (ohne @ -Suffix) window.app_props , auf das verwiesen wird.

Eine Angular Site kopiert als Bootstrap/Init-Schritt einfach das tote Requisiten-Objekt in $ rootScope und zerstört es (optional) aus dem globalen/Fenster-Bereich

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

anschließend in die für die Verarbeitung Verantwortlichen injiziert werden:

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

oder von Bindungen in Ansichten verwiesen:

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

Vorbehalte? Das @ -Zeichen ist keine gültige JS/JSON-Variablen-/Schlüsselnennung, wurde jedoch bisher akzeptiert. Wenn dies ein Deal-Breaker ist, ersetzen Sie eine beliebige Konvention wie "__" (doppelter Unterstrich), solange Sie sich daran halten.

Die Technik kann serverseitig angewendet und auf Java oder C # portiert werden, Ihre Effizienz/Kompaktheit kann jedoch variieren.

Alternativ könnte die Funktion/Konvention Teil Ihres Front-End-Kompilierungsskripts sein, sodass die vollständige JSON-Umgebung/Sprache niemals über das Netzwerk übertragen wird.

[~ # ~] Update [~ # ~]

Wir haben die Verwendung dieser Technik weiterentwickelt, um mehrere Suffixe für einen Schlüssel zuzulassen, um zu vermeiden, dass Auflistungen verwendet werden müssen (Sie können immer noch so tief wie Sie möchten) und um die Reihenfolge der bevorzugten Suffixe einzuhalten.

Beispiel (siehe auch working jsFiddle ):

var o = { 'a':'Apple', '[email protected]':'Apple-dev', '[email protected]':'pomme',
          'b':'banana', '[email protected]':'banane', '[email protected]&fr':'banane-dev',
          'c':{ 'o':'c-dot-oh', '[email protected]':'c-point-oh' }, '[email protected]': { 'o':'c-dot-oh-dev', '[email protected]':'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 (grundlegende Verwendung) bevorzugt '@dev'-Schlüssel, verwirft alle anderen Schlüssel mit Suffix

3 bevorzugt '@dev' gegenüber '@fr', bevorzugt '@ dev & fr' gegenüber allen anderen

4 (wie 3, bevorzugt jedoch '@fr' gegenüber '@dev')

5 keine bevorzugten Suffixe, löscht ALLE Suffixeigenschaften

Dies wird erreicht, indem jede Suffixeigenschaft bewertet und der Wert einer Suffixeigenschaft zu der Eigenschaft ohne Suffix heraufgestuft wird, wenn die Eigenschaften durchlaufen werden und ein Suffix mit höherer Bewertung gefunden wird.

Einige Effizienzvorteile in dieser Version, einschließlich der Beseitigung der Abhängigkeit von JSON für Deep Copy und nur der Wiederkehr in Objekte, die die Bewertungsrunde in ihrer Tiefe überstehen:

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

Wenn Sie Brunch verwenden, hilft Ihnen das Plugin Constangular , Variablen für verschiedene Umgebungen zu verwalten.

2
jvannistelrooy