Jak mogę zadeklarować zmienną globalną w Angular 2 / Typescript? [Zamknięte]

164

Chciałbym niektóre zmienne mają być dostępne wszędzie w sposób Angular 2w Typescriptjęzyku. Jak mam się do tego zabrać?

supercobra
źródło
2
Jeśli są to zmienne statyczne, nie ma potrzeby korzystania z usług. Po prostu dodaj zmienną do jakiegoś pliku, a następnie zaimportuj ją wszędzie tam, gdzie jej potrzebujesz.
Eric Martinez
Niestety Angular2 ma wyjątek w czasie wykonywania, mówiąc Uncaught ReferenceError: Settings is not defined. Klasa Settingsz publicznymi zmiennymi statycznymi jest ustawiona na eksport i została zaimportowana tam, gdzie była używana.
Sen Jacob
Wiem, że ten post jest stary i jest wiele ważnych odpowiedzi. Ale jak wspomniał Eric. Jeśli jest to prosta wartość, którą chcesz zadeklarować i do której masz dostęp w całej aplikacji, możesz utworzyć klasę i wyeksportować ją z właściwością statyczną. Zmienne statyczne są skojarzone z klasą, a nie z instancją klasy. Możesz zaimportować klasę i będziesz mieć dostęp do właściwości z class.directly.
De Wet Ellis

Odpowiedzi:

195

O to najprostsze rozwiązanie w / o Servicenie Observer:

Umieść zmienne globalne w pliku i wyeksportuj je.

//
// ===== File globals.ts    
//
'use strict';

export const sep='/';
export const version: string="22.2.2";    

Aby użyć globals w innym pliku, użyj importinstrukcji: import * as myGlobals from 'globals';

Przykład:

// 
// ===== File heroes.component.ts    
//
import {Component, OnInit} from 'angular2/core';
import {Router} from 'angular2/router';
import {HeroService} from './hero.service';
import {HeroDetailComponent} from './hero-detail.component';
import {Hero} from './hero';
import * as myGlobals from 'globals'; //<==== this one (**Updated**)

export class HeroesComponent implements OnInit {
    public heroes: Hero[];
    public selectedHero: Hero;
    // 
    //
    // Here we access the global var reference.
    //  
    public helloString: string="hello " + myGlobals.sep + " there";

         ...

        }
    }

Dzięki @ eric-martinez

supercobra
źródło
3
Wystąpił błąd w instrukcji importu. musiał użyćimport * as globs from 'globals'
Mike M
13
Dlaczego użyłeś „require” zamiast importowania z?
Ayyash
4
Chciałbym używać export constzamiast export var- Naprawdę chcesz się upewnić, te zmienne globalne nie mogą być zmieniane
Michał Boska
1
Takie importowanie nie jest już prawidłowe w języku TypeScript. Zaktualizuj swoją odpowiedź. Prawidłowe byłoby:import * as myGlobals from 'globals'
Mick
7
import * as myGlobals from './path/to/globals';
Timothy Zorn
89

Podoba mi się też rozwiązanie z @supercobra. Chciałbym tylko trochę to poprawić. Jeśli eksportujesz obiekt, który zawiera wszystkie stałe, możesz po prostu zaimportować moduł za pomocą es6 bez użycia wymagania .

Użyłem również Object.freeze, aby właściwości stały się prawdziwymi stałymi. Jeśli jesteś zainteresowany tematem, możesz przeczytać ten post .

// global.ts

 export const GlobalVariable = Object.freeze({
     BASE_API_URL: 'http://example.com/',
     //... more of your variables
 });

Skorzystaj z modułu za pomocą importu.

//anotherfile.ts that refers to global constants
import { GlobalVariable } from './path/global';

export class HeroService {
    private baseApiUrl = GlobalVariable.BASE_API_URL;

    //... more code
}
Tim Hong
źródło
jest to dla mnie najlepsze rozwiązanie, ponieważ (1) jest najprostsze z najmniejszą ilością kodu i (2) nie wymaga od ciebie wstrzykiwania jakiejś usługi do każdego komponentu lub miejsca, w którym chcesz go użyć, ani też nie wymagają zarejestrowania go w @NgModule. Przez całe życie nie mogę zrozumieć, dlaczego konieczne byłoby stworzenie usługi Angular 2, aby to zrobić, ale może jest coś, co przeoczę? Na razie używam tego wspaniałego rozwiązania, ale proszę, daj mi znać, dlaczego inne, bardziej skomplikowane odpowiedzi są lepsze?
FireDragon
8
Twoja zmienna GlobalVariable nie jest zmienną. To stała.
Priya R
@PriyaR LOL, tak, masz rację. Założyłem, że głównym celem pytania jest zapewnienie bezpiecznego dostępu do niektórych wartości na całym świecie, więc improwizowałem. W przeciwnym razie możesz zmienić const na var, otrzymasz zmienną.
Tim Hong
Wadą Object.freeze jest to, że wartości nie są wpisywane. W każdym razie zawijanie wartości w klasę to lepszy projekt z mojej perspektywy. Musimy więc wybrać między właściwościami wpisanymi a prawdziwymi stałymi.
Harps
jak ustawić GlobalVariable.BASE_API_URL w innym komponencie ..?
Sunil Chaudhry
59

