Как я могу объявить глобальную переменную в Angular 2 / Typescript? [закрыто]

164

Я хотел бы, чтобы некоторые переменные были доступны везде Angular 2на Typescriptязыке. Как мне это сделать?

supercobra
источник
2
Если они являются статическими переменными, нет необходимости использовать сервисы. Просто добавьте переменную в некоторый файл и затем импортируйте ее везде, где вам это нужно.
Эрик Мартинес
К сожалению Angular2 имеет исключение во время выполнения, говоря Uncaught ReferenceError: Settings is not defined. Класс Settingsс открытыми статическими переменными настроен на экспорт и импортирован там, где он использовался.
Сен Джейкоб
Я знаю, что этот пост старый и есть много правильных ответов. Но, как упоминал Эрик. Если это простое значение, которое вы хотите объявить и получить доступ к нему через ваше приложение, вы можете создать класс и экспортировать класс со статическим свойством. Статические переменные связаны с классом, а не с экземпляром класса. Вы можете импортировать класс и получить доступ к свойству из class.directly.
De Wet Ellis

Ответы:

195

Вот самое простое решение ж / о , Serviceни Observer:

Поместите глобальные переменные в файл и экспортируйте их.

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

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

Чтобы использовать глобалы в другом файле, используйте importоператор: import * as myGlobals from 'globals';

Пример:

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

         ...

        }
    }

Спасибо @ Эрик-Мартинес

supercobra
источник
3
Получил ошибку в операторе импорта. пришлось использоватьimport * as globs from 'globals'
Mike M
13
Почему вы использовали «require» вместо импорта из?
Ayyash
4
Я бы использовал export constвместо export var- вы действительно хотите убедиться, что эти глобальные переменные не могут быть изменены
Михал Боска
1
Такой импорт больше не действует в TypeScript. Пожалуйста, обновите свой ответ. Правильно было бы:import * as myGlobals from 'globals'
Мик
7
import * as myGlobals from './path/to/globals';
Тимоти Цорн
89

Мне тоже нравится решение от @supercobra. Я просто хотел бы немного улучшить его. Если вы экспортируете объект, который содержит все константы, вы можете просто использовать es6 для импорта модуля без использования require .

Я также использовал Object.freeze, чтобы свойства стали настоящими константами. Если вы заинтересованы в этой теме, вы можете прочитать этот пост .

// global.ts

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

Обратитесь к модулю, используя импорт.

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

export class HeroService {
    private baseApiUrl = GlobalVariable.BASE_API_URL;

    //... more code
}
Тим Хонг
источник
для меня это лучшее решение, потому что (1) это самое простое с наименьшим количеством кода и (2) оно не требует, чтобы вы вводили какую-то чертову услугу в каждый отдельный компонент или место, где вы хотите ее использовать, и при этом требуют, чтобы вы зарегистрировали его в @NgModule. Я не могу понять, почему для этого необходимо создать Службу Angular 2, но, возможно, я что-то упускаю из виду? Сейчас я использую это отличное решение, но, пожалуйста, дайте мне знать, почему другие более сложные ответы здесь лучше?
FireDragon
8
Ваша GlobalVariable не является переменной. Это константа.
Прия Р
@PriyaR LOL, да, ты прав. Я предположил, что основная цель этого вопроса заключается в том, чтобы иметь безопасный способ доступа к некоторым значениям во всем мире, поэтому я импровизировал. В противном случае, не стесняйтесь менять const на var, вы получите свою переменную.
Тим Хонг
Недостатком Object.freeze является то, что значения не печатаются. В любом случае, упаковка значений в классе - лучший дизайн с моей точки зрения. Поэтому мы должны выбирать между типизированными свойствами и истинными константами.
Арфы
как установить GlobalVariable.BASE_API_URL в другой компонент ..?
Сунил Чаудхри
59

Общий сервис - лучший подход

export class SharedService {
  globalVar:string;
}

Но вы должны быть очень осторожны при регистрации, чтобы иметь возможность поделиться одним экземпляром для всего вашего приложения. Вы должны определить это при регистрации вашего приложения:

bootstrap(AppComponent, [SharedService]);

но не определять его снова в providersатрибутах ваших компонентов:

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

В противном случае будет создан новый экземпляр вашего сервиса для компонента и его подкомпонентов.

Вы можете взглянуть на этот вопрос относительно того, как в Angular2 работают внедрение зависимостей и иерархические инжекторы:

