Як я можу оголосити глобальну змінну у Angular 2 / Typescript? [зачинено]


164

Я хотів би деякі змінні повинні бути доступні скрізь в Angular 2в Typescriptмові. Як я повинен іти до досягнення цього?


2
Якщо вони є статичними змінними, не потрібно користуватися послугами. Просто додайте змінну в якийсь файл, а потім імпортуйте її всюди, де вам це потрібно.
Ерік Мартінес

На жаль, Angular2, маючи виняток під час виконання, каже Uncaught ReferenceError: Settings is not defined. Клас Settingsіз загальнодоступними статичними змінними встановлюється для експорту та імпортує туди, де він використовується.
Сена Якова

Я знаю, що цей пост є старим і що є багато вагомих відповідей. Але, як згадував Ерік. Якщо це просте значення, яке ви хочете оголосити і мати доступ до своєї програми, ви можете створити клас та експортувати клас зі статичним властивістю. Статичні змінні асоціюються з класом, а не екземпляром класу. Ви можете імпортувати клас і матимете доступ до ресурсу з class.direct.
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";

         ...

        }
    }

Дякую @ eric-martinez


3
Помилка в операторі імпорту. довелося використовуватиimport * as globs from 'globals'
Майк М

13
Чому ви використовували "вимагати" замість імпорту?
Айяш

4
Я б застосував export constзамість цього export var- ви дійсно хочете переконатися, що ці глобальні змінні неможливо змінити
Michal Boska

1
Імпорт подібного більше не діє в TypeScript. Будь ласка, оновіть свою відповідь. Правильно було б:import * as myGlobals from 'globals'
Мік

7
import * as myGlobals from './path/to/globals';
Тимофій Зорн

89

Мені подобається рішення від @supercobra. Я просто хотів би трохи її покращити. Якщо ви експортуєте об'єкт, який містить усі константи, ви можете просто використовувати імпорт es6 модуля, не використовуючи потребу .

Я також використовував 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 не є змінною. Його постійна.
Priya R

@PriyaR LOL, так, ти маєш рацію. Я припустив, що головна мета питання - це безпечний доступ до деяких цінностей у всьому світі, тому я імпровізував. Інакше сміливо змінюйте const на var, ви отримуєте свою змінну.
Тім Гонг

Нижня частина Object.freeze полягає в тому, що значення не набираються. У будь-якому випадку, з моєї точки зору, обгортання значень у класі є кращим дизайном. Тому нам потрібно вибирати між введеними властивостями та справжніми константами.
Арфи

як встановити GlobalVariable.BASE_API_URL в іншому компоненті ..?
Sunil Chaudhry

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()?
Günter Zöchbauer

2
Так, я просто прочитав аргумент та розділ у документі. Чесно кажучи, я не дуже розумію, чому це відсторонено від док. Це спосіб визначення логічного розколу: що таке ядро ​​Angular2 (провайдери маршрутизації, провайдери http) під час завантаження та що стосується додатку в інжекторі компонентів додатка. Це означає, що ми можемо мати лише один допоміжний інжектор (додаток один) для кореневого (визначений під час завантаження). Я щось сумую? Більше того, у документі щодо ієрархічних інжекторів постачальники послуг визначені в кореневому інжекторі ;-)
Тем’єр Темплієр

3
Єдиний аргумент, який я бачу, полягає в тому, що збереження області як можна вужчої та використання кореневого компонента принаймні теоретично трохи вужча, ніж використання, bootstrap()але на практиці це не має значення. Я думаю, що їх перелік boostrap()полегшує розуміння коду. У компонента є постачальники, директиви, шаблон. Я вважаю це перевантаженим і без перелічених там глобальних провайдерів. Тому я віддаю перевагу bootstrap().
Günter Zöchbauer

2
і як можна посилатися на такі глобальні змінні? навіть після завантаження послуги, виклик alert(globalVar)призводить до помилки.
phil294

Я ще цього не пробував, але вам захочеться щось на кшталт: alert (this.SharedService.globalVar)
tree_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 beta 7 (я думаю), ви не повинні реєструвати свою послугу безпосередньо в кореневому компоненті (aka bootstrap). Однак ви можете ввести там конкретного постачальника, якщо хочете щось замінити у своїй програмі.
Міхай

1
Не впевнений, що ти маєш на увазі. Звичайно, ви можете зареєструвати послугу в bootstrap(). bootstrap()і кореневий компонент - це дві різні речі. Під час дзвінка bootstrap(AppComponent, [MyService])ви зареєструєте послугу в boostrap()і AppComponentє кореневим компонентом. Документи десь згадують, що краще зареєструвати провайдерів (службу) в кореневих компонентах, providers: ['MyService']але я ще не знайшов жодного аргументу на користь чи проти bootstrap()або кореневого компонента.
Günter Zöchbauer

Ви можете знайти свій аргумент у кутовому розділі 2 введення залежностей ( angular.io/docs/ts/latest/guide/dependency-injection.html ). Як кажуть, ви можете це зробити, але це ВІДКРИТИ. Цей користувач просить найкращого способу ввести щось відьом, щоб ваше рішення не було явним. те саме стосується @ThierryTemplier
Mihai

1
Я думаю, що більш важливою цитатою з Angular doc є "Опція постачальника завантажувальних програм призначена для налаштування та переопределення власних попередньо зареєстрованих служб 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 {
}

Це найкраща відповідь, оскільки він пропонує більш портативний підхід, ніж необхідність додавання послуги до кожного компонента через додаток. Дякую!
Vidal Quevedo

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]у файл батьківського модуля. Дякую.
Rocky

Це не працює, коли ми ліниві модулі завантаження.
lpradhap

9

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

window.GlobalVariable = "what ever!"

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


1
Я б сказав, що це найгірший спосіб. Використання статичної змінної не є складнішим, але це теж не потворно ;-)
Günter Zöchbauer

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 та оголосіть об’єкт. Я надав йому тип Object, але ви також можете використовувати будь-який тип або {}

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

Після цього імпортуйте його

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

І використовувати його

console.log(globalVariables);

3

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

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

export const sep='/';
export const version: string="22.2.2"; 
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.