Como posso declarar uma variável global em Angular 2 / Typecript? [fechadas]

164

Eu gostaria que algumas variáveis ​​estivessem acessíveis em qualquer Angular 2lugar do Typescriptidioma. Como devo fazer isso?

supercobra
fonte
2
Se são variáveis ​​estáticas, não há necessidade de usar serviços. Basta adicionar uma variável em algum arquivo e importá-la para qualquer lugar que você precisar.
Eric Martinez
Infelizmente Angular2 tendo exceção em tempo de execução, dizendo Uncaught ReferenceError: Settings is not defined. A classe Settingscom variáveis ​​estáticas públicas está configurada para exportar e importada para onde é usada.
Sen Jacob
Eu sei que este post é antigo e que existem muitas respostas válidas. Mas como Eric mencionou. Se for um valor simples que você deseja declarar e ter acesso a todo o aplicativo, é possível criar uma classe e exportar a classe com uma propriedade estática. Variáveis ​​estáticas estão associadas a uma classe e não a uma instância da classe. Você pode importar a classe e poderá acessar a propriedade a partir da class.directly.
De Wet Ellis

Respostas:

195

Aqui está a solução mais simples w o / Servicenem Observer:

Coloque as variáveis ​​globais em um arquivo e exporte-as.

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

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

Para usar globais em outro arquivo, use uma importinstrução: import * as myGlobals from 'globals';

Exemplo:

// 
// ===== 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";

         ...

        }
    }

Obrigado @ eric-martinez

supercobra
fonte
3
Ocorreu um erro na declaração de importação. teve que usarimport * as globs from 'globals'
Mike M
13
Por que você usou "require" em vez de importar?
precisa
4
Eu usaria em export constvez de export var- você realmente quer ter certeza de que essas variáveis ​​globais não podem ser alteradas
Michal Boska
1
Importar assim não é mais válido no TypeScript. Atualize sua resposta. Correto seria:import * as myGlobals from 'globals'
Mick
7
import * as myGlobals from './path/to/globals';
Timothy Zorn
89

Também gosto da solução da @supercobra. Eu só gostaria de melhorar um pouco. Se você exportar um objeto que contenha todas as constantes, poderá simplesmente usar o es6 import o módulo sem usar o require .

Também usei o Object.freeze para tornar as propriedades verdadeiras constantes. Se você está interessado no tópico, pode ler esta postagem .

// global.ts

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

Consulte o módulo usando a importação.

//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
fonte
essa para mim é a melhor solução porque (1) é a mais simples com menos quantidade de código e (2) não exige que você injete algum serviço danado em cada componente ou local em que deseja usá-lo, nem requer que você o registre no @NgModule. Não consigo descobrir por que seria necessário criar um serviço Angular 2 para fazer isso, mas talvez haja algo que estou ignorando? Estou usando esta ótima solução por enquanto, mas por favor, deixe-me saber por que as outras respostas mais complicadas aqui são melhores?
FireDragon
8
Seu GlobalVariable não é uma variável. É uma constante.
Amina R
@PriyaR LOL, sim, você está certo. Eu assumi que o principal objetivo da pergunta era ter uma maneira segura de acessar alguns valores globalmente, então improvisei. Caso contrário, sinta-se à vontade para alterar const para var, você obtém sua variável.
Tim Hong
O lado negativo do Object.freeze é que os valores não são digitados. Enfim, agrupar valores em uma classe é um design melhor da minha perspectiva. Portanto, temos que escolher entre propriedades digitadas e constantes verdadeiras.
Harps
como definir GlobalVariable.BASE_API_URL em outro componente ..?
Sunil Chaudhry
59

Um serviço compartilhado é a melhor abordagem

export class SharedService {
  globalVar:string;
}

Mas você precisa ter muito cuidado ao registrá-lo para poder compartilhar uma única instância para todo o aplicativo. Você precisa defini-lo ao registrar seu aplicativo:

bootstrap(AppComponent, [SharedService]);

mas não para defini-lo novamente dentro dos providersatributos de seus componentes:

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

Caso contrário, uma nova instância do seu serviço será criada para o componente e seus subcomponentes.

Você pode dar uma olhada nesta pergunta sobre como a injeção de dependência e os injetores hierárquicos funcionam no Angular2:

