ฉันจะสร้างบริการเดี่ยวใน Angular 2 ได้อย่างไร


142

ฉันได้อ่านแล้วว่าการฉีดเมื่อการบูตสแตรปควรให้เด็กทุกคนใช้อินสแตนซ์เดียวกันร่วมกัน แต่ส่วนประกอบหลักและส่วนหัวของฉัน (แอพหลักรวมถึงส่วนหัวและเต้ารับเราเตอร์) แต่ละเครื่องได้รับอินสแตนซ์แยกจากบริการของฉัน

ฉันมี FacebookService ซึ่งฉันใช้เพื่อโทรไปที่ facebook javascript api และ UserService ที่ใช้ FacebookService นี่คือบูตของฉัน:

bootstrap(MainAppComponent, [ROUTER_PROVIDERS, UserService, FacebookService]);

จากการเข้าสู่ระบบของฉันดูเหมือนว่าการเรียก bootstrap เสร็จสิ้นจากนั้นฉันเห็น FacebookService จากนั้น UserService ที่ถูกสร้างขึ้นก่อนที่รหัสในแต่ละตัวสร้างการทำงาน, MainAppComponent, HeaderComponent และ DefaultComponent:

ป้อนคำอธิบายรูปภาพที่นี่


8
คุณแน่ใจหรือว่ายังไม่ได้เพิ่มUserServiceและFacebookServiceไปprovidersที่อื่น?
GünterZöchbauer

คำตอบ:


132

เจสันพูดถูก! มันเกิดจากการทำงานของการฉีดพึ่งพา มันขึ้นอยู่กับหัวฉีดแบบลำดับชั้น

มีหัวฉีดหลายตัวภายในแอปพลิเคชัน Angular2:

  • รูทที่คุณกำหนดค่าเมื่อทำการบู๊ตแอปพลิเคชันของคุณ
  • หัวฉีดต่อองค์ประกอบ ถ้าคุณใช้ส่วนประกอบภายในอีกอันหนึ่ง หัวฉีดองค์ประกอบเป็นลูกขององค์ประกอบหลักหนึ่ง ส่วนประกอบแอปพลิเคชัน (องค์ประกอบที่คุณระบุเมื่อเพิ่มแอปพลิเคชันของคุณ) มีหัวฉีดรูทเป็นพาเรนต์หนึ่ง)

เมื่อ Angular2 พยายามฉีดบางอย่างในตัวสร้างส่วนประกอบ:

  • มันมีลักษณะเป็นหัวฉีดที่เกี่ยวข้องกับส่วนประกอบ หากมีการจับคู่หนึ่งมันจะใช้มันเพื่อรับอินสแตนซ์ที่เกี่ยวข้อง อินสแตนซ์นี้ถูกสร้างขึ้นอย่างขี้เกียจและเป็นซิงเกิลตันสำหรับหัวฉีดนี้
  • หากไม่มีผู้ให้บริการในระดับนี้ก็จะดูที่ผู้ปกครองหลัก (และอื่น ๆ )

ดังนั้นหากคุณต้องการมีซิงเกิลตันสำหรับแอปพลิเคชันทั้งหมดคุณจะต้องกำหนดผู้ให้บริการทั้งที่ระดับรูทหัวฉีดหรือหัวฉีดส่วนประกอบแอปพลิเคชัน

แต่ Angular2 จะมองที่ต้นไม้หัวฉีดจากด้านล่าง ซึ่งหมายความว่าผู้ให้บริการในระดับต่ำสุดจะถูกนำมาใช้และขอบเขตของอินสแตนซ์ที่เกี่ยวข้องจะเป็นระดับนี้

ดูคำถามนี้สำหรับรายละเอียดเพิ่มเติม:


1
ขอบคุณที่อธิบายได้ดี มันเป็นเรื่องที่ต่อต้านง่ายสำหรับฉันเพราะมันแบ่งออกมาด้วยกระบวนทัศน์องค์ประกอบที่มีอยู่ในตัวของ angular 2 สมมุติว่าฉันสร้างคลังส่วนประกอบสำหรับ Facebook แต่ฉันต้องการให้พวกเขาใช้บริการซิงเกิล อาจมีส่วนประกอบในการแสดงภาพโปรไฟล์ของผู้ใช้ที่เข้าสู่ระบบและอีกคนหนึ่งที่โพสต์ แอพที่ใช้ส่วนประกอบเหล่านั้นจะต้องรวมบริการ Facebook เป็นผู้ให้บริการแม้ว่าจะไม่ได้ใช้บริการเอง ฉันสามารถคืนบริการด้วยวิธี 'getInstance ()' ที่จัดการซิงเกิลของบริการจริง ...
Jason Goemaat

@tThierryTemplier ว่าฉันจะทำตรงข้ามผมได้เรียนบริการทั่วไปฉันต้องการที่จะฉีดในองค์ประกอบหลาย แต่อินสแตนซ์อินสแตนซ์ใหม่ทุกครั้ง (ผู้ให้บริการ / ตัวเลือกคำสั่งควรจะเลิกใช้และลบออกในรุ่นถัดไป)
Boban Stojanovski

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

ดังนั้นเพื่อทำงานกับอินสแตนซ์เดียวของบริการควรถูกประกาศเป็นผู้ให้บริการใน app.module.ts หรือใน app.component.ts
user1767316

ประกาศแต่ละบริการใน app.module.ts เท่านั้นทำงานให้ฉันได้
user1767316

142

อัปเดต (Angular 6 +)

วิธีที่แนะนำในการสร้างบริการแบบซิงเกิลมีการเปลี่ยนแปลง ตอนนี้ขอแนะนำให้ระบุใน@Injectableมัณฑนากรในบริการที่ควรจะให้ไว้ใน 'ราก' ทำให้ฉันมีความรู้สึกอย่างมากและไม่จำเป็นต้องแสดงรายการบริการทั้งหมดที่มีให้ในโมดูลของคุณอีกต่อไป คุณเพียงแค่นำเข้าบริการเมื่อคุณต้องการและพวกเขาลงทะเบียนตัวเองในสถานที่ที่เหมาะสม นอกจากนี้คุณยังสามารถระบุโมดูลเพื่อที่จะได้รับก็ต่อเมื่อนำเข้าโมดูล

@Injectable({
  providedIn: 'root',
})
export class ApiService {
}

อัปเดต (เชิงมุม 2)

ด้วย NgModule วิธีที่จะทำตอนนี้ฉันคิดว่าคือการสร้าง 'CoreModule' ด้วยคลาสบริการของคุณในนั้นและแสดงรายการบริการในผู้ให้บริการโมดูล จากนั้นคุณนำเข้าโมดูลหลักในโมดูลแอพหลักของคุณซึ่งจะให้อินสแตนซ์เดียวให้กับเด็ก ๆ ที่ร้องขอคลาสนั้นในคอนสตรัคเตอร์ของพวกเขา:

CoreModule.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ApiService } from './api.service';

@NgModule({
    imports: [
        CommonModule
    ],
    exports: [ // components that we want to make available
    ],
    declarations: [ // components for use in THIS module
    ],
    providers: [ // singleton services
        ApiService,
    ]
})
export class CoreModule { }

AppModule.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { AppComponent } from './app.component';
import { CoreModule } from './core/core.module';

@NgModule({
    declarations: [ AppComponent ],
    imports: [
        CommonModule,
        CoreModule // will provide ApiService
    ],
    providers: [],
    bootstrap: [ AppComponent ]
})
export class AppModule { }

คำตอบเดิม

หากคุณแสดงรายชื่อผู้ให้บริการbootstrap()คุณไม่จำเป็นต้องระบุรายชื่อผู้ให้บริการในอุปกรณ์ตกแต่งภายในของคุณ:

import { ApiService } from '../core/api-service';