Wspólna usługa to najlepsze podejście

export class SharedService {
  globalVar:string;
}

Ale musisz być bardzo ostrożny podczas rejestracji, aby móc współużytkować jedną instancję dla całej aplikacji. Musisz to zdefiniować podczas rejestracji aplikacji:

bootstrap(AppComponent, [SharedService]);

ale nie po to, aby zdefiniować go ponownie w providersatrybutach komponentów:

@Component({
  (...)
  providers: [ SharedService ], // No
  (...)
})

W przeciwnym razie dla komponentu i jego składników podrzędnych zostanie utworzone nowe wystąpienie usługi.

Możesz rzucić okiem na to pytanie dotyczące działania iniekcji zależności i hierarchicznych wtryskiwaczy w Angular2:

Możesz zauważyć, że możesz również zdefiniować Observablewłaściwości w usłudze, aby powiadomić części aplikacji o zmianie właściwości globalnych:

export class SharedService {
  globalVar:string;
  globalVarUpdate:Observable<string>;
  globalVarObserver:Observer;

  constructor() {
    this.globalVarUpdate = Observable.create((observer:Observer) => {
      this.globalVarObserver = observer;
    });
  }

  updateGlobalVar(newValue:string) {
    this.globalVar = newValue;
    this.globalVarObserver.next(this.globalVar);
  }
}

Zobacz to pytanie, aby uzyskać więcej informacji:

Thierry Templier
źródło
Wygląda na to, że ten jest inny. Wydaje się, że @ Rat2000 uważa, że ​​nasza odpowiedź jest błędna. Zwykle pozostawiam tę decyzję innym niż osoba udzielająca konkurencyjnych odpowiedzi, ale jeśli jest przekonany, że nasze odpowiedzi są błędne, myślę, że jest to uzasadnione. Doktorzy, z którymi łączył się w komentarzu do mojej odpowiedzi, wspominają, że jest ZRÓWNOWAŻONY, ale nie widzę żadnej wady, a argumenty w dokumentach są dość słabe. Dość często dodaje się dostawców do bootstrapu. Jaki byłby w ogóle cel tego argumentu. A co z HTTP_PROVIDERSpodobnymi, czy też nie powinno się ich dodawać bootstrap()?
Günter Zöchbauer
2
Tak, właśnie przeczytałem argument i sekcję w dokumencie. Szczerze mówiąc, naprawdę nie rozumiem, dlaczego to zniechęca do doktora. Czy jest to sposób na zdefiniowanie logicznego podziału: co jest specyficzne dla rdzenia Angular2 (dostawcy routingu, dostawcy http) podczas ładowania początkowego i co jest specyficzne dla aplikacji w iniektorze komponentów aplikacji. To powiedziawszy, możemy mieć tylko jeden wtryskiwacz pomocniczy (aplikacyjny) dla głównego (zdefiniowany podczas ładowania początkowego). Czy coś mi brakuje? Ponadto w dokumencie dotyczącym wtryskiwaczy hierarchicznych, dostawcy usług są zdefiniowani w ramach wtryskiwacza głównego ;-)
Thierry Templier
3
Jedynym argumentem, jaki widzę, jest to, że utrzymanie zakresu tak wąskiego, jak to tylko możliwe, i użycie składnika głównego jest przynajmniej w teorii nieco węższe niż użycie, bootstrap()ale w praktyce nie ma to znaczenia. Myślę, że umieszczenie ich na boostrap()liście ułatwia zrozumienie kodu. Komponent ma dostawców, dyrektywy, szablon. Uważam, że jest to przeciążone bez globalnych dostawców tam również. Dlatego wolę bootstrap().
Günter Zöchbauer
2
i jak można odwołać się do takich zmiennych globalnych? nawet po załadowaniu usługi wywołanie alert(globalVar)skutkuje błędem.
phil294,
Jeszcze tego nie próbowałem, ale będziesz chciał coś takiego: alert (this.SharedService.globalVar)
trees_are_great
39

Zobacz na przykład Angular 2 - Implementacja usług wspólnych