Você pode perceber que também pode definir Observablepropriedades no serviço para notificar partes do seu aplicativo quando suas propriedades globais forem alteradas:

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

Veja esta pergunta para mais detalhes:

Thierry Templier
fonte
Parece que este é diferente. Parece que @ Rat2000 acha que nossa resposta está errada. Normalmente, deixo essa decisão para outras pessoas além de fornecer respostas concorrentes, mas se ele está convencido de que nossas respostas estão erradas, acho que é válido. Os documentos aos quais ele vinculou em um comentário à minha resposta mencionam que é DESCOBERTO, mas não vejo nenhuma desvantagem e os argumentos nos documentos são bastante fracos. Também é bastante comum adicionar provedores ao bootstrap. Qual seria o objetivo desse argumento de qualquer maneira. E o que dizer HTTP_PROVIDERSe semelhante, eles também não devem ser adicionados bootstrap()?
Günter Zöchbauer 22/03
2
Sim, acabei de ler o argumento e a seção no documento. Honestamente, eu realmente não entendo por que isso é desencorajado pelo documento. É uma maneira de definir uma divisão lógica: o que é específico do núcleo do Angular2 (provedores de roteamento, provedores http) durante a inicialização e o que é específico do aplicativo no injetor de componentes do aplicativo. Dito isto, só podemos ter um sub-injetor (o aplicativo) para o root (definido durante a inicialização). Eu sinto falta de alguma coisa? Além disso, no doc sobre injetores hierárquicos, prestadores de serviços são definidos dentro do injector raiz ;-)
Thierry Templier
3
O único argumento que vejo é que manter o escopo o mais estreito possível e usar o componente raiz é, pelo menos em teoria, um pouco mais estreito do que usar, bootstrap()mas na prática isso não importa. Acho que listá-los boostrap()facilita o entendimento do código. Um componente possui provedores, diretivas, um modelo. Acho isso sobrecarregado sem fornecedores globais listados lá também. Por isso eu prefiro bootstrap().
Günter Zöchbauer 22/03
2
e como alguém faz referência a essas variáveis ​​globais? mesmo após a inicialização do serviço, a chamada alert(globalVar)resulta em um erro.
phil294
Eu não tentei isso ainda, mas você vai querer algo como: alert (this.SharedService.globalVar)
trees_are_great
39

Veja, por exemplo, Angular 2 - Implementação de serviços compartilhados

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

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

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

ou fornecer valores individuais

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

class MyComponent {
  constructor(@Inject('myConfigValue') private myConfigValue:string) {
    console.log(myConfigValue);
  }
}
Günter Zöchbauer
fonte
Desde o Angular2 beta 7 (eu acho), você não deve registrar seu serviço diretamente no componente raiz (também conhecido como bootstrap). No entanto, você pode injetar um provedor específico se desejar substituir algo em seu aplicativo.
22616 Mihai
1
Não tenho certeza do que você quer dizer. Claro que você pode registrar um serviço bootstrap(). bootstrap()e componente raiz são duas coisas diferentes. Quando você liga, bootstrap(AppComponent, [MyService])registra o serviço boostrap()e AppComponenté o componente raiz. Os documentos mencionam que é preferível registrar provedores (serviço) nos componentes raiz, providers: ['MyService']mas ainda não encontrei nenhum argumento a favor ou contra bootstrap()ou componente raiz.
Günter Zöchbauer 22/03
Você pode encontrar seu argumento na seção angular 2guide Injection de dependência ( angular.io/docs/ts/latest/guide/dependency-injection.html ). Como se costuma dizer, você pode fazê-lo, mas é DESCOBERTA. Este usuário está pedindo a melhor maneira de injetar algo, claramente que sua solução não está correta. O mesmo vale para @ThierryTemplier
Mihai
1
Acho que a citação mais importante do documento Angular é "A opção do provedor de auto-inicialização é destinada à configuração e substituição dos serviços pré-registrados da Angular, como o suporte a roteamento". Eu mesmo raramente coloco serviços no bootstrap e fico feliz em ver os documentos sugerindo isso agora.
Mark Rajcok
1
não se esqueça de exportar a classe
Demodave
15

Crie a classe Globals em app / globals.ts :

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

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

No seu componente:

import { Globals } from './globals';

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

Nota : Você pode adicionar o provedor de serviços Globals diretamente ao módulo, em vez do componente, e não precisará adicionar como provedor a todos os componentes desse módulo.

