ฉันจะประกาศตัวแปรทั่วโลกใน Angular 2 / Typescript ได้อย่างไร [ปิด]


164

ฉันต้องการให้ตัวแปรบางอย่างสามารถเข้าถึงได้ทุกที่Angular 2ในTypescriptภาษา ฉันจะทำสิ่งนี้ให้สำเร็จได้อย่างไร?


2
หากเป็นตัวแปรคงที่ไม่จำเป็นต้องใช้บริการ เพียงเพิ่มตัวแปรในไฟล์แล้วนำเข้าทุกที่ที่คุณต้องการ
Eric Martinez

แต่น่าเสียดายที่ Angular2 Uncaught ReferenceError: Settings is not definedมีข้อยกเว้นที่รันไทม์พูด คลาสที่Settingsมีตัวแปรสแตติกสาธารณะถูกตั้งค่าให้ส่งออกและนำเข้าที่ที่ใช้
Sen Jacob

ฉันรู้ว่าโพสต์นี้เก่าและมีคำตอบที่ถูกต้องมากมาย แต่อย่างที่เอริคพูดถึง หากเป็นค่าอย่างง่ายที่คุณต้องการประกาศและมีสิทธิ์เข้าถึงแอปพลิเคชันของคุณคุณสามารถสร้างคลาสและส่งออกคลาสด้วยคุณสมบัติสแตติก ตัวแปรสแตติกเกี่ยวข้องกับคลาสมากกว่าอินสแตนซ์ของคลาส คุณสามารถนำเข้าชั้นเรียนและจะสามารถเข้าถึงคุณสมบัติจาก class.directly
De Wet Ellis

คำตอบ:


195

นี่คือทางออกที่ง่ายที่สุดโดยServiceไม่มีหรือObserver:

วางตัวแปรโกลบอลในไฟล์เพื่อเอ็กซ์ปอร์ต

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

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

หากต้องการใช้ globals ในไฟล์อื่นให้ใช้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'
Mike M

13
เหตุใดคุณจึงใช้ "ต้องการ" แทนที่จะนำเข้า
Ayyash

4
ฉันจะใช้export constแทนexport var- คุณต้องการให้แน่ใจว่าตัวแปรทั่วโลกเหล่านั้นไม่สามารถเปลี่ยนแปลงได้
Michal Boska

1
การนำเข้าลักษณะนี้ไม่ถูกต้องใน TypeScript อีกต่อไป โปรดอัปเดตคำตอบของคุณ การแก้ไขจะเป็น:import * as myGlobals from 'globals'
มิก

7
import * as myGlobals from './path/to/globals';
Timothy Zorn

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) คุณไม่จำเป็นต้องใช้บริการ Darn ในทุกองค์ประกอบหรือสถานที่ที่คุณต้องการใช้และไม่ใช้มัน คุณต้องลงทะเบียนใน @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 คิดว่าคำตอบของเราผิด ฉันมักจะออกจากการตัดสินใจนี้กับคนอื่น ๆ มากกว่าคนที่ให้คำตอบการแข่งขัน แต่ถ้าเขาเชื่อว่าคำตอบของเราผิดแล้วฉันคิดว่ามันถูกต้อง เอกสารที่เขาเชื่อมโยงกับในความคิดเห็นต่อคำตอบของฉันกล่าวถึงว่ามันถูกทำลาย แต่ฉันไม่เห็นข้อเสียและข้อโต้แย้งในเอกสารค่อนข้างอ่อนแอ เป็นเรื่องปกติที่จะเพิ่มผู้ให้บริการลงใน bootstrap อะไรคือวัตถุประสงค์ของการโต้แย้งนี้ และสิ่งที่เกี่ยวกับHTTP_PROVIDERSและคล้ายกันพวกเขาไม่ควรถูกเพิ่มเข้ามาbootstrap()?
GünterZöchbauer