@Component({
    selector: 'main-app',
    templateUrl: '/views/main-app.html',
    // DO NOT LIST PROVIDERS HERE IF THEY ARE IN bootstrap()!
    // (unless you want a new instance)
    //providers: [ApiService]
})
export class MainAppComponent {
    constructor(private api: ApiService) {}
}

ในความเป็นจริงการระบุชั้นเรียนของคุณใน 'ผู้ให้บริการ' จะสร้างอินสแตนซ์ใหม่ของมันหากองค์ประกอบหลักใด ๆ แสดงรายการอยู่แล้วเด็ก ๆ ก็ไม่จำเป็นต้องทำและหากพวกเขาทำเช่นนั้นพวกเขาจะได้รับอินสแตนซ์ใหม่


1
ณ ตอนนี้ (มกราคม 2017) คุณจะแสดงรายการบริการภายใต้[providers]ในไฟล์โมดูลของคุณไม่ใช่bootstrap()ใช่ไหม?
Jason Swett

5
ทำไมไม่ใส่ApiServiceในAppModule's providers, และหลีกเลี่ยงความจำเป็นในการCoreModule? (ไม่แน่ใจว่านี่คือสิ่งที่ @JasonSwett แนะนำ)
Jan Aagaard

1
@JasonGoemaat คุณสามารถเพิ่มตัวอย่างวิธีใช้ในส่วนประกอบได้หรือไม่? เราสามารถนำเข้าApiServiceด้านบนได้ แต่ทำไมต้องมาใส่ใจกับมันในอาร์เรย์ผู้ให้บริการของ CoreModule แล้วนำเข้าอันนั้นใน app.module ... มันยังไม่ได้คลิกเพื่อฉัน
โฮแกน

ดังนั้นการให้บริการกับผู้ให้บริการโมดูลจะให้ซิงเกิลตันสำหรับโมดูลนั้น และการวางบริการบนผู้ให้บริการคอมโพเนนต์จะสร้างอินสแตนซ์ใหม่สำหรับแต่ละอินสแตนซ์ของส่วนประกอบ นั่นถูกต้องใช่ไหม?
BrunoLM

@BrunoLM ฉันสร้างแอปทดสอบเพื่อช่วยแสดงว่าเกิดอะไรขึ้น ที่น่าสนใจแม้ว่าจะTestServiceมีการระบุทั้งในคอร์และโมดูลแอพ แต่อินสแตนซ์นั้นไม่ได้ถูกสร้างขึ้นสำหรับโมดูลเพราะมันถูกจัดเตรียมโดยส่วนประกอบ ดังนั้นหากคุณให้บริการในโมดูลของคุณและไม่เคยใช้มันจะไม่มีการสร้างอินสแตนซ์
Jason Goemaat

24

ฉันรู้ว่าเชิงมุมมีหัวฉีดแบบลำดับชั้นเช่น Thierry กล่าว

แต่ฉันมีตัวเลือกอื่นที่นี่ในกรณีที่คุณพบกรณีใช้ที่คุณไม่ต้องการฉีดที่ผู้ปกครอง

เราสามารถบรรลุเป้าหมายดังกล่าวได้โดยการสร้างอินสแตนซ์ของบริการและให้ผลตอบแทนนั้นเสมอ

import { provide, Injectable } from '@angular/core';
import { Http } from '@angular/core'; //Dummy example of dependencies

@Injectable()
export class YourService {
  private static instance: YourService = null;

  // Return the instance of the service
  public static getInstance(http: Http): YourService {
    if (YourService.instance === null) {
       YourService.instance = new YourService(http);
    }
    return YourService.instance;
  }

  constructor(private http: Http) {}
}

export const YOUR_SERVICE_PROVIDER = [
  provide(YourService, {
    deps: [Http],
    useFactory: (http: Http): YourService => {
      return YourService.getInstance(http);
    }
  })
];

และจากนั้นในส่วนของคุณคุณใช้วิธีการให้แบบกำหนดเองของคุณ

