Как разделить сервис между двумя модулями - @NgModule в angular, а не между компонентами?

В моем приложении у меня есть два разных модуля начальной загрузки (@NgModule), работающих независимо в одном приложении. Не существует одного отдельного модуля начальной загрузки для приложения angular, и теперь я хочу, чтобы они общались друг с другом и обменивались данными.

Я знаю, что через службу @Injectable в качестве поставщика в модуле я могу делиться данными во всех компонентах в @NgModule, но как я буду делиться данными между двумя разными модулями (а не компонентом внутри модуля).

Есть ли способ получить доступ к одному объекту службы в другом модуле? Есть ли способ получить доступ к объекту Service, доступному в памяти браузера, и использовать его в другом модуле angular?


person Aniruddha Das    schedule 17.10.2016    source источник
comment
Что вы имеете в виду под модулем? @NgModule(). Услуги, предоставляемые в @NgModule(), используются совместно со всем приложением и, следовательно, также со всеми модулями в вашем приложении (кроме случаев, когда модуль, в котором вы предоставляете услугу, загружается лениво).   -  person Günter Zöchbauer    schedule 17.10.2016
comment
Я имею ввиду два разных модуля anglar2. Да, я знаю, что сервис может обмениваться данными по всему моему модулю. Я хочу поделиться данными вне моего модуля и в другом модуле, созданном другой командой   -  person Aniruddha Das    schedule 17.10.2016
comment
Если вы предоставили службу в @NgModule, все перечисленные ниже неленивые модули могут использовать эту службу. Если вы выполняете ленивую загрузку, каждый модуль с ленивой загрузкой должен предоставлять эти услуги. Если вам нужно использовать его в другом приложении, либо предложите конечную точку API, либо отправьте им код.   -  person gelliott181    schedule 17.10.2016
comment
Что вы имеете в виду под модулем Angular2 и что вы имеете в виду под независимым запуском? Мне все еще непонятно. Вы имеете в виду два приложения Angular2, в которых загружаются два разных модуля?   -  person Günter Zöchbauer    schedule 17.10.2016
comment
@ GünterZöchbauer angula2 module означает модуль angular2, созданный с помощью декоратора NgModule. Моя команда создает один модуль angular2, а другая команда создает еще один модуль, который будет на той же странице html. теперь они хотят поделиться теми же данными. например, если я получаю доступ к некоторым данным, они могут быть доступны в другом модуле   -  person Aniruddha Das    schedule 17.10.2016
comment
Как эти разные модули загружаются на страницу? Если вы просто импортируете другие модули, они являются частью одного и того же приложения, а службы по умолчанию являются общими.   -  person Günter Zöchbauer    schedule 17.10.2016
comment
Два разных модуля будут загружаться по-разному и не будут иметь никакого отношения. таким образом я хочу получить к нему доступ из памяти браузера. В качестве angular 2 создайте любой машинописный класс @Injectable. Есть ли способ получить эту услугу из доступного объекта в JavaScript.   -  person Aniruddha Das    schedule 17.10.2016
comment


Ответы (7)


2021-02-03

В последних версиях Angular это решается с помощью @Injectable({providedIn: 'platform'})

https://angular.io/api/core/Injectable

Исходный

Вы можете создать экземпляр службы вне Angular и указать значение:

class SharedService {
  ...
}
window.sharedService = new SharedService();

@NgModule({
  providers: [{provide: SharedService, useValue: window.sharedService}],
  ...
})
class AppModule1 {}

@NgModule({
  providers: [{provide: SharedService, useValue: window.sharedService}],
  ...
})
class AppModule2 {}

Если одно приложение изменяет состояние в SharedService или вызывает метод, который заставляет Observable испускать значение, а подписчик находится в другом приложении, чем эмиттер, код в подписчике выполняется в NgZone эмиттера.

Поэтому при подписке на наблюдаемое в SharedService используйте

class MyComponent {
  constructor(private zone:NgZone, private sharedService:SharedService) {
    sharedService.someObservable.subscribe(data => this.zone.run(() => {
      // event handler code here
    }));
  }
}

См. Также Как динамически создавать модальные окна начальной загрузки как компоненты Angular2?