@Injectable() 
export class MyGlobals {
  readonly myConfigValue:string = 'abc';
}

@NgModule({
  providers: [MyGlobals],
  ...
})

class MyComponent {
  constructor(private myGlobals:MyGlobals) {
    console.log(myGlobals.myConfigValue);
  }
}

lub podaj indywidualne wartości

@NgModule({
  providers: [{provide: 'myConfigValue', useValue: 'abc'}],
  ...
})

class MyComponent {
  constructor(@Inject('myConfigValue') private myConfigValue:string) {
    console.log(myConfigValue);
  }
}
Günter Zöchbauer
źródło
Od Angular2 beta 7 (myślę) nie powinieneś rejestrować swojej usługi bezpośrednio w komponencie root (aka bootstrap). Możesz jednak wstrzyknąć tam konkretnego dostawcę, jeśli chcesz zastąpić coś w swojej aplikacji.
Mihai
1
Nie wiem co masz na myśli. Oczywiście możesz zarejestrować usługę w bootstrap(). bootstrap()a składnik główny to dwie różne rzeczy. Kiedy dzwonisz bootstrap(AppComponent, [MyService]), rejestrujesz usługę boostrap()i jesteś AppComponentgłównym komponentem. Dokumentacja wspomina gdzieś, że preferowane jest rejestrowanie dostawców (usług) w komponentach głównych, providers: ['MyService']ale nie znalazłem jeszcze żadnego argumentu za lub przeciw bootstrap()lub komponent root.
Günter Zöchbauer
Możesz znaleźć swój argument w sekcji Angular 2guide Dependency Injection ( angular.io/docs/ts/latest/guide/dependency-injection.html ). Jak mówią, możesz to zrobić, ale jest to WSTRZYMANE. Ten użytkownik prosi o najlepszy sposób wstrzyknięcia czegoś, co najwyraźniej nie jest poprawne. to samo dotyczy @ThierryTemplier
Mihai
1
Myślę, że ważniejszym cytatem z dokumentu Angular jest „Opcja dostawcy ładowania początkowego jest przeznaczona do konfigurowania i zastępowania własnych, wstępnie zarejestrowanych usług Angulara, takich jak obsługa routingu”. Rzadko sam umieszczam usługi w bootstrapie i cieszę się, że dokumentacja teraz to sugeruje.
Mark Rajcok
1
nie zapomnij wyeksportować klasy
Demodave
15

Utwórz klasę Globals w app / globals.ts :

import { Injectable } from '@angular/core';

Injectable()
export class Globals{
    VAR1 = 'value1';
    VAR2 = 'value2';
}

W twoim komponencie:

import { Globals } from './globals';

@Component({
    selector: 'my-app',
    providers: [ Globals ],
    template: `<h1>My Component {{globals.VAR1}}<h1/>`
})
export class AppComponent {
    constructor(private globals: Globals){
    }
}

Uwaga : Możesz dodać dostawcę usług Globals bezpośrednio do modułu zamiast komponentu i nie musisz dodawać jako dostawcy do każdego komponentu w tym module.

@NgModule({
    imports: [...],
    declarations: [...],
    providers: [ Globals ],
    bootstrap: [ AppComponent ]
})
export class AppModule {
}
gradosevic
źródło
To najlepsza odpowiedź, ponieważ oferuje bardziej przenośne podejście niż konieczność dodawania usługi do każdego komponentu w aplikacji. Dziękuję Ci!
Vidal Quevedo,
5
Kod działa. Należy jednak pamiętać, że wstrzyknięcie klasy Globalsz dodaniem jej do providers: [ ... ]oznacza, że ​​nie można zmienić wartości w jednym komponencie, a następnie poprosić o zaktualizowaną wartość w drugim komponencie. Za każdym razem, Globalsgdy wstrzykujesz , jest to nowa instancja. Jeśli chcesz zmienić to zachowanie, po prostu NIE dodawaj Globals jako dostawcy.
Timo Bähr
tylko uwaga, powinno być@Injectable()
mast3rd3mon
11

IMHO dla Angular2 (v2.2.3) najlepszym sposobem jest dodanie usług, które zawierają zmienną globalną i wstrzyknięcie ich do komponentów bez providerstagu wewnątrz @Componentadnotacji. W ten sposób możesz wymieniać informacje między komponentami.

Przykładowa usługa, która jest właścicielem zmiennej globalnej:

import { Injectable } from '@angular/core'

@Injectable()
export class SomeSharedService {
  public globalVar = '';
}

Przykładowy składnik aktualizujący wartość zmiennej globalnej:

import { SomeSharedService } from '../services/index';