@Component({
  providers: [YOUR_SERVICE_PROVIDER]
})

และคุณควรมีบริการแบบซิงเกิลตันโดยไม่ต้องขึ้นอยู่กับหัวฉีดแบบลำดับชั้น

ฉันไม่ได้บอกว่านี่เป็นวิธีที่ดีกว่าคือในกรณีที่บางคนมีปัญหาซึ่งเป็นไปไม่ได้ที่หัวฉีดแบบลำดับชั้น


1
ควรSHARE_SERVICE_PROVIDERจะYOUR_SERVICE_PROVIDERอยู่ในองค์ประกอบหรือไม่ นอกจากนี้ฉันสมมติว่าจำเป็นต้องนำเข้าไฟล์บริการเช่นปกติและตัวสร้างจะยังคงมีพารามิเตอร์ประเภท 'YourService' ใช่ไหม ฉันชอบสิ่งนี้ฉันคิดว่าช่วยให้คุณสามารถรับประกันซิงเกิลตันและไม่ต้องตรวจสอบให้แน่ใจว่าบริการถูกจัดลำดับชั้นไว้ นอกจากนี้ยังอนุญาตให้แต่ละส่วนประกอบได้รับสำเนาของตนเองโดยเพียงแค่แสดงรายการบริการprovidersแทนผู้ให้บริการรายเดียวใช่ไหม?
Jason Goemaat

@ JasonGoemaat คุณพูดถูก แก้ไขแล้วว่า YOUR_SERVICE_PROVIDERว่าคุณทำมันตรงเช่นเดียวกับในตัวสร้างและผู้ให้บริการขององค์ประกอบที่คุณเพิ่ม ใช่ส่วนประกอบทั้งหมดจะได้รับอินสแตนซ์เดียวกันโดยเพียงแค่เพิ่มในผู้ให้บริการ
Joel Almeida

ณ จุดที่นักพัฒนาใช้สิ่งนี้พวกเขาควรถามตัวเองว่า "ทำไมฉันถึงใช้ Angular เลยถ้าฉันไม่ใช้กลไกที่ Angular จัดหาให้สำหรับสถานการณ์นี้" การให้บริการในระดับแอปพลิเคชันควรเพียงพอสำหรับทุกสถานการณ์ภายในบริบทของแองกูลาร์
RyNo

1
+1 แม้ว่านี่จะเป็นวิธีที่จะสร้างบริการเดี่ยวมันจะทำหน้าที่อย่างมากเป็นวิธีการสร้างเป็นMultitonบริการโดยเพียงแค่การเปลี่ยนinstanceคุณสมบัติเป็นแผนที่ที่สำคัญค่าของอินสแตนซ์
Precastic

1
@RyNo ฉันสามารถจินตนาการแอพที่ไม่ต้องการบริการสำหรับทุกเส้นทางหรือโมดูลที่ใช้ซ้ำได้ซึ่งต้องการอินสแตนซ์แบบคงที่และต้องการใช้อินสแตนซ์เดียวกันกับโมดูลอื่น ๆ ที่ใช้งาน บางทีสิ่งที่สร้างการเชื่อมต่อเว็บซ็อกเก็ตไปยังเซิร์ฟเวอร์และประมวลผลข้อความ อาจมีเพียงไม่กี่เส้นทางในแอปที่ต้องการใช้ดังนั้นจึงไม่จำเป็นต้องสร้างอินสแตนซ์ของบริการและการเชื่อมต่อเว็บซ็อกเก็ตเมื่อแอปเริ่มต้นหากไม่จำเป็น คุณสามารถโปรแกรมรอบ ๆ เพื่อให้ส่วนประกอบเริ่มต้นบริการได้ทุกที่ที่ใช้ แต่ความต้องการซิงเกิลตันจะมีประโยชน์
Jason Goemaat

16

มีการเปลี่ยนแปลงไวยากรณ์ ตรวจสอบลิงค์นี้