Вы также можете заметить, что вы также можете определить Observableсвойства в сервисе, чтобы уведомлять части вашего приложения об изменении ваших глобальных свойств:

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

Смотрите этот вопрос для более подробной информации:

Тьерри Темплиер
источник
Кажется, этот отличается. Кажется, @ Rat2000 думает, что наш ответ неправильный. Я обычно оставляю это решение за исключением тех, кто дает конкурирующие ответы, но если он убежден, что наши ответы неверны, то я думаю, что это правильно. Документы, на которые он ссылался в комментарии к моему ответу, упоминают, что он ОБНАРУЖЕН, но я не вижу никаких недостатков, и аргументы в документах довольно слабые. Также довольно часто добавляют провайдеров для начальной загрузки. Какова будет цель этого аргумента в любом случае. А как насчет HTTP_PROVIDERSи тому подобное, к ним также нельзя добавить bootstrap()?
Гюнтер Цохбауэр
2
Да, я только что прочитал аргумент и раздел в документе. Честно говоря, я не очень понимаю, почему это не рекомендуется из документа. Это способ определения логического разделения: что характерно для ядра Angular2 (провайдеры маршрутизации, провайдеры http) при начальной загрузке и что специфично для приложения в инжекторе компонента приложения. Тем не менее, у нас может быть только один вспомогательный инжектор (прикладной) для корневого (определенного при начальной загрузке). Я что-то пропустил? Более того, в документе, касающемся иерархических инжекторов, поставщики услуг определены в корневом инжекторе ;-)
Тьерри Темплиер,
3
Единственный аргумент, который я вижу, заключается в том, что сохранение области видимости как можно более узким и использование корневого компонента, по крайней мере, теоретически немного уже, чем использование, bootstrap()но на практике это не имеет значения. Я думаю, что перечисление их в boostrap()делает код легче для понимания. У компонента есть поставщики, директивы, шаблон. Я нахожу это перегруженным без перечисленных там глобальных провайдеров. Поэтому я предпочитаю bootstrap().
Гюнтер Цохбауэр
2
и как можно ссылаться на такие глобальные переменные? даже после загрузки службы вызов вызывает alert(globalVar)ошибку.
phil294
Я еще не пробовал это, но вы захотите что-то вроде: alert (this.SharedService.globalVar)
trees_are_great
39

См. Например, Angular 2 - Реализация общих служб

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

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

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

или предоставить индивидуальные значения

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

class MyComponent {
  constructor(@Inject('myConfigValue') private myConfigValue:string) {
    console.log(myConfigValue);
  }
}
Гюнтер Цохбауэр
источник
Начиная с бета-версии Angular2 7 (я думаю) вам не следует регистрировать свой сервис непосредственно в корневом компоненте (он же загрузчик). Однако вы можете добавить туда конкретного провайдера, если хотите что-то переопределить в своем приложении.
Михай
1
Не уверен, что вы имеете в виду. Конечно, вы можете зарегистрировать услугу в bootstrap(). bootstrap()и корневой компонент - это две разные вещи. Когда вы звоните, bootstrap(AppComponent, [MyService])вы регистрируете сервис в boostrap()и AppComponentявляется корневым компонентом. В документах где-то упоминается, что предпочтительно регистрировать провайдеров (сервис) в корневых компонентах, providers: ['MyService']но я пока не нашел аргументов в пользу или против bootstrap()или корневого компонента.
Гюнтер Цохбауэр
Вы можете найти свой аргумент в разделе Угловое руководство по вводу зависимостей ( angular.io/docs/ts/latest/guide/dependency-injection.html ). Как говорится, вы можете сделать это, но это ОБЯЗАТЕЛЬНО. Этот пользователь запрашивает лучший способ ввести что-то, что явно не является вашим решением. То же самое касается @ThierryTemplier
Mihai
1
Я думаю, что более важная цитата из документа Angular: «Опция поставщика начальной загрузки предназначена для настройки и переопределения собственных предварительно зарегистрированных сервисов Angular, таких как поддержка маршрутизации». Я редко помещаю сервисы в самозагрузку, и я рад видеть, что документы теперь предлагают это.
Марк Райкок
1
не забудьте экспортировать класс
Demodave
15

Создайте класс Globals в app / globals.ts :

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

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

В вашем компоненте:

import { Globals } from './globals';

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

