How SHOULD I inject document and window instances in angular2? [duplicate] - javascript

I have an Angular 2 application. For mocking the Document object in tests, I'd like to inject it to the service like:
import { Document } from '??'
#Injectable()
export class MyService {
constructor(document: Document) {}
}
The Title service of Angular uses the internal getDOM() method.
Is there any simple way to inject the Document to the service? Also, how should I reference it in the providers array?

This has been supported by Angular for a while.
You can use the DOCUMENT constant provided by the #angular/common package.
Description of the DOCUMENT constant (taken from the API documentation):
A DI Token representing the main rendering context. In a browser, this is the DOM Document.
An example is as shown below:
my-service.service.ts:
import { Inject, Injectable } from '#angular/core';
import { DOCUMENT } from '#angular/common';
#Injectable()
export class MyService {
constructor(#Inject(DOCUMENT) private document: Document) {}
}
my-service.service.spec.ts
import { provide } from '#angular/core';
import { DOCUMENT } from '#angular/common';
import { MyService } from './my-service';
class MockDocument {}
describe('MyService', () => {
beforeEachProviders(() => ([
provide(DOCUMENT, { useClass: MockDocument }),
MyService
]));
...
});

I'm unable to comment directly on adamdport's question (not yet 50 rep points), but here it is as stated in the angular docs.
Blockquote
#GünterZöchbauer it looks like DOCUMENT is deprecated. Any idea how to do this once it's gone? For example, how would I set the favicon dynamically?
Instead of importing from platform browser like so:
import { DOCUMENT } from '#angular/platform-browser';
Import it from angular common:
import {DOCUMENT} from '#angular/common';

in addition to #Günter Zöchbauer's answer.
Angular define DOCUMENT as an InjectionToken
export const DOCUMENT = new InjectionToken<Document>('DocumentToken');
dom_tokens.ts
And inject it with document in browser.ts
{provide: DOCUMENT, useFactory: _document, deps: []}
export function _document(): any {
return document;
}
Therefore, when we use it, we just need to inject #Inject(DOCUMENT)
or use the token directly in deps:[DOCUMENT]

import { Inject, Injectable } from '#angular/core';
import { DOCUMENT } from '#angular/common';
#Injectable()
export class MyService {
constructor(#Inject(DOCUMENT) private document) {}
}
It's the ": Document" that's causing the problem.

Related

Angular: How to override the shared module service from different module

I have multiple modules say SchoolModule, UniversityModule, SharedModule
SharedModule has BaseService to which both SchoolModule and UniversityModule providers are extending
Now when I load my SchoolModule, I want BaseService should get the implementation of schoolService, and the same goes for UniversityModule
Structure
app
-- SharedModule
-- base.service
-- secret.service uses base.service
-- shared.component uses secret.service
-- SchoolModule
-- school.component uses shared.component
-- school.service
-- UniversityModule
-- university.component uses shared.component
-- university.service
StackBlitz
So how I can achieve this with Dependency Injection?
You have to declare your base service class as an abstract class with an abstract method getName()
export abstract class BaseService{
abstract getName(): string { return 'Base Service' }
}
export class SchoolService extends BaseService{
getName(): string { return 'School Service' }
}
Unfortunately, Angular2 can not inject a class from another module without get the import between modules, and if you need to lazy load it, things do not work.
There is a project that load dynamically a component from another module.
It is difficult to import this in to your project, but saves you to not code twice.
This project can be found here.
The problem is that SecretService provided is the one build with build in UniversityModule last imported module in App Module so if you want to have SecretService update with correctly BaseService you have to provide both BaseService and SecretService in university and school component something like this in school.component.ts:
import { SchoolService } from './school.service'
import { BaseService } from '../shared/base.service'
import { SecretService } from '../shared/secret.service'
#Component({
selector: 'app-school',
template: `SchoolName :: <app-shared></app-shared>`,
providers: [
{ provide: BaseService, useClass: SchoolService },
SecretService]
})
export class SchoolComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}
and in university.component.ts
import {  UniversityService } from './university.service'
import { BaseService } from '../shared/base.service'
import { SecretService } from '../shared/secret.service'
#Component({
selector: 'app-university',
template: `UniversityName :: <app-shared></app-shared>`,
providers: [{ provide: BaseService, useClass: UniversityService },
SecretService]
})
export class UniversityComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}
But why you wanna wrap base service in SecretService instead directly use BaseService in shared component ?
Using BaseService like this in shared.component.ts
import { Component, OnInit } from '#angular/core';
import { BaseService } from './base.service'
#Component({
selector: 'app-shared',
template: `{{name}}`
})
export class SharedComponent implements OnInit {
name: string
constructor(private ss: BaseService) {
this.name = ss.getName()
}
ngOnInit() {
}
}
Using BaseService you will solve this injection problem without any workaround on SecretService

