Jak skonfigurować różne środowiska w Angular.js?

220

Jak zarządzasz zmiennymi / stałymi konfiguracyjnymi dla różnych środowisk?

To może być przykład:

Mój pozostały interfejs API jest dostępny localhost:7080/myapi/, ale mój przyjaciel, który działa na tym samym kodzie pod kontrolą wersji Git, ma interfejs API wdrożony w swoim Tomcat localhost:8099/hisapi/.

Załóżmy, że mamy coś takiego:

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

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

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

Jak dynamicznie wstrzykiwać poprawną wartość punktu końcowego interfejsu API, w zależności od środowiska?

W PHP zwykle robię takie rzeczy z config.username.xmlplikiem, łącząc podstawowy plik konfiguracyjny (config.xml) z plikiem konfiguracyjnym lokalnego środowiska rozpoznawanym przez nazwę użytkownika. Ale nie wiem, jak zarządzać tego typu rzeczami w JavaScript?

rbarilani
źródło

Odpowiedzi:

209

Jestem trochę spóźniony do wątku, ale jeśli używasz Grunta , odniosłem duży sukces grunt-ng-constant.

W sekcji config dla ngconstantmoich Gruntfile.jswygląda jak

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

ngconstantWyglądają używane zadania

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

Więc uruchomienie grunt serverwygeneruje config.jsplik, app/scripts/który wygląda

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

Na koniec deklaruję zależność od potrzebnych modułów:

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

Teraz moimi stałymi można wstrzykiwać zależność w razie potrzeby. Na przykład,

app.controller('MyController', ['ENV', function( ENV ) {
  if( ENV === 'production' ) {
    ...
  }
}]);
André Dion
źródło
10
Zamiast wprowadzenia 'ngconstant:development'w 'serve'- jeśli można umieścić go w konfiguracji zegarka pod 'gruntfile'jak tasks: ['ngconstant:development']- nie będzie konieczne ponowne uruchomienie grunt servepo aktualizacji zmiennych rozwojowych w gruntfile.
spędził
10
Zamiast dodawać swoje stałe do pliku gruntfile.js, możesz umieścić osobne pliki w następujący sposób:package: grunt.file.readJSON('development.json')
Guilhem Soulas
3
Zaktualizowana składnia dla Gruntfile.js w wersji 0.5 programu grunt-ng-constant: github.com/werk85/grunt-ng-constant/issues/31 . Świetna odpowiedź, dzięki!
pherris
10
Dla tych, którzy używają gulp, dostępna jest stała gulp-ng .
Dheeraj Vepakomma
4
Znalazłem Potrzebne jest również dołączenie pliku scripts / config.js do angulara, aby znaleźć moduł, jak poniżej: <script src = "scripts / config.js"> </script>
Toni Gamez
75

Jednym fajnym rozwiązaniem może być rozdzielenie wszystkich wartości specyficznych dla środowiska na osobny moduł kątowy, od którego zależą wszystkie pozostałe moduły:

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

Następnie moduły, które potrzebują tych wpisów, mogą zadeklarować zależność od niego:

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

Teraz możesz pomyśleć o dalszych fajnych rzeczach:

Moduł zawierający konfigurację można podzielić na plik konfiguracyjny.js, który zostanie dołączony na twojej stronie.

Skrypt może być łatwo edytowany przez każdego z was, o ile nie zamieniamy tego osobnego pliku w git. Łatwiej jednak nie sprawdzać konfiguracji, jeśli znajduje się ona w osobnym pliku. Możesz także rozgałęzić go lokalnie.

Teraz, jeśli masz system kompilacji, taki jak ANT lub Maven, twoje dalsze kroki mogą być implementacją niektórych symboli zastępczych dla wartości API_END_POINT, które zostaną zastąpione podczas kompilacji twoimi konkretnymi wartościami.

Lub masz swój configuration_a.jsi configuration_b.jsi decydujesz na zapleczu, które należy uwzględnić.

kfis
źródło
30

Dla użytkowników Gulp przydatna jest również stała gulp-ng w połączeniu z gulp-concat , strumieniem zdarzeń i 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() { });
});

W moim folderze konfiguracji mam te pliki:

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

Następnie możesz uruchomić, gulp config --env developmenta to stworzy coś takiego:

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

Mam również tę specyfikację:

beforeEach(module('app'));

it('loads the config', inject(function(config) {
  expect(config).toBeTruthy();
}));
Rimian
źródło
Czy istnieje sposób na usunięcie tablicy zależności za pomocą stałej gulp ng? Nie mam żadnej zależności od moich stałych, takich jak ty w tym np. „NgAnimate”. Jeśli go nie uwzględnię, otrzymam pustą tablicę zależności jako angular.module („my.module.config”, []), ale chcę, aby dane wyjściowe były jako angular.module („my.module.config”). Nie widzę żadnej opcji w gulp ng stałej, ale widzę, że możesz przekazać deps: false w chrząknięciu stałej paczki. Jakaś pomoc?
Arun Gopalpuri
17