Примечание . Вы можете добавить поставщика услуг Globals непосредственно в модуль вместо компонента, и вам не нужно добавлять его в качестве поставщика для каждого компонента в этом модуле.

@NgModule({
    imports: [...],
    declarations: [...],
    providers: [ Globals ],
    bootstrap: [ AppComponent ]
})
export class AppModule {
}
gradosevic
источник
Это лучший ответ, поскольку он предлагает более переносимый подход, чем добавление службы к каждому компоненту в приложении. Спасибо!
Видаль Кеведо
5
Код работает. Но обратите внимание, что внедрение класса Globalsс добавлением его также providers: [ ... ]означает, что вы не можете изменить значение внутри одного компонента, а затем запросить обновленное значение внутри второго компонента. Каждый раз, когда вы вводите Globalsэто свежий экземпляр. Если вы хотите изменить это поведение, просто НЕ добавляйте в Globals качестве поставщика.
Тимо
только примечание, это должно быть@Injectable()
mast3rd3mon
11

IMHO для Angular2 (v2.2.3) лучший способ - это добавить сервисы, которые содержат глобальную переменную, и внедрить их в компоненты без providersтега внутри @Componentаннотации. Таким образом, вы можете обмениваться информацией между компонентами.

Пример сервиса, которому принадлежит глобальная переменная:

import { Injectable } from '@angular/core'

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

Пример компонента, который обновляет значение вашей глобальной переменной:

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

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

  constructor(private someSharedService: SomeSharedService) { }

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

Пример компонента, который читает значение вашей глобальной переменной:

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

Обратите внимание , что providers: [ SomeSharedService ]следует не быть добавлены в @Componentаннотации. Не добавляя эту строку, инъекция всегда даст вам один и тот же экземпляр SomeSharedService. Если вы добавляете строку, вводится только что созданный экземпляр.

Тимо Бар
источник
Но, не добавляя строку провайдеров, я получил такую ​​ошибку:Unhandled Promise rejection: No provider for SomeSharedService
Rocky
Понимаю. Я должен добавить providers: [SomeSharedService]в файл родительского модуля. Спасибо.
Рокки
Это не работает, когда мы лениво загружаем модули.
lpradhap
9

Я не знаю лучший способ, но самый простой способ, если вы хотите определить глобальную переменную внутри компонента, это использовать windowпеременную для записи следующим образом:

window.GlobalVariable = "what ever!"

вам не нужно передавать его для начальной загрузки или импортировать его в другие места, и он доступен глобально для всех JS (не только для угловых 2 компонентов).

Махди Джадалиха
источник
1
Я бы сказал, что это худший путь. Использование статической переменной не сложнее, но и не так безобразно ;-)
Гюнтер Цохбауэр
2
Я согласен, что это затрудняет управление. Однако я прекратил использовать их в разработке, пока не найду то, что хочу поставить в производство. В статической переменной вы должны импортировать их снова и снова везде, где вы хотите использовать, кроме того, был случай, когда я создавал свой взгляд на ходу с jquery в угловых компонентах - не было шаблона, и добавлял события в производимый DOM с использованием статического переменная боль.
Махди Джадалиха
1
Плюс, это не статично, вы можете изменить значение отовсюду!
Махди Джадалиха
1
Это также разрушает рендеринг на стороне сервера. Держитесь подальше от прямого манипулирования окном или документом.
Эрик Хонн
1
Согласовано. но я лично не следую никаким руководящим принципам в моей жизни (если бы я мог сделать лучше, чем это).
Махди Джадалиха
7

Вот как я это использую:

global.ts

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

чтобы использовать его просто импортируйте так:

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
}

Отредактировано: добавлено префикс "_", как рекомендуется.

Гильерме Теубл
источник
Не используйте «_» в качестве префикса для частных свойств. github.com/Microsoft/TypeScript/wiki/Coding-guidelines
crh225
4

Я думаю, что лучший способ - это поделиться объектом с глобальными переменными во всем приложении, экспортируя и импортируя его в нужное вам место.

Сначала создайте новый ts - файл для примера globals.ts и объявить объект. Я дал ему тип объекта, но вы также можете использовать любой тип или {}

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

После этого импортируйте его

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

И использовать это

console.log(globalVariables);
0x1ad2
источник
3

Мне нравится ответ @supercobra, но я бы использовал ключевое слово const, которое уже доступно в ES6:

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

export const sep='/';
export const version: string="22.2.2"; 
Zolcsi
источник