Làm cách nào tôi có thể khai báo một biến toàn cục trong Angular 2 / Typecript? [đóng cửa]


164

Tôi muốn một số biến để có thể truy cập ở khắp mọi nơi trong một Angular 2trong các Typescriptngôn ngữ. Làm thế nào tôi nên đi về việc thực hiện điều này?


2
Nếu chúng là biến tĩnh thì không cần sử dụng dịch vụ. Chỉ cần thêm một biến trong một số tệp và sau đó nhập nó ở mọi nơi bạn cần.
Eric Martinez

Thật không may Angular2 có ngoại lệ trong thời gian chạy, nói Uncaught ReferenceError: Settings is not defined. Lớp Settingscó các biến tĩnh công khai được đặt thành xuất và đã nhập vào nơi nó được sử dụng.
Sen Jacob

Tôi biết bài này đã cũ và có nhiều câu trả lời hợp lệ. Nhưng như Eric đã đề cập. Nếu đó là một giá trị đơn giản mà bạn muốn khai báo và có quyền truy cập thông qua ứng dụng của mình, bạn có thể tạo một lớp và xuất lớp với thuộc tính tĩnh. Các biến tĩnh được liên kết với một lớp chứ không phải là một thể hiện của lớp. Bạn có thể nhập lớp và sẽ có thể truy cập thuộc tính từ class.directly.
De Wet Ellis

Câu trả lời:


195

Đây là giải pháp đơn giản nhất Servicecũng không phải Observer:

Đặt các biến toàn cục trong một tệp xuất chúng.

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

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

Để sử dụng toàn cầu trong một tệp khác, sử dụng một importtuyên bố: import * as myGlobals from 'globals';

Thí dụ:

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

         ...

        }
    }

Cảm ơn @ eric-martinez


3
Có một lỗi trên báo cáo nhập khẩu. phải sử dụngimport * as globs from 'globals'
Mike M

13
Tại sao bạn sử dụng "yêu cầu" thay vì nhập từ?
Ayyash

4
Tôi sẽ sử dụng export constthay vì export var- bạn thực sự muốn đảm bảo rằng các biến toàn cục đó không thể thay đổi
Michal Boska

1
Nhập như thế này không còn hợp lệ trong TypeScript nữa. Hãy cập nhật câu trả lời của bạn. Đúng sẽ là:import * as myGlobals from 'globals'
Mick

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

89

Tôi cũng thích giải pháp từ @supercobra. Tôi chỉ muốn cải thiện nó một chút. Nếu bạn xuất một đối tượng chứa tất cả các hằng số, bạn chỉ cần sử dụng es6 nhập mô-đun mà không cần sử dụng yêu cầu .

Tôi cũng đã sử dụng Object.freeze để làm cho các thuộc tính trở thành hằng số thực sự. Nếu bạn quan tâm đến chủ đề này, bạn có thể đọc bài viết này .

// global.ts

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

Tham khảo mô-đun bằng cách nhập.

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

export class HeroService {
    private baseApiUrl = GlobalVariable.BASE_API_URL;

    //... more code
}

Đây là giải pháp tốt nhất bởi vì (1) nó đơn giản nhất với số lượng mã ít nhất và (2) nó không yêu cầu bạn phải tiêm một số dịch vụ vô dụng vào mọi thành phần hoặc nơi bạn muốn sử dụng, cũng như không yêu cầu bạn phải đăng ký nó trong @NgModule. Cả đời tôi không thể hiểu tại sao cần phải tạo Dịch vụ Angular 2 để làm việc này, nhưng có lẽ tôi đang nhìn thấy điều gì đó? Tôi đang sử dụng giải pháp tuyệt vời này ngay bây giờ nhưng xin vui lòng cho tôi biết tại sao các câu trả lời phức tạp khác ở đây tốt hơn?
FireDragon

8
GlobalVariable của bạn không phải là một biến. Đó là một hằng số.
Priya R

@PriyaR LOL, vâng, bạn đúng. Tôi giả định mục tiêu chính của câu hỏi là có một cách an toàn để truy cập một số giá trị trên toàn cầu, vì vậy tôi đã ứng biến. Nếu không, hãy thoải mái thay đổi const thành var, bạn nhận được biến của mình.
Tim Hồng