2
ใช่ฉันเพิ่งอ่านอาร์กิวเมนต์และส่วนในเอกสาร จริง ๆ แล้วฉันไม่เข้าใจว่าทำไมมันถึงหมดกำลังใจจากเอกสาร มันเป็นวิธีที่จะกำหนดแยกตรรกะ: อะไรคือสิ่งที่เฉพาะเจาะจงหลัก Angular2 (ผู้ให้บริการการกำหนดเส้นทาง, ผู้ให้บริการ http) เมื่อ bootstrapping และสิ่งที่เป็นแอปพลิเคชันที่เฉพาะเจาะจงในหัวฉีดส่วนประกอบของโปรแกรมประยุกต์ ที่กล่าวว่าเราสามารถมีหนึ่งหัวฉีดย่อย (แอพลิเคชันหนึ่ง) สำหรับรากหนึ่ง (กำหนดเมื่อ bootstrapping) ฉันคิดถึงอะไรไหม? ยิ่งไปกว่านั้นในเอกสารเกี่ยวกับหัวฉีดแบบลำดับชั้นผู้ให้บริการจะถูกกำหนดภายในรูทหัวฉีด ;-)
Thierry Templier

3
ข้อโต้แย้งเดียวที่ฉันเห็นคือการรักษาขอบเขตให้แคบที่สุดเท่าที่จะเป็นไปได้และการใช้องค์ประกอบรากอย่างน้อยก็ในทางทฤษฎีแคบกว่าการใช้bootstrap()แต่ในทางปฏิบัติมันไม่สำคัญ ฉันคิดว่าboostrap()การใส่รหัสไว้ในนั้นจะทำให้เข้าใจรหัสได้ง่ายขึ้น องค์ประกอบมีผู้ให้บริการคำสั่งแม่แบบ ฉันพบว่าโอเวอร์โหลดนี้ไม่มีผู้ให้บริการระดับโลกที่ระบุไว้เช่นกัน bootstrap()ดังนั้นผมจึงต้องการ
GünterZöchbauer

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 beta 7 (ฉันคิดว่า) คุณไม่ควรลงทะเบียนบริการของคุณโดยตรงในองค์ประกอบของราก (aka bootstrap) อย่างไรก็ตามคุณสามารถฉีดผู้ให้บริการเฉพาะได้หากคุณต้องการแทนที่บางอย่างในแอปพลิเคชันของคุณ
หมดเวลา

1
ไม่แน่ใจคุณหมายถึงอะไร. bootstrap()แน่นอนคุณสามารถลงทะเบียนบริการใน bootstrap()และองค์ประกอบของรากเป็นสองสิ่งที่แตกต่างกัน เมื่อคุณโทรหาbootstrap(AppComponent, [MyService])คุณลงทะเบียนบริการboostrap()และAppComponentเป็นองค์ประกอบหลัก เอกสารกล่าวถึงบางที่ที่ต้องการลงทะเบียนผู้ให้บริการ (บริการ) ในองค์ประกอบรากproviders: ['MyService']แต่ฉันยังไม่พบข้อโต้แย้งใด ๆ ในความโปรดปรานหรือต่อต้านbootstrap()หรือส่วนประกอบราก
GünterZöchbauer

คุณสามารถหาข้อโต้แย้งของคุณในส่วน 2guide เชิงมุมพึ่งพาการฉีด ( angular.io/docs/ts/latest/guide/dependency-injection.html ) เหมือนที่พวกเขาพูดว่าคุณสามารถทำได้ แต่มันถูกทำลาย ผู้ใช้รายนี้ขอวิธีที่ดีที่สุดในการ iinject สิ่งที่แม่มดเห็นได้ชัดว่าโซลูชันของคุณไม่ถูกต้อง เดียวกันสำหรับ @ThierryTemplier
Mihai