@NgModule({
    imports: [...],
    declarations: [...],
    providers: [ Globals ],
    bootstrap: [ AppComponent ]
})
export class AppModule {
}
gradosevic
fonte
Essa é a melhor resposta, pois oferece uma abordagem mais portátil do que ter que adicionar o serviço a cada componente no aplicativo. Obrigado!
Vidal Quevedo
5
O código está funcionando. Mas observe que a injeção de uma classe Globalse também a adição de uma classe providers: [ ... ]significa que você não pode alterar um valor dentro de um componente e solicitar o valor atualizado dentro de um segundo componente. Toda vez que você injeta Globals, é uma nova instância. Se você deseja alterar esse comportamento, simplesmente NÃO adicione Globals como provedor.
Timo Bähr
apenas uma nota, deveria ser@Injectable()
mast3rd3mon 18/04/19
11

O IMHO para Angular2 (v2.2.3), a melhor maneira é adicionar serviços que contenham a variável global e injetá-los em componentes sem a providerstag dentro da @Componentanotação. Dessa forma, você pode compartilhar informações entre componentes.

Um serviço de amostra que possui uma variável global:

import { Injectable } from '@angular/core'

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

Um componente de amostra que atualiza o valor da sua variável global:

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

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

  constructor(private someSharedService: SomeSharedService) { }

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

Um componente de amostra que o valor da sua variável global:

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

Observe que nãoproviders: [ SomeSharedService ] deve ser adicionado à sua anotação. Ao não adicionar esta injeção de linha, você sempre terá a mesma instância de . Se você adicionar a linha, uma instância recém-criada será injetada.@ComponentSomeSharedService

Timo Bähr
fonte
Mas sem adicionar a linha de provedores, recebi um erro como este: #Unhandled Promise rejection: No provider for SomeSharedService
Rocky
Entendo. Eu devo adicionar providers: [SomeSharedService]no arquivo do módulo pai. Obrigado.
Rocky
Isso não funciona quando somos preguiçosos carregando módulos.
Lpradhap 11/1118
9

Não sei a melhor maneira, mas a maneira mais fácil, se você deseja definir uma variável global dentro de um componente, é usar a windowvariável para escrever assim:

window.GlobalVariable = "what ever!"

você não precisa transmiti-lo para inicializar ou importar outros locais, e é acessível globalmente a todos os JS (não apenas aos componentes angulares 2).

Mahdi Jadaliha
fonte
1
Eu diria que é o pior caminho. Usando uma variável estática não é mais complicado, mas não é tão feia ;-)
Günter Zöchbauer
2
Concordo que dificulta o gerenciamento. No entanto, acabei usando-as no desenvolvimento até encontrar o que quero colocar nas produções. Na variável estática, é necessário importá-las repetidamente em todos os lugares que você deseja usar. Além disso, havia um caso em que eu estava produzindo minha visão em movimento com jquery em componentes angulares - não havia modelo e adicionar eventos ao DOM produzido usando estática variável é dor.
Mahdi Jadaliha
1
Além disso, não é estático, você pode alterar o valor de qualquer lugar!
Mahdi Jadaliha
1
Ele também destrói a renderização do lado do servidor. Evite manipular diretamente a janela ou o documento.
Erik Honn
1
Acordado. mas eu pessoalmente não sigo nenhuma orientação na minha vida (se eu pudesse fazer melhor que isso).
Mahdi Jadaliha
7

É assim que eu uso:

global.ts

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

para usá-lo, basta importar assim:

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
}

EDITADO: Eliminado o prefixo "_" conforme recomendado.

Guilherme Teubl
fonte
Não use "_" como prefixo para propriedades particulares. github.com/Microsoft/TypeScript/wiki/Coding-guidelines
crh225
4

Eu acho que a melhor maneira é compartilhar um objeto com variáveis ​​globais em todo o aplicativo, exportando e importando-o para onde você quiser.

Primeiro, crie um novo arquivo .ts, por exemplo, globals.ts e declare um objeto. Eu dei a ele um tipo de objeto, mas você também pode usar qualquer tipo ou {}

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

Depois disso, importe

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

E use

console.log(globalVariables);
0x1ad2
fonte
3

Eu gosto da resposta de @supercobra, mas usaria a palavra-chave const, pois ela já está disponível no ES6:

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

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