Mặt trái của Object.freeze là các giá trị không được gõ. Dù sao, gói các giá trị trong một lớp là một thiết kế tốt hơn từ quan điểm của tôi. Vì vậy, chúng ta phải lựa chọn giữa các thuộc tính gõ và hằng số thực.
hạc

Làm cách nào để đặt GlobalVariable.BASE_API_URL trong thành phần khác ..?
Sunil Chaudhry

59

Một dịch vụ chia sẻ là cách tiếp cận tốt nhất

export class SharedService {
  globalVar:string;
}

Nhưng bạn cần phải rất cẩn thận khi đăng ký để có thể chia sẻ một ví dụ duy nhất cho toàn bộ ứng dụng của bạn. Bạn cần xác định nó khi đăng ký ứng dụng của bạn:

bootstrap(AppComponent, [SharedService]);

nhưng không xác định lại nó trong các providersthuộc tính của các thành phần của bạn:

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

Nếu không, một phiên bản mới của dịch vụ của bạn sẽ được tạo cho thành phần và các thành phần phụ của nó.

Bạn có thể xem câu hỏi này liên quan đến cách thức tiêm phụ thuộc và kim phun phân cấp hoạt động trong Angular2:

Bạn có thể nhận thấy rằng bạn cũng có thể xác định Observablecác thuộc tính trong dịch vụ để thông báo cho các bộ phận trong ứng dụng của bạn khi các thuộc tính toàn cầu của bạn thay đổi:

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

Xem câu hỏi này để biết thêm chi tiết:


Có vẻ như điều này là khác nhau mặc dù. Có vẻ @ Rat2000 nghĩ rằng câu trả lời của chúng tôi là sai. Tôi thường để lại quyết định này cho người khác ngoài việc đưa ra các câu trả lời cạnh tranh nhưng nếu anh ta tin chắc rằng câu trả lời của chúng tôi là sai thì tôi nghĩ nó hợp lệ. Các tài liệu anh ấy liên kết trong một bình luận cho câu trả lời của tôi đề cập rằng nó BỊ TỪNG nhưng tôi thấy không có nhược điểm nào và các lập luận trong các tài liệu khá yếu. Nó cũng khá phổ biến để thêm các nhà cung cấp vào bootstrap. Mục đích của cuộc tranh luận này là gì Và những gì về HTTP_PROVIDERSvà tương tự, họ cũng không nên được thêm vào bootstrap()?
Günter Zöchbauer 22/03/2016

2
Có tôi chỉ đọc các đối số và phần trong tài liệu. Thành thật mà nói tôi không thực sự hiểu tại sao nó không được khuyến khích từ tài liệu. Đây có phải là một cách để xác định phân chia hợp lý: cụ thể lõi Angular2 (nhà cung cấp định tuyến, nhà cung cấp http) khi bootstrapping là gì và cụ thể ứng dụng trong bộ tiêm thành phần ứng dụng. Điều đó nói rằng chúng ta chỉ có thể có một trình tiêm phụ (ứng dụng một) cho cái gốc (được xác định khi bootstrapping). Tôi có bỏ lỡ điều gì không? Ngoài ra, trong tài liệu liên quan đến kim phun phân cấp, các nhà cung cấp dịch vụ được xác định trong công cụ tiêm gốc ;-)
Thierry Templier

3
Lập luận duy nhất tôi thấy là việc giữ phạm vi càng hẹp càng tốt và sử dụng thành phần gốc ít nhất là về lý thuyết hẹp hơn một chút so với sử dụng bootstrap()nhưng trong thực tế, điều đó không thành vấn đề. Tôi nghĩ rằng việc liệt kê chúng vào boostrap()làm cho mã dễ hiểu hơn. Một thành phần có nhà cung cấp, chỉ thị, một mẫu. Tôi thấy điều này quá tải mà không có nhà cung cấp toàn cầu được liệt kê ở đó là tốt. Vì vậy tôi thích bootstrap().
Günter Zöchbauer 22/03/2016

2
và làm thế nào để tham chiếu các biến toàn cầu như vậy? ngay cả sau khi bootstrapping dịch vụ, alert(globalVar)kết quả cuộc gọi bị lỗi.
phil294