person Günter Zöchbauer    schedule 17.10.2016
comment
@Gunter, я получаю Duplicate identifier ошибку, делая то же самое, что и вы. - person Ahmad Baktash Hayeri; 21.02.2017
comment
Извините, но этой информации недостаточно. Вы можете предоставить Plunker? Я предлагаю вам создать новый вопрос, который показывает ваш код, Plunker и полное сообщение об ошибке. - person Günter Zöchbauer; 21.02.2017
comment
Ну, на самом деле вы можете подробнее рассказать, что делает эта часть private sharedService.subscribe ( ... )? Спецификатор доступа private, кажется, включен дважды, то есть один раз в параметрах конструктора, а затем в определении конструктора. - person Ahmad Baktash Hayeri; 21.02.2017
comment
Спасибо за подсказку. Один private был избыточным - исправлен. Другой также является избыточным, если sharedService и zone используются только для подписки в конструкторе. Также в сервисе должен быть наблюдаемый (я добавил someObservable). - person Günter Zöchbauer; 21.02.2017
comment
@ GünterZöchbauer, мне удалось реализовать логику связи между двумя отдельно загружаемыми модулями приложения, используя экземпляр Subject (который, я полагаю, наследуется от класса Observable) в службе, даже не вызывая zone.run(). Можете ли вы сказать мне, нужен ли мне ngZone здесь? - person Ahmad Baktash Hayeri; 23.02.2017
comment
Я так думаю, потому что в противном случае обнаружение изменений не запустится. Не в каждой ситуации требуется обнаружение изменений. В общем случае, скорее всего, вы это сделаете. - person Günter Zöchbauer; 23.02.2017
comment
Кроме того, нет ли синтаксической ошибки в выполнении ... .subscribe(data => this.zone.run() => { // event handler code here }), поскольку я не понимаю, как это работает? - person Ahmad Baktash Hayeri; 23.02.2017
comment
Вы правы, забыл ( и ) - исправлено. Спасибо за подсказку. Если вы загружаете два компонента из одного AppModule, тогда вам также не понадобится zone.run(), только если вы хотите взаимодействовать между двумя приложениями, которые загружаются индивидуально. В некоторых случаях ChangeDetectorRef.markForCheck(), ChangeDetectorRef.detectChanges(), ApplicationRef.tick(), setTimeout(...) или | async будут иметь аналогичный эффект и вызывать обнаружение изменений. zone.run() - действительно самый общий случай. - person Günter Zöchbauer; 23.02.2017
comment
@LeonardoX, ты прав. Тогда я не понимал модули должным образом и смешал это с аналогичными вопросами о совместном использовании между отдельными приложениями Angular, которые часто задавались ранее. - person Günter Zöchbauer; 15.09.2017
comment
providedIn: 'platform' проблема не устранена. Не могли бы вы взглянуть на stackoverflow.com/questions/66137969/ - person ; 10.02.2021

В соответствии с окончательной версией Angular 2 услуги, предоставляемые модулем, доступны для всех других модулей, которые его импортируют. Официальное руководство по стилю советует, что общесистемные сервисы (одиночные), которые должны повторно использоваться в любом месте приложения, должны предоставляться каким-то Core Module, то есть импортироваться в главный App Module, чтобы он быть инъекционным везде.

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

Вот модуль провайдера:

/// some.module.ts
import { NgModule } from '@angular/core';

import { SomeComponent }   from './some.component';

@NgModule({
    imports: [],
    exports: [],
    declarations: [SomeComponent],
    providers: [ MyService ], // <======================= PROVIDE THE SERVICE
})
export class SomeModule { }

Вот другой модуль, который хочет использовать MyService

/// some-other.module.ts
import { NgModule } from '@angular/core';

import { SomeModule } from 'path/to/some.module'; // <=== IMPORT THE JSMODULE

import { SomeOtherComponent }   from './some.other.component';

@NgModule({
    imports: [ SomeModule ], // <======================== IMPORT THE NG MODULE
    exports: [],
    declarations: [SomeOtherComponent],
    providers: [],
})
export class SomeOtherModule { }

Таким образом, сервис должен быть внедрен в любой компонент, объявленный SomeOtherModule, а в самом SomeModule - просто запросите его в конструкторе:

/// some-other.module.ts

import { MyService } from 'path/to/some.module/my-service';

/* ...
    rest of the module
*/

export class SomeOtherModule {
    constructor( private _myService: MyService) { <====== INJECT THE SERVICE
        this._myService.dosmth();
    }
}

Если это не дает ответа на ваш вопрос, я предлагаю вам переформулировать его.

person FacelessPanda    schedule 13.11.2016
comment
Я пробовал эту строку из вашего кода: import {MyService} из 'path / to / some.module / my-service'; Но я получаю сообщение об ошибке «path / to / some.module / my-service» не найден. - person Vishal; 19.04.2017
comment
@Vishal это обычно означает, что ваш транспилятор не может найти ваш файл по этому пути. Убедитесь, что путь к вашему служебному файлу (относительно источника проекта) и ваш оператор импорта совпадают (вам не нужно расширение в пути импорта). Но это проблема компиляции / транспиляции, а не Angular, поэтому я не буду здесь подробно останавливаться на этой проблеме. - person FacelessPanda; 20.04.2017
comment
Действительно ли этот ответ работает для модулей, загружаемых отдельно? Я пробовал, но у меня не работает. Он работает только в том случае, если общая служба используется в компонентах модуля. - person Dipendu Paul; 07.07.2017
comment
это не работает. Он создает два экземпляра каждой службы, поэтому вы на самом деле не используете службу ... Посмотрите здесь: stackoverflow.com/a / 46243224/1683040 - person LeonardoX; 15.09.2017
comment
У вас есть пример этого «CoreModule», службы которого используются в разных модулях приложения? - person Rich; 18.09.2017
comment
Это не работает. У LeonardoX есть рабочий эталон! Работал для меня stackoverflow.com/a/46243224/1683040 - person Anos K. Mhazo; 28.11.2017
comment
Не могли бы вы взглянуть на stackoverflow.com/questions/66137969/ - person ; 10.02.2021

  1. создать общий модуль

    @NgModule({})
    export class SharedModule {
        static forRoot(): ModuleWithProviders {
            return {
                ngModule: SharedModule,
                providers: [SingletonService]
            };
        }
    }
    
  2. в модуле приложения ваш родительский модуль приложения импортирует общий модуль, подобный этому

    SharedModule.forRoot()
    
  3. любые дочерние модули, если вам нужно импортировать общий модуль, импортируйте его без root

    SharedModule
    

https://angular-2-training-book.rangle.io/handout/modules/shared-modules-di.html.

person Hager Aly    schedule 15.01.2018
comment
Один из лучших ответов. Ionic делает то же самое. - person Kanchan; 27.06.2018

Ответ, который дал Гюнтер Цохбауэр, великолепен, однако у вас может возникнуть одна проблема:

window.sharedService

вызовет ошибку в TypeScript. Вы можете обойти это, заменив все экземпляры

window.sharedService

с участием

(<any>window).sharedService
person Ryan Anderson    schedule 20.10.2016
comment
Спасибо @Ryan за добавление. Немного занят другой частью приложения. придет к той части через несколько дней. - person Aniruddha Das; 20.10.2016
comment
Вы также можете расширить интерфейс окна: interface Window {sharedService: any} и избежать многократного повторения этого изменения с помощью ‹any›. - person Matthias Max; 14.07.2017

Я попытался использовать идеи в этом ответе для обмена данными между несколькими загружаемыми модулями в моем приложении angular. Данные, которые меня интересовали, поступали из вызова http.get (). Это был дорогостоящий звонок, и я хотел сделать звонок только один раз и поделиться его результатами между двумя моими загрузочными модулями.

Первоначально я сохранил службу как свойство в моем глобальном оконном объекте, как было предложено, но в конце я решил использовать статические переменные и вызвать publishReplay () на моем наблюдаемом, чтобы создать ReplaySubject для воспроизведения моих выбросов будущим подписчикам. Это позволило нескольким подписчикам обмениваться данными и выполнять вызов REST только один раз.

Одно предостережение ... В исходном вопросе задавался вопрос, как поделиться одной службой ... Этот ответ не разделяет одну службу ... Создается несколько служб, но данные распределяются, поскольку наблюдаемое хранится в статической переменной ... Это означает, что наблюдаемое сохраняется до тех пор, пока ваше приложение и из-за вызова publishReplay() любого, кто подписывается на наблюдаемое, воспроизводит ранее возвращенные данные.

Вот мой код:

import { Injectable } from '@angular/core';
import { Observable, ReplaySubject } from 'rxjs/Rx';
import { Http } from '@angular/http';

@Injectable()
export class InitService {

    static observable: Observable<number> = null;

    constructor(private http: Http) {}

    getInitData(): Observable<number> {
        if (InitService.observable == null) {
            InitService.observable = this.http.get('api/Init')
                .map(this.extractData)
                .publishReplay()
                .refCount()
                .catch(this.handleError);
        }
        return InitService.observable;
    }


    extractData(res: Response) {
        let body: any = res.json();
        return body || {};
    }

}

Надеюсь это поможет.

person birwin    schedule 11.06.2017

Я думаю, что ниже - аккуратное и чистое решение. Я использовал во многих проектах.

Создайте общий модуль (AdModule в моем случае)

/// ad.module.ts

import ...

// create shared service(AdmobService in my case) object, pass constructor params (if you required)
const admobService = new AdmobService(new AdMobFree(), new InAppPurchase()); 

@NgModule({
  providers: [
    ...,
    { provide: AdmobService, useValue: admobService }, //  use shared service here like this
  ]
})
export class AdModule { }

импортировать AdModule в любой модуль, где вы хотите использовать общую службу (AdmobService)

Модуль1

/// module1.ts
@NgModule({
  imports: [
    ...,
    AdModule, // import shared module
  ],
})
export class Module1 { }

Модуль 2

/// module2.ts
@NgModule({
  imports: [
    ...,
    AdModule, // import shared module
  ],
})
export class Module2 { }
person Sanjay Nishad    schedule 24.10.2019

Вы не можете использовать ответ @ Günter Zöchbauer, если вы вводите какую-либо зависимость от службы. Мое решение для служб, которым требуются дополнительные зависимости, - назначить их объекту window в конструкторе службы.

export class MyService {
  private Cahces = {};
  constructor(protected http: HttpClient, private logger: LogService) {
    if (!(<any>window).myService) {
      (<any>window).myService = this;
    }
    return (<any>window).myService;
  }
}

Это гарантирует, что будет только одна услуга, независимо от того, как услуга предоставляется в модулях.

person Nisan Coskun    schedule 10.07.2020