Aby to osiągnąć, sugeruję użycie wtyczki środowiska AngularJS: https://www.npmjs.com/package/angular-environment

Oto przykład:

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

Następnie możesz wywołać zmienne z kontrolerów, takie jak to:

envService.read('apiUrl');

Mam nadzieję, że to pomoże.

juanpablob
źródło
1
jak przełącza się między rozwojem a produkcją?
Mawg mówi o przywróceniu Moniki
Cześć Juan Pablo, lub @Mawg, jeśli to wymyśliłeś. Zanim zadam pytanie dotyczące SO / poruszę problem na Github; jak angular-environmentwykrywa środowisko? tj. co musisz zrobić na lokalnym komputerze / serwerze internetowym, aby wiedzieć, że jest to odpowiednio program / prod?
StevieP,
Ponowne czytanie dokumentów ... „ envServiceProvider.check()... automatycznie ustawi odpowiednie środowisko na podstawie podanych domen”. Sądzę więc, że wykrywa bieżącą domenę i odpowiednio ustawia środowisko - czas na przetestowanie!
StevieP,
13

Możesz użyć, lvh.me:9000aby uzyskać dostęp do aplikacji AngularJS ( lvh.metylko wskazuje 127.0.0.1), a następnie określić inny punkt końcowy, jeśli lvh.mejest to 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/';
  }
});

Następnie wstrzyknij usługę konfiguracji i używaj jej Configuration.APItam, gdzie potrzebujesz dostępu do interfejsu API:

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

Troszkę niezgrabny, ale działa dla mnie dobrze, choć w nieco innej sytuacji (punkty końcowe API różnią się produkcją i rozwojem).

Jure Triglav
źródło
1
więc myślę, że często ludzie często komplikują sprawy. Proste użycie window.location.hostbyło dla mnie więcej niż wystarczające.
joseym
7

Moglibyśmy również zrobić coś takiego.

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

    });

})();

W twoim przypadku controller/servicemożemy wprowadzić zależność i wywołać metodę get z właściwością, do której można uzyskać dostęp.

(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') zwróci adres URL w oparciu o środowisko hosta.

Thalaivar
źródło
5

Dobre pytanie!

Jednym z rozwiązań może być dalsze używanie pliku config.xml i dostarczanie informacji o punkcie końcowym interfejsu API z backendu do wygenerowanego html, tak jak to (przykład w php):

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

Może nie jest to ładne rozwiązanie, ale zadziałałoby.

Innym rozwiązaniem może być utrzymanie API_END_POINTstałej wartości, tak jak powinna być w produkcji, i modyfikowanie pliku hosts tylko w taki sposób, aby wskazywał ten adres URL na lokalny interfejs API.

A może rozwiązanie wykorzystujące localStoragedo zastępowania, takie jak to:

.factory('User',['$resource','API_END_POINT'],function($resource,API_END_POINT){
   var myApi = localStorage.get('myLocalApiOverride');
   return $resource((myApi || API_END_POINT) + 'user');
});
joakimbeng
źródło
Cześć joakimbeng, napisałem rozwiązanie, którego używam w PHP, aby wyjaśnić punkt. Staramy się kodować czystego klienta javascript za pomocą czystego zaplecza RESTful Java, więc miks php / js to nie moja sprawa, a także, gdy piszę w php, zawsze staram się nie mieszać php i js. ale dzięki za odpowiedź. Myślę, że rozwiązanie odpowiedzi @kfis mogłoby działać: plik configuration.js nie jest pod kontrolą wersji, który zawiera moduł konfiguracji. Dzięki takiemu podejściu mogę wstrzykiwać / ładować również inny moduł konfiguracyjny do celów testowych, jeśli to konieczne. Dzięki chłopaki.
rbarilani
@ hal9087 Całkowicie zgadzam się na temat mieszania języków, należy go za wszelką cenę unikać :) Podoba mi się również rozwiązanie configuration.js, będę o tym pamiętać, gdy będę potrzebować czegoś podobnego!
joakimbeng
4

Bardzo późno do wątku, ale techniką, którą stosowałem, przed Angularem, jest wykorzystanie JSON i elastyczności JS do dynamicznego odwoływania się do kluczy kolekcji i używania niezbywalnych faktów dotyczących środowiska (nazwa serwera hosta, aktualny język przeglądarki itp.) jako dane wejściowe do selektywnego rozróżniania / preferowania sufiksowanych nazw kluczy w strukturze danych JSON.

Zapewnia to nie tylko kontekst środowiska wdrażania (na OP), ale dowolny kontekst kontekstu (taki jak język) w celu zapewnienia i18n lub dowolnej innej wymaganej wariancji jednocześnie oraz (najlepiej) w ramach jednego manifestu konfiguracji, bez powielania i oczywiste.

W OKOLICY 10 LINII VANILLA JS

Zbyt uproszczony, ale klasyczny przykład: podstawowy adres URL punktu końcowego interfejsu API w pliku właściwości w formacie JSON, który różni się w zależności od środowiska, w którym (natch) serwer hosta również się zmienia:

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