@Component({
  templateUrl: '...'
})
export class UpdatingComponent {

  constructor(private someSharedService: SomeSharedService) { }

  updateValue() {
    this.someSharedService.globalVar = 'updated value';
  }
}

Przykładowy składnik odczytujący wartość zmiennej globalnej:

import { SomeSharedService } from '../services/index';

@Component({
  templateUrl: '...'
})
export class ReadingComponent {

  constructor(private someSharedService: SomeSharedService) { }

  readValue() {
    let valueReadOut = this.someSharedService.globalVar;
    // do something with the value read out
  }
}

Pamiętaj, że nieproviders: [ SomeSharedService ] należy ich dodawać do adnotacji. Nie dodając tej linii, wstrzyknięcie zawsze da ci to samo wystąpienie . Jeśli dodasz linię, zostanie wstrzyknięta świeżo utworzona instancja.@ComponentSomeSharedService

Timo Bähr
źródło
Ale bez dodania linii dostawców otrzymałem taki błąd:Unhandled Promise rejection: No provider for SomeSharedService
Rocky
Widzę. Powinienem dodać providers: [SomeSharedService]w pliku modułu nadrzędnego. Dzięki.
Rocky
To nie działa, gdy jesteśmy leniwi w ładowaniu modułów.
lpradhap
9

Nie znam najlepszego sposobu, ale najłatwiejszym sposobem, jeśli chcesz zdefiniować zmienną globalną wewnątrz komponentu, jest użycie windowzmiennej do napisania w ten sposób:

window.GlobalVariable = "what ever!"

nie musisz go przekazywać do ładowania początkowego lub importować w inne miejsca, i jest on dostępny globalnie dla wszystkich JS (nie tylko komponentów angular 2).

Mahdi Jadaliha
źródło
1
Powiedziałbym, że to najgorszy sposób. Używanie zmiennej statycznej nie jest bardziej skomplikowane, ale też nie jest takie brzydkie ;-)
Günter Zöchbauer
2
Zgadzam się, że to utrudnia zarządzanie. Skończyło się jednak na używaniu ich w rozwoju, dopóki nie znajdę tego, co chcę umieścić w produkcjach. W zmiennej statycznej musisz importować je raz po raz wszędzie tam, gdzie chcesz, poza tym był przypadek, że tworzyłem swój widok w ruchu za pomocą jquery w komponentach kątowych - nie było szablonu i dodawać zdarzenia do wyprodukowanego DOM za pomocą statycznego zmienną jest ból.
Mahdi Jadaliha
1
Ponadto nie jest statyczny, możesz zmienić wartość z dowolnego miejsca!
Mahdi Jadaliha
1
Niszczy również renderowanie po stronie serwera. Unikaj bezpośredniego manipulowania oknem lub dokumentem.
Erik Honn
1
Zgoda. ale ja osobiście nie kieruję się żadnymi wskazówkami w moim życiu (gdybym mógł to zrobić lepiej).
Mahdi Jadaliha
7

Tak go używam:

global.ts

export var server: string = 'http://localhost:4200/';
export var var2: number = 2;
export var var3: string = 'var3';

aby go użyć, po prostu zaimportuj w ten sposób:

import { Injectable } from '@angular/core';
import { Http, Headers, RequestOptions } from '@angular/http';
import { Observable } from 'rxjs/Rx';
import * as glob from '../shared/global'; //<== HERE

@Injectable()
export class AuthService {
    private AuhtorizationServer = glob.server
}

EDYTOWANE: Usunięto przedrostek „_” zgodnie z zaleceniami.

Guilherme Teubl
źródło
Nie używaj „_” jako przedrostka dla właściwości prywatnych. github.com/Microsoft/TypeScript/wiki/Coding-guidelines
crh225
4

Myślę, że najlepszym sposobem jest udostępnienie obiektu zmiennym globalnym w całej aplikacji poprzez eksportowanie i importowanie go tam, gdzie chcesz.

Najpierw utwórz nowy plik .ts , na przykład globals.ts i zadeklaruj obiekt. Nadałem mu typ obiektu, ale możesz też użyć dowolnego typu lub {}

export let globalVariables: Object = {
 version: '1.3.3.7',
 author: '0x1ad2',
 everything: 42
};

Następnie zaimportuj go

import {globalVariables} from "path/to/your/globals.ts"

I użyj go

console.log(globalVariables);
0x1ad2
źródło
3

Podoba mi się odpowiedź @supercobra, ale użyłbym słowa kluczowego const, które jest już dostępne w ES6:

//
// ===== File globals.ts    
//
'use strict';

export const sep='/';
export const version: string="22.2.2"; 
Zolcsi
źródło