Tôi chưa thử điều này, nhưng bạn sẽ muốn một cái gì đó như: alert (this.SharedService.globalVar)
cây_are_great

39

Xem ví dụ Angular 2 - Triển khai các dịch vụ chia sẻ

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

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

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

hoặc cung cấp các giá trị riêng lẻ

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

class MyComponent {
  constructor(@Inject('myConfigValue') private myConfigValue:string) {
    console.log(myConfigValue);
  }
}

Vì Angular2 beta 7 (tôi nghĩ), bạn không nên đăng ký dịch vụ của mình trực tiếp trong thành phần gốc (hay còn gọi là bootstrap). Tuy nhiên, bạn có thể cung cấp cho một nhà cung cấp cụ thể nếu bạn muốn ghi đè lên một cái gì đó trong ứng dụng của bạn.
Mihai

1
Không chắc chắn những gì bạn có ý nghĩa. Tất nhiên bạn có thể đăng ký một dịch vụ trong bootstrap(). bootstrap()và thành phần gốc là hai thứ khác nhau. Khi bạn gọi, bootstrap(AppComponent, [MyService])bạn đăng ký dịch vụ boostrap()AppComponentlà thành phần gốc. Các tài liệu đề cập đến một nơi nào đó rằng nó thích đăng ký nhà cung cấp (dịch vụ) trong các thành phần gốc providers: ['MyService']nhưng tôi chưa tìm thấy bất kỳ đối số nào ủng hộ hoặc chống lại bootstrap()hoặc thành phần gốc.
Günter Zöchbauer 22/03/2016

Bạn có thể tìm thấy đối số của mình trong phần 2guide góc cạnh Dependency Injection ( angular.io/docs/ts/latest/guide/dependency-injection.html ). Giống như họ nói, bạn có thể làm điều đó nhưng nó bị BẮT BUỘC. Người dùng này đang yêu cầu cách tốt nhất để iinject một cái gì đó phù hợp rõ ràng giải pháp của bạn không phải là xác chết. tương tự với @ThierryTemplier
Mihai

1
Tôi nghĩ rằng trích dẫn quan trọng hơn từ tài liệu Angular là "Tùy chọn nhà cung cấp bootstrap dành cho việc định cấu hình và ghi đè các dịch vụ được đăng ký trước của Angular, như hỗ trợ định tuyến của nó." Tôi hiếm khi tự đặt dịch vụ vào bootstrap và tôi rất vui khi thấy các tài liệu hiện gợi ý điều đó.
Mark Rajcok 22/03/2016

1
đừng quên xuất khẩu lớp
Demodave

15

Tạo lớp Globals trong ứng dụng / globalals.ts :

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

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

Trong thành phần của bạn:

import { Globals } from './globals';

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

Lưu ý : Bạn có thể thêm nhà cung cấp dịch vụ Globals trực tiếp vào mô-đun thay vì thành phần và bạn sẽ không cần thêm nhà cung cấp cho mọi thành phần trong mô-đun đó.

@NgModule({
    imports: [...],
    declarations: [...],
    providers: [ Globals ],
    bootstrap: [ AppComponent ]
})
export class AppModule {
}

Đây là câu trả lời tốt nhất, vì nó cung cấp một cách tiếp cận di động hơn là phải thêm dịch vụ cho từng thành phần trên ứng dụng. Cảm ơn bạn!
Vidal Quevedo

5
Mã đang hoạt động. Nhưng lưu ý rằng lớp tiêm Globalscũng có thêm nó providers: [ ... ]có nghĩa là bạn không thể thay đổi một giá trị bên trong một thành phần và sau đó yêu cầu giá trị được cập nhật bên trong một thành phần thứ hai. Mỗi khi bạn tiêm Globalsnó là một ví dụ mới. Nếu bạn muốn thay đổi hành vi này, chỉ cần KHÔNG thêm Globals làm nhà cung cấp.
Timo Bähr

chỉ là một ghi chú, nó phải là@Injectable()
mast3rd3mon

11

IMHO cho Angular2 (v2.2.3) cách tốt nhất là thêm các dịch vụ có chứa biến toàn cục và đưa chúng vào các thành phần mà không có providersthẻ bên trong @Componentchú thích. Bằng cách này, bạn có thể chia sẻ thông tin giữa các thành phần.