1
ฉันคิดว่าข้อความที่สำคัญกว่าจาก Angular doc คือ "ตัวเลือกผู้ให้บริการ bootstrap มีไว้สำหรับการกำหนดค่าและลบล้างบริการที่ลงทะเบียนล่วงหน้าของ Angular เช่นการสนับสนุนการกำหนดเส้นทาง" ฉันไม่ค่อยใส่บริการใน bootstrap ตัวเองและฉันดีใจที่เห็นเอกสารตอนนี้แนะนำว่า
Mark Rajcok

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 เป็นผู้ให้บริการ
Timo Bähr

เพียงทราบมันควรจะเป็น@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 ]ควรเพิ่มในบันทึกย่อของคุณ โดยไม่มีการเพิ่มการฉีดบรรทัดนี้จะให้คุณได้เช่นเดียวกันของ หากคุณเพิ่มบรรทัดอินสแตนซ์ที่สร้างขึ้นใหม่จะถูกฉีด@ComponentSomeSharedService


แต่ถ้าไม่มีการเพิ่มผู้ให้บริการฉันมีข้อผิดพลาดเช่นนี้:Unhandled Promise rejection: No provider for SomeSharedService
Rocky

ฉันเห็น. ฉันควรเพิ่มproviders: [SomeSharedService]ในไฟล์โมดูลหลัก ขอบคุณ
ร็อคกี้

สิ่งนี้ไม่ทำงานเมื่อเราโหลดโมดูลขี้เกียจ
lpradhap

9

ฉันไม่ทราบวิธีที่ดีที่สุด แต่วิธีที่ง่ายที่สุดถ้าคุณต้องการกำหนดตัวแปรทั่วโลกภายในองค์ประกอบคือการใช้windowตัวแปรเพื่อเขียนดังนี้:

window.GlobalVariable = "what ever!"

คุณไม่จำเป็นต้องส่งผ่านไปยัง bootstrap หรือนำเข้าที่อื่นและมันสามารถเข้าถึงได้ทั่วโลกไปยัง JS ทั้งหมด (ไม่เพียง แต่ส่วนประกอบเชิงมุม 2)


1
ฉันว่ามันเป็นวิธีที่แย่ที่สุด การใช้ตัวแปรแบบคงที่นั้นไม่ซับซ้อนมากนัก แต่ก็ไม่ได้เป็นเช่นนั้นที่น่าเกลียด ;-)
GünterZöchbauer

2
ฉันเห็นด้วยที่ทำให้การจัดการเป็นเรื่องยาก อย่างไรก็ตามฉันได้ใช้พวกมันในการพัฒนาจนกระทั่งพบสิ่งที่ฉันต้องการใส่ในการผลิต ในตัวแปรแบบคงที่คุณต้องนำเข้ามันซ้ำแล้วซ้ำอีกทุกที่ที่คุณต้องการใช้ข้าง ๆ มีกรณีที่ฉันสร้างมุมมองของฉันในระหว่างการเดินทางด้วย jquery ในองค์ประกอบเชิงมุม - ไม่มีแม่แบบและเพิ่มกิจกรรมเพื่อผลิต DOM โดยใช้แบบคงที่ ตัวแปรคือความเจ็บปวด
Mahdi Jadaliha

1
นอกจากนี้ยังไม่คงที่คุณสามารถเปลี่ยนค่าได้จากทุกที่!
Mahdi Jadaliha

1
นอกจากนี้ยังทำลายการแสดงผลฝั่งเซิร์ฟเวอร์ อยู่ห่างจากการจัดการหน้าต่างหรือเอกสารโดยตรง
Erik Honn

1
ตกลง แต่โดยส่วนตัวฉันไม่ปฏิบัติตามแนวทางใด ๆ ในชีวิตของฉัน (ถ้าฉันสามารถทำได้ดีกว่านั้น)
Mahdi Jadaliha

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

3

ฉันชอบคำตอบของ @supercobra แต่ฉันจะใช้คำหลัก const เพราะมันมีอยู่ใน ES6 แล้ว:

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

export const sep='/';
export const version: string="22.2.2"; 
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.