การพึ่งพาเป็นซิงเกิลภายในขอบเขตของหัวฉีด ในตัวอย่างด้านล่างอินสแตนซ์ HeroService เดียวจะถูกใช้ร่วมกันระหว่าง HeroesComponent และ HeroListComponent

ขั้นตอนที่ 1 สร้างคลาส singleton ด้วย @Injectable decorator

@Injectable()
export class HeroService {
  getHeroes() { return HEROES;  }
}

ขั้นตอน 2. Inject in constructor

export class HeroListComponent { 
  constructor(heroService: HeroService) {
    this.heroes = heroService.getHeroes();
  }

ขั้นตอนที่ 3 ลงทะเบียนผู้ให้บริการ

@NgModule({
  imports: [
    BrowserModule,
    FormsModule,
    routing,
    HttpModule,
    JsonpModule
  ],
  declarations: [
    AppComponent,
    HeroesComponent,
    routedComponents
  ],
  providers: [
    HeroService
  ],
  bootstrap: [
    AppComponent
  ]
})
export class AppModule { }

เกิดอะไรขึ้นถ้าInjectableชั้นเรียนของฉันไม่ใช่บริการและมีเพียงstaticสตริงสำหรับการใช้งานทั่วโลก
Syed Ali Taqi

2
ชอบผู้ให้บริการนี้: [{ให้: 'API_URL', useValue: ' coolapi.com '}]
Whisher

7

การเพิ่ม@Injectableมัณฑนากรในบริการและการลงทะเบียนเป็นผู้ให้บริการในโมดูลรูทจะทำให้เป็นซิงเกิลตัน


แค่บอกฉันว่าฉันเข้าใจไหม ถ้าฉันทำในสิ่งที่คุณพูดโอเคมันจะเป็นซิงเกิล ถ้านอกจากนั้นบริการยังเป็นผู้ให้บริการในโมดูลอื่นมันจะไม่กลายเป็นซิงเกิลใช่มั้ย เนื่องจากลำดับชั้น
heringer

1
และห้ามลงทะเบียนผู้ให้บริการใน @Component decorator ของหน้าเว็บ
ลอร่า

@laura ฉันยังคงนำเข้าสู่ส่วนประกอบที่ใช้บริการจริงหรือไม่
ทำเครื่องหมาย

@ มาร์คใช่คุณต้องนำเข้าแล้วคุณจะต้องประกาศในconstructorลักษณะนี้: import { SomeService } from '../../services/some/some'; @Component({ selector: 'page-selector', templateUrl: 'page.html', }) export class SomePage { constructor( public someService: SomeService ) { }
ลอร่า

6

ดูเหมือนว่าจะทำงานได้ดีสำหรับฉัน

@Injectable()
export class MyStaticService {
  static instance: MyStaticService;

  constructor() {
    return MyStaticService.instance = MyStaticService.instance || this;
  }
}

8
ฉันเรียกสิ่งนี้ว่ารูปแบบการต่อต้าน Angular2 ให้บริการอย่างถูกต้องและ Angular2 จะฉีดอินสแตนซ์เดียวกันเสมอ ดูstackoverflow.com/questions/12755539/…
GünterZöchbauer

3
@GünterZöchbauerโปรดให้คำแนะนำบางอย่างเกี่ยวกับ "ให้บริการได้อย่างถูกต้องและ Angular2 มักจะฉีดอินสแตนซ์เดียวกัน" ? เพราะมันไม่ชัดเจนและฉันไม่สามารถหาข้อมูลที่เป็นประโยชน์ได้โดย googling
nowiko

ฉันเพิ่งโพสต์คำตอบนี้ที่อาจช่วยตอบคำถามของคุณstackoverflow.com/a/38781447/217408 (ดูลิงค์ที่นั่น)
GünterZöchbauer

2
มันสมบูรณ์แบบ คุณควรใช้การฉีดพึ่งพาตัวเองของ Angulars แต่ไม่มีอันตรายใด ๆ ในการใช้รูปแบบนี้เพื่อให้แน่ใจว่าบริการของคุณเป็นแบบซิงเกิลเมื่อคุณคาดหวัง อาจช่วยประหยัดเวลาในการค้นหาข้อผิดพลาดเพียงเพราะคุณฉีดบริการเดียวกันในสองแห่ง
PaulMolloy

ฉันใช้รูปแบบนี้เพื่อยืนยันว่าปัญหาที่ฉันกำลังเผชิญอยู่นั้นเป็นเพราะการบริการไม่ได้เป็นแบบซิงเกิล
hr-tis

5

นี่คือตัวอย่างการทำงานกับ Angular เวอร์ชัน 2.3 เพียงแค่เรียกนวกรรมิกของการบริการด้วยวิธีนี้เหมือนกับ constructor นี้ (private _userService: UserService) และมันจะสร้างซิงเกิลตันสำหรับแอพ

user.service.ts

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Rx';
import { Subject }    from 'rxjs/Subject';
import { User } from '../object/user';


@Injectable()
export class UserService {
    private userChangedSource;
    public observableEvents;
    loggedUser:User;

    constructor() {
       this.userChangedSource = new Subject<any>();
       this.observableEvents = this.userChangedSource.asObservable();
    }

    userLoggedIn(user:User) {
        this.loggedUser = user;
        this.userChangedSource.next(user);
    }

    ...
}

app.component.ts

import { Component } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { UserService } from '../service/user.service';
import { User } from '../object/user';

@Component({
    selector: 'myApp',
    templateUrl: './app.component.html'
})
export class AppComponent implements OnInit {
    loggedUser:User;

    constructor(private _userService:UserService) { 
        this._userService.observableEvents.subscribe(user => {
                this.loggedUser = user;
                console.log("event triggered");
        });
    }
    ...
}

4

A singleton serviceคือบริการที่มีอินสแตนซ์เดียวเท่านั้นที่มีอยู่ในแอป

มีวิธี(2) ในการให้บริการแบบซิงเกิลสำหรับใบสมัครของคุณ

  1. ใช้providedInคุณสมบัติหรือ

  2. จัดหาโมดูลโดยตรงในAppModuleแอปพลิเคชัน

ใช้ที่มีให้ใน

เริ่มต้นด้วย Angular 6.0 วิธีที่ต้องการในการสร้างบริการซิงเกิลตันคือตั้งค่าprovidedInให้รูทบน@Injectable()มัณฑนากรของบริการ สิ่งนี้จะบอก Angular เพื่อให้บริการในแอปพลิเคชันรูท

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

@Injectable({
  providedIn: 'root',
})
export class UserService {
}

อาร์เรย์ผู้ให้บริการ NgModule

ในแอพที่สร้างขึ้นด้วยเวอร์ชันเชิงมุมก่อนหน้า 6.0 บริการจะลงทะเบียนผู้ให้บริการ NgModule อาร์เรย์ดังนี้

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

หากนี่NgModuleคือรูAppModuleท UserService จะเป็นซิงเกิลตันและใช้ได้ตลอดทั้งแอพ แม้ว่าคุณจะเห็นรหัสนี้ด้วยวิธีนี้การใช้providedInคุณสมบัติของ@Injectable()มัณฑนากรบนบริการนั้นเป็นที่นิยมมากกว่า Angular 6.0 เพราะทำให้บริการของคุณเป็นที่นิยม


3

คุณสามารถใช้useValueในผู้ให้บริการ

import { MyService } from './my.service';

@NgModule({
...
  providers: [ { provide: MyService, useValue: new MyService() } ],
...
})

5
useValueไม่เกี่ยวข้องกับซิงเกิล Usevalue เป็นเพียงการส่งผ่านค่าแทนType( useClass) ที่ DI เรียกnewหรือuseFactoryที่ผ่านฟังก์ชั่นที่ส่งกลับค่าเมื่อเรียกโดย DI Angular DI รักษาอินสแตนซ์เดียวต่อผู้ให้บริการโดยอัตโนมัติ ให้เพียงครั้งเดียวและคุณมีซิงเกิล ขออภัยฉันต้องลงคะแนนเพราะเป็นเพียงข้อมูลที่ไม่ถูกต้อง: - /
GünterZöchbauer

3

จากเชิงมุม @ 6 คุณสามารถมีในprovidedInInjectable

@Injectable({
  providedIn: 'root'
})
export class UserService {

}

ตรวจสอบเอกสารที่นี่

มีสองวิธีในการให้บริการแบบซิงเกิลตันใน Angular:

  1. ประกาศว่าควรให้บริการในรูทแอปพลิเคชัน
  2. รวมบริการใน AppModule หรือในโมดูลที่นำเข้าโดย AppModule เท่านั้น

เริ่มต้นด้วย Angular 6.0 วิธีที่ต้องการในการสร้างบริการซิงเกิลตันคือการระบุบริการที่ควรให้ไว้ในรูทแอปพลิเคชัน สิ่งนี้ทำได้โดยการตั้งค่าที่ระบุไว้ในรูทบนมัณฑนากร @Injectable ของบริการ:


นี้เป็นสิ่งที่ดี public staticแต่คุณยังอาจมีปัญหาที่ไม่คาดคิดกับตัวแปรที่ไม่ได้มีที่อาจจะได้รับการแก้ไขโดยประกาศบางรายการ
cjbarth

2

เพียงประกาศบริการของคุณในฐานะผู้ให้บริการใน app.module.ts เท่านั้น

มันทำงานให้ฉันได้

providers: [Topic1Service,Topic2Service,...,TopicNService],

จากนั้นก็ทำการติดตั้งโดยใช้พารามิเตอร์ส่วนตัวของคอนสตรัค:

constructor(private topicService: TopicService) { }

หรือเนื่องจากหากใช้บริการของคุณจาก html ตัวเลือก -prod จะอ้างสิทธิ์:

Property 'topicService' is private and only accessible within class 'SomeComponent'.

เพิ่มสมาชิกสำหรับบริการของคุณและกรอกด้วยอินสแตนซ์ที่ได้รับในตัวสร้าง:

export class SomeComponent {
  topicService: TopicService;
  constructor(private topicService: TopicService) { 
    this.topicService= topicService;
  }
}

0
  1. หากคุณต้องการให้บริการแบบซิงเกิลที่ระดับแอพพลิเคชั่นคุณควรกำหนดไว้ในapp.module.ts

    ผู้ให้บริการ: [MyApplicationService] (คุณสามารถกำหนดเหมือนกันในโมดูลย่อยเช่นกันเพื่อให้เป็นโมดูลเฉพาะ)

    • อย่าเพิ่มบริการนี้ในผู้ให้บริการซึ่งสร้างอินสแตนซ์สำหรับองค์ประกอบนั้นที่ทำลายแนวคิดของซิงเกิลเพียงแค่แทรกผ่านคอนสตรัคเตอร์
  2. หากคุณต้องการกำหนดบริการซิงเกิลที่ระดับบริการสร้างส่วนประกอบเพิ่มบริการนั้นใน app.module.ts และเพิ่มในอาร์เรย์ผู้ให้บริการภายในองค์ประกอบเฉพาะดังแสดงในตัวอย่างด้านล่าง

    @Component ({selector: 'app-root', templateUrl: './test.component.html', styleUrls: ['./test.component.scss'] ผู้ให้บริการ: [TestMyService]})

  3. Angular 6 เป็นวิธีใหม่ในการเพิ่มบริการในระดับแอปพลิเคชัน แทนที่จะเพิ่มคลาสบริการลงในอาร์เรย์ [] ใน AppModule คุณสามารถตั้งค่าคอนฟิกต่อไปนี้ใน @Injectable ():

    @Injectable ({ที่ระบุไว้: 'รูท'}) เอ็กซ์พอร์ตคลาส MyService {... }

"ไวยากรณ์ใหม่" มีข้อได้เปรียบอย่างหนึ่งแม้ว่า: สามารถโหลดบริการอย่างเกียจคร้านโดย Angular (เบื้องหลัง) และสามารถลบรหัสซ้ำซ้อนได้โดยอัตโนมัติ สิ่งนี้สามารถนำไปสู่ประสิทธิภาพที่ดีขึ้นและความเร็วในการโหลด - แม้ว่านี่จะเป็นการเริ่มต้นสำหรับบริการและแอพที่ใหญ่กว่าโดยทั่วไปเท่านั้น


0

นอกจากคำตอบที่ดีเลิศข้างต้นอาจมีสิ่งอื่นที่ขาดหายไปหากสิ่งต่างๆในซิงเกิลตันของคุณยังคงไม่ทำตัวเหมือนซิงเกิล ฉันพบปัญหาเมื่อเรียกใช้ฟังก์ชันสาธารณะบนซิงเกิลตันและพบว่ามันใช้ตัวแปรผิด ปรากฎว่าปัญหาthisคือไม่รับประกันว่าจะผูกพันกับซิงเกิลตันสำหรับฟังก์ชั่นสาธารณะใด ๆ ในซิงเกิล สิ่งนี้สามารถแก้ไขได้โดยทำตามคำแนะนำที่นี่เช่น:

@Injectable({
  providedIn: 'root',
})
export class SubscriptableService {
  public serviceRequested: Subject<ServiceArgs>;
  public onServiceRequested$: Observable<ServiceArgs>;

  constructor() {
    this.serviceRequested = new Subject<ServiceArgs>();
    this.onServiceRequested$ = this.serviceRequested.asObservable();

    // save context so the singleton pattern is respected
    this.requestService = this.requestService.bind(this);
  }

  public requestService(arg: ServiceArgs) {
    this.serviceRequested.next(arg);
  }
}

หรือคุณก็สามารถประกาศให้สมาชิกชั้นเรียนเป็นpublic staticแทนpublicแล้วบริบทจะไม่ว่า แต่คุณจะต้องเข้าถึงพวกเขาชอบแทนการใช้ฉีดพึ่งพาและการเข้าถึงพวกเขาผ่านทางSubscriptableService.onServiceRequested$this.subscriptableService.onServiceRequested$


0

บริการพ่อแม่และลูก

ฉันมีปัญหากับบริการผู้ปกครองและลูกของมันโดยใช้อินสแตนซ์ที่แตกต่างกัน ในการบังคับให้ใช้หนึ่งอินสแตนซ์คุณสามารถนามแฝงพาเรนต์โดยอ้างอิงกับลูกในผู้ให้บริการโมดูลแอป ผู้ปกครองจะไม่สามารถเข้าถึงคุณสมบัติของเด็กได้ แต่จะใช้อินสแตนซ์เดียวกันสำหรับบริการทั้งสองรายการ https://angular.io/guide/dependency-injection-providers#aliased-class-providers

app.module.ts

providers: [
  ChildService,
  // Alias ParentService w/ reference to ChildService
  { provide: ParentService, useExisting: ChildService}
]

บริการที่ใช้โดยส่วนประกอบนอกขอบเขตโมดูลแอพของคุณ

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

-outside.component.ts ของฉัน

@Component({...})
export class MyOutsideComponent {
  @Input() serviceInstance: MyOutsideService;
  ...
}

-inside.component.ts ของฉัน

  constructor(public myService: MyOutsideService) { }

ฉัน-inside.component.hmtl

<app-my-outside [serviceInstance]="myService"></app-my-outside>

คุณหมายถึงตอบคำถามของคุณเองหรือ ถ้าเป็นเช่นนั้นคุณสามารถแยกคำตอบออกเป็นคำตอบอย่างเป็นทางการใน StackOverflow โดยตัด / วางคำตอบลงในฟิลด์คำตอบหลังจากที่โพสต์คำถาม
ryanwebjackson
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.