Một dịch vụ mẫu sở hữu một biến toàn cục:

import { Injectable } from '@angular/core'

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

Một thành phần mẫu cập nhật giá trị của biến toàn cục của bạn:

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

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

  constructor(private someSharedService: SomeSharedService) { }

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

Một thành phần mẫu đọc giá trị của biến toàn cục của bạn:

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

Lưu ý rằng providers: [ SomeSharedService ]nên không được bổ sung vào @Componentchú thích. Bằng cách không thêm dòng tiêm này sẽ luôn cung cấp cho bạn cùng một ví dụ SomeSharedService. Nếu bạn thêm dòng, một thể hiện mới được tạo sẽ được thêm vào.


Nhưng không thêm dòng nhà cung cấp, tôi đã gặp một lỗi như thế này:Unhandled Promise rejection: No provider for SomeSharedService
Rocky

Tôi hiểu rồi. Tôi nên thêm providers: [SomeSharedService]vào trong tập tin mô-đun cha. Cảm ơn.
Rocky

Điều này không hoạt động khi chúng ta lười tải các mô-đun.
lpradhap

9

Tôi không biết cách tốt nhất, nhưng cách dễ nhất nếu bạn muốn xác định một biến toàn cục bên trong một thành phần là sử dụng windowbiến để viết như thế này:

window.GlobalVariable = "what ever!"

bạn không cần phải chuyển nó vào bootstrap hoặc nhập nó ở những nơi khác và nó có thể truy cập toàn cầu vào tất cả các JS (không chỉ 2 thành phần góc cạnh).


1
Tôi muốn nói đó là cách tồi tệ nhất. Sử dụng một biến tĩnh không phức tạp hơn nhưng cũng không xấu lắm ;-)
Günter Zöchbauer

2
Tôi đồng ý nó làm cho khó quản lý. Tuy nhiên tôi đã kết thúc sử dụng chúng trong quá trình phát triển cho đến khi tôi tìm thấy những gì tôi muốn đưa vào sản xuất. Trong biến tĩnh, bạn phải nhập chúng nhiều lần ở mọi nơi bạn muốn sử dụng, bên cạnh đó là trường hợp tôi đang tạo ra quan điểm của mình khi đang di chuyển với các thành phần góc - không có khuôn mẫu và thêm các sự kiện vào DOM được tạo bằng tĩnh biến là đau.
Mahdi Jadaliha

1
Thêm vào đó, nó không tĩnh, bạn có thể thay đổi giá trị từ mọi nơi!
Mahdi Jadaliha

1
Nó cũng phá hỏng kết xuất phía máy chủ. Tránh xa thao tác trực tiếp với cửa sổ hoặc tài liệu.
Erik Honn

1
Đã đồng ý. nhưng cá nhân tôi không tuân theo bất kỳ hướng dẫn nào trong cuộc sống của tôi (nếu tôi có thể làm tốt hơn thế).
Mahdi Jadaliha

7

Đó là cách tôi sử dụng nó:

toàn cầu

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

để sử dụng nó chỉ cần nhập như thế:

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
}

EDITED: Đã bỏ tiền tố "_" theo khuyến nghị.


Không sử dụng "_" làm tiền tố cho các thuộc tính riêng tư. github.com/Microsoft/TypeScript/wiki/Coding-guferences
crh225

4

Tôi nghĩ cách tốt nhất là chia sẻ một đối tượng với các biến toàn cục trong ứng dụng của bạn bằng cách xuất và nhập nó vào nơi bạn muốn.

Đầu tiên tạo một tệp .ts mới, ví dụ globalals.ts và khai báo một đối tượng. Tôi đã cung cấp cho nó một loại Đối tượng nhưng bạn cũng có thể sử dụng bất kỳ loại nào hoặc {}

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

Sau đó nhập nó

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

Và sử dụng nó

console.log(globalVariables);

3

Tôi thích câu trả lời của @supercobra, nhưng tôi sẽ sử dụng từ khóa const như trong ES6 đã có sẵn:

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

export const sep='/';
export const version: string="22.2.2"; 
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.