Angular 5 service replacement/override

I created a core library for my project containing some components and services. I built the library with ng-packagr. In the consuming project which references the library I built my webapp containing components provided by the library. Nothing special so far. But sometimes I want a component (coming from my lib) calling a method from a Service outside of the lib. Is this possible? Can I somehow inject a service to a component which is defined inside a library?
Cheers
I've achieved this before with something like this:
Your library's service(s) should be defined as an interface rather than as a concrete implementation (as is done in OO languages quite often). If your implementing application will only sometimes want to pass in its own version of the service then you should create a Default service in your library, and use it as so:
import { Component, NgModule, ModuleWithProviders, Type, InjectionToken, Inject, Injectable } from '#angular/core';
export interface ILibService {
aFunction(): string;
}
export const LIB_SERVICE = new InjectionToken<ILibService>('LIB_SERVICE');
export interface MyLibConfig {
myService: Type<ILibService>;
}
#Injectable()
export class DefaultLibService implements ILibService {
aFunction() {
return 'default';
}
}
#Component({
// whatever
})
export class MyLibComponent {
constructor(#Inject(LIB_SERVICE) libService: ILibService) {
console.log(libService.aFunction());
}
}
#NgModule({
declarations: [MyLibComponent],
exports: [MyLibComponent]
})
export class LibModule {
static forRoot(config?: MyLibConfig): ModuleWithProviders {
return {
ngModule: LibModule,
providers: [
{ provide: LIB_SERVICE, useClass: config && config.myService || DefaultLibService }
]
};
}
}
Then in your implementing application you have the ability to pass in the optional config via your library's forRoot method (note that forRoot should only be called once per application and at the highest level possible). Note that I've marked the config parameter as optional, so you should call forRoot even if you have no config to pass.
import { NgModule, Injectable } from '#angular/core';
import { LibModule, ILibService } from 'my-lib';
#Injectable()
export class OverridingService implements ILibService {
aFunction() {
return 'overridden!';
}
}
#NgModule({
imports: [LibModule.forRoot({ myService: OverridingService })]
})
export class ImplementingModule {
}
This was from memory as I don't have the code to hand at the moment so if it doesn't work for any reason let me know.

Sending whole object from one component to another in angular 2