Kluczem do funkcji dyskryminacji jest po prostu nazwa hosta serwera w żądaniu.

Można to oczywiście połączyć z dodatkowym kluczem w oparciu o ustawienia językowe użytkownika:

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

Zakres dyskryminacji / preferencji można ograniczyć do pojedynczych kluczy (jak wyżej), w których klucz „podstawowy” jest nadpisywany tylko wtedy, gdy istnieje pasujący klucz + sufiks dla danych wejściowych funkcji - lub cała struktura, i sama ta struktura rekursywnie analizowane w celu dopasowania sufiksów dyskryminacji / preferencji:

    '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',
        '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': '[email protected]'
    },

SO, jeśli odwiedzający użytkownik na stronie produkcyjnej ma ustawienie preferencji języka niemieckiego ( de ), powyższa konfiguracja zwinie się do:

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

Jak wygląda taka magiczna preferencja / dyskryminacja funkcja przepisywania JSON? Niewiele:

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

    };
};

W naszych implementacjach, które obejmują witryny Angular i strony sprzed Angular, po prostu ładujemy konfigurację znacznie wcześniej niż inne wywołania zasobów, umieszczając JSON w ramach samowykonywającego się zamknięcia JS, w tym funkcję prefer (), i karmimy podstawowe właściwości nazwy hosta i kod języka (i akceptuje wszelkie dodatkowe dowolne sufiksy, których możesz potrzebować):

(function(prefs){ var props = {
    'svcs': {
        'VER': '2.3',
        'API@localhost': '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])  ] );

Witryna przed Angularem miałaby teraz zwinięte okno (bez przyrostków @). App_props, do którego można się odwoływać.

Witryna Angular, jako krok bootstrap / init, po prostu kopiuje martwy obiekt rekwizytów do $ rootScope i (opcjonalnie) niszczy go z zakresu globalnego / okna

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

do późniejszego wstrzyknięcia do kontrolerów:

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

lub o których mowa w powiązaniach w widokach:

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

Ostrzeżenia? Znak @ nie jest prawidłowym nazewnictwem zmiennej / klucza JS / JSON, ale do tej pory został zaakceptowany. Jeśli jest to przełom, zastąp dowolną konwencję, taką jak „__” (podwójny znak podkreślenia), o ile się jej trzymasz.

Technikę tę można zastosować po stronie serwera, przenieść do Java lub C #, ale wydajność / kompaktowość mogą się różnić.

Alternatywnie, funkcja / konwencja może być częścią skryptu kompilującego frontonu, tak że pełna krwawa JSON dla wszystkich środowisk / dla wszystkich języków nigdy nie jest przesyłana przewodowo.

AKTUALIZACJA

Rozwinęliśmy tę technikę, aby umożliwić stosowanie wielu sufiksów w kluczu, aby uniknąć zmuszania do korzystania z kolekcji (nadal możesz, tak głęboko, jak chcesz), a także aby uszanować kolejność preferowanych sufiksów.

Przykład (patrz także działający 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 (podstawowe użycie) preferuje klucze „@dev”, odrzuca wszystkie pozostałe klucze z przyrostkami

3 woli „@dev” niż „@fr”, woli „@ dev & fr” od wszystkich innych

4 (to samo co 3, ale woli „@fr” niż „@dev”)

5 brak preferowanych sufiksów, upuszcza WSZYSTKIE właściwości sufiksów

Dokonuje tego poprzez ocenianie każdej właściwości z przyrostkiem i promowanie wartości właściwości z przyrostkiem do właściwości bez przyrostka podczas iteracji po właściwościach i znajdowaniu przyrostka z wyższą liczbą punktów.

Niektóre usprawnienia w tej wersji, w tym usunięcie zależności od JSON do głębokiego kopiowania i rekurencja tylko w obiektach, które przetrwają rundę punktacji na ich głębokości:

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());
}
storsoc
źródło
-8

Czy widziałeś to pytanie i jego odpowiedź?

Możesz ustawić globalnie prawidłową wartość dla swojej aplikacji, tak jak to:

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

a następnie użyj go w swoich usługach. Możesz przenieść ten kod do pliku config.js i wykonać go przy ładowaniu strony lub w innym dogodnym momencie.

thalador
źródło
7
Czy ktoś mógłby wyjaśnić, dlaczego jest to tak zła odpowiedź? Zostało to mocno ocenione, ale ani jednego komentarza ...
aendrew
5
Jest to stare jak diabli, ale gdybym musiał zgadywać, dlaczego są to negatywne opinie, to dlatego, że nie rozwiązuje problemu konfiguracji specyficznych dla środowiska, jest to tylko sugestia użycia .value () do ustawienia wartości globalnej w dowolnej starej aplikacji. Nie ma wzmianki o tym, w jaki sposób można by tego użyć w zależności od env lub czegokolwiek w stosunku do oryginalnych parametrów pytań.
coblr