I have a problem. I don't know how to send object from one component to another.
In first component cinema.component.html I have following function call:
<a title="Reserve" (click)="openReservationPage(repertoire)">Reserve</a>
In cinema.component.ts file, for that .html I have something like:
openReservationPage(repertoire: UpcomingRepertoire) {
this.router.navigate(['/reserve', {repertoire: JSON.stringify(repertoire)}]);
}
My app.routes.ts file contains appropriate routing:
{ path: 'reserve', component: ReserveFormComponent }
How can I use this repertoire object in another page reserve-form.component.ts and reserve-form.component.html ?
As an answer for the question in the title, i would said create a service to pass data between components.
Since its a router implementation you can pass the repertoire as a route parameter.
Follow these steps:
1)Modify the route in app.routes.ts to take a param
{ path: 'reserve/:repertoire', component: ReserveFormComponent }
2)In cinema.component.ts pass the repertoire as param
this.router.navigate(['/reserve',JSON.stringify(repertoire)]‌​);
3)Extract the param in reserve-form.component.ts
First of all you need to import
import {ActivatedRoute } from "#angular/router";
Technique 1
repertoire:any;
constructor(private activatedRoute: ActivatedRoute) {
this.repertoire = JSON.parse(activatedRoute.snapshot.params["repertoire"]);
}
Technique 2
import { Subscription } from "rxjs/Rx";
private subscription: Subscription;
repertoire:any;
constructor(private activatedRoute: ActivatedRoute) {
this.subscription = activatedRoute.params.subscribe(
(param: any) => this.repertoire = JSON.parse(param['repertoire'])
);
}
ngOnDestroy() { // here we unsubscribe to the observable
this.subscription.unsubscribe();
}
Further Explanation :
Technique 1 is adopted when you are sure that the param will be passed every time you navigate to the component.
Technique 2 is a subscription to the observable once there a param published but don't forget to unsubscribe in the ngOnDestroy() component's life cycle method to prevent memory leak.
It is more preferable because some times there a scenario that a param is passed to a component after it was created where the snapshot method wouldn't capture and it more flexible with different scenario than the basic one in technique 1.
The link below explains how you can do this. I've recently used this to create a messaging service. The example below, shows the code for a simple messaging service. It allows you to pass a number between components, just change the to I guess. You can also write out to local storage, but It seems services are more popular. Once you've got your head around them, they're easy to re-use.
Hope this helps
Sharing Data Between Angular Components - Four Methods
Message Service (PmMessageService)
import { Injectable } from '#angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
#Injectable()
export class PmMessageService
{
private pillMenuIndexBS = new BehaviorSubject <number> (null);
pillMenuIndex = this.pillMenuIndexBS.asObservable();
constructor() {}
setPillMenuIndex(index : number)
{
this.pillMenuIndexBS.next(index);
}
}
Component consuming message service, setting a value
import { Component, OnInit } from '#angular/core';
import { PmMessageService } from '../pm-message-service/pm-message.service'
import { BrowserModule } from '#angular/platform-browser';
import { platformBrowserDynamic } from '#angular/platform-browser-dynamic';
#Component({
selector: 'app-pm-configure',
templateUrl: './pm-configure.component.html',
styleUrls: ['./pm-configure.component.css']
})
export class PmConfigureComponent implements OnInit
{
constructor (private messageService : PmMessageService) {}
ngOnInit()
{
this.messageService.setPillMenuIndex(1);
}
}
Component consuming and subscribing.
import { Component, OnInit } from '#angular/core';
import { PmMessageService } from '../pm-message-service/pm-message.service'
import { BrowserModule } from '#angular/platform-browser';
import { platformBrowserDynamic } from '#angular/platform-browser-dynamic';
#Component({
selector: 'pm-bs-navbar',
templateUrl: './pm-bs-navbar.component.html',
styleUrls: ['./pm-bs-navbar.component.css']
})
export class PmBsNavbarComponent implements OnInit
{
tabActiveNumber;
constructor (private messageService : PmMessageService) {}
ngOnInit()
{
this.messageService.pillMenuIndex.subscribe(index => this.tabActiveNumber = index)
}
}

How to use document.write in Angular 2

In my normal HTML page I am having a simple 4 line code shown below:
<script>
var getData = JSON.parse(localStorage.getItem("templateType"));
document.write(getData.template_code); // in angular its wriiten as getData["template_code"]
console.log(getData.template_code);
document.getElementById("main-wrapper").innerHTML = getData.template_code;
</script>
How can I do the same in Angular 2, I have a component webview.component.html and webview.component.ts?
You can import document object inside your angular component in this way:
import { Inject, Injectable } from '#angular/core';
import { DOCUMENT } from '#angular/platform-browser';
So you use it:
#Injectable()
export class MyService {
constructor(#Inject(DOCUMENT) private document: any) {
document.write(getData.template_code);
}
}
There is a pretty similar question here.

A way to load service to all of the components in angular 2

I built a service that called lang.service.ts. What it does is simply a key: value mechanism. Whenever I need it to my component, I import it and declare it in the constructor and then I use it as {{lang('key').text}}. so far, so good.
The thing is that I've noticed that I'm gonna load it for each component, like for view-header.component.ts, view-footer.components.ts and a lot of other components. I never used Angular 1, but IIRC, I could do there something like rootScope.lang(..) and it could've accomplish what I was looking for. Is there a way to do something like that in Angular 2?
If you register your service at your root componet, all child components will have access to the service.
On your root component...
import { Component } from '#angular/core';
import { YourService } from './my-servive.service.ts';
import { ChildComponent } from './child-component.component.ts';
#Component({
selector: 'root-component',
providers: [YourService],
directives: [ChildComponent],
template: `<child-component></child-component>`
})
export class RootComponent {}
On your child component...
import { Component } from '#angular/core';
import { YourService } from './my-servive.service.ts';
#Component({
selector: 'child-component'
})
export class ChildComponent {
contructor(private myService: YourService) {}
}
Hope this help you.

Categories