I'm new to Angular5 and TypeScript, so it's very possible it's a simple thing I'm overlooking.
I have an Angular hybrid app that uses ngUpgrade to run AngularJS and Angular5 side-by-side. I'm trying to inject $templateCache into the OnAppInit function so that I can load all the AngularJS HTML templates before the app completely initializes. I'm getting the error "Cannot find name '$templateCacheService'" as indicated below. Is my syntax wrong or is this not possible?
I "upgrade" $templateCache in upgraded-providers.ts like this:
import { InjectionToken, Directive, ElementRef, Injector } from '#angular/core';
import { UpgradeComponent } from '#angular/upgrade/static';
export const $templateCacheService = new InjectionToken<any>('$templateCacheService');
export const $templateCacheServiceProvider = {
provide: $templateCacheService,
useFactory: (i: any) => i.get('$templateCache'),
deps: ['$injector']
};
Then in app.module.ts, I try to inject it into OnAppInit:
import { NgModule, APP_INITIALIZER, Inject } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { BrowserAnimationsModule } from '#angular/platform-browser/animations';
import { MatCommonModule } from '#angular/material';
import { FlexLayoutModule } from '#angular/flex-layout';
import { HttpClientModule, HttpClient, HTTP_INTERCEPTORS } from '#angular/common/http';
import { downgradeInjectable, UpgradeModule, downgradeComponent } from '#angular/upgrade/static';
import { environment } from '../environments/environment';
import {
$templateCacheServiceProvider,
$templateCacheService
} from './upgraded-providers';
import { AppComponent } from './app.component';
import { GlobalVarsService } from './core/global-vars.service';
import { WinAuthInterceptor } from './core/interceptors/win-auth.interceptor';
declare var angular: any;
#NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
BrowserAnimationsModule,
MatCommonModule,
FlexLayoutModule,
HttpClientModule,
UpgradeModule
],
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: WinAuthInterceptor,
multi: true
},
{
provide: APP_INITIALIZER,
useFactory: OnAppInit,
multi: true,
deps: [GlobalVarsService, HttpClient, $templateCacheService]
},
GlobalVarsService,
$templateCacheServiceProvider
]
})
export class AppModule {
constructor(private upgrade: UpgradeModule, private http: HttpClient) { }
ngDoBootstrap() {
angular.module('app').factory('globalVars', downgradeInjectable(GlobalVarsService));
this.upgrade.bootstrap(document.body, ['app'], { strictDi: true });
}
}
////// THIS NEXT LINE GETS error TS2304: Cannot find name '$templateCacheService' /////
export function OnAppInit(globalVars: GlobalVarsService, http: HttpClient, $templateCache: $templateCacheService) {
return (): Promise<any> => {
return new Promise((resolve, reject) => {
http.get(environment.apiBase + '/api/meta/data').subscribe(x => {
globalVars.MetaData = x;
globalVars.VersionNumber = globalVars.MetaData.versionNumber;
globalVars.IsDebugBuild = globalVars.MetaData.isDebugBuild;
globalVars.User = globalVars.MetaData.user;
globalVars.ApiBase = environment.apiBase;
globalVars.Templates.forEach(template => {
$templateCache.put(template.Item1, template.Item2);
});
resolve();
});
});
};
}
This is TypeScript type error, it doesn't affect how the application works (as long as compilation errors are ignored).
templateCacheService is not a valid type here, because $templateCacheService is a variable (injection token), not a type or an interface.
Only Angular class constructors are annotated with types for DI. Since factory functions are annotated with deps property, types in function signature exist only to provide type safety. If it's not needed, types can be skipped:
export function OnAppInit(
globalVars: GlobalVarsService, http: HttpClient,
$templateCache
) { ... }
Otherwise proper types should be used. $templateCache is an object with get, put, etc methods. Appropriate types are provided with AngularJS #types/angular type definitions. It will be something like:
export function OnAppInit(
globalVars: GlobalVarsService, http: HttpClient,
$templateCache: ng.ITemplateCacheService
) { ... }
Related
I am using angular 15.0 and installed #auth0/angular-jwt. the App.Module has configured as follows:
import {HttpClientModule} from "#angular/common/http";
import { JwtModule } from "#auth0/angular-jwt";
export function tokenGetter() {
return localStorage.getItem("jwt");
}
#NgModule({
declarations: [
...
],
imports: [
....
HttpClientModule,
...
JwtModule.forRoot({
config:{
tokenGetter: tokenGetter,
whitelistedDomains: ["localhost:5001"],
blacklistedRoutes: []
}
})
],
providers: [
....
],
exports: [
],
bootstrap: [AppComponent]
})
export class AppModule {
constructor(library: FaIconLibrary) {
library.addIconPacks(far, fab);
}
}
at first, when I imported the JWTModule, I got warnning that: Argument type {config: {tokenGetter: () => string | null, blacklistedRoutes: any[], whitelistedDomains: string[]}} is not assignable to parameter type Routes but no error!. so far, when I run the app, no pages is loaded and in the console this error is rised: NG0203: inject() must be called from an injection context such as a constructor, a factory function, a field initializer, or a function used with EnvironmentInjector#runInContext.I found that this error is only for importing JwtModule and when I remove this, the main page of app is loaded and everything is normal. also, I created an AuthGuard service, I haven't used it yet, Because I assumed that the above error should be resolved first. the snippet code for service is:
import { Injectable } from '#angular/core';
import {ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot} from "#angular/router";
import { JwtHelperService } from '#auth0/angular-jwt';
#Injectable({
providedIn: 'root'
})
export class AuthGuard implements CanActivate {
constructor(private router:Router, private jwtHelper: JwtHelperService){}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
const token = localStorage.getItem("jwt");
if (token && !this.jwtHelper.isTokenExpired(token)){
return true;
}
this.router.navigate(["login"]);
return false;
}
}
Thank you for your guidance on the correct use of the JwtModule of the Auth0/angular-jwt.
I'm trying access a database using a custom provider as per this guide. At startup, Nestjs throws the error Nest can't resolve dependencies of the EventsService (?). Please make sure that the argument DATA_SOURCE at index [0] is available in the AppModule context.
Here are my files
Database providers
import { DataSource } from 'typeorm';
export const databaseProviders = [
{
provide: 'DATA_SOURCE',
useFactory: async () => {
const dataSource = new DataSource({
type: "mysql",
host: "host",
port: 3306,
username: "username",
password: "password",
synchronize: true,
logging: true,
});
return dataSource.initialize();
},
},
];
Database module
import { databaseProviders } from "./database.providers";
import { Module } from "#nestjs/common";
#Module({
providers: [...databaseProviders],
exports: [...databaseProviders],
})
export class DatabaseModule {}
Events service
import { Inject, Injectable } from '#nestjs/common';
import { DataSource } from 'typeorm';
import { DatabaseModule } from './database.module';
import { Event } from './entities/event.entity';
import { EventInvite } from './entities/eventInvite.entity';
#Injectable()
export class EventsService {
constructor(#Inject("DATA_SOURCE") private readonly database: DataSource) { }
createEvent(userId: string, event: Event) {
this.database.manager.create(Event, event)
}
deleteEvent(eventId: string){
this.database.manager.delete(Event, { eventId })
}
}
Events Module
import { Module } from '#nestjs/common';
import { DatabaseModule } from './database.module';
import { EventsController } from './events.controller';
import { EventsService } from './events.service';
#Module({
imports: [DatabaseModule],
controllers: [EventsController],
providers: [EventsService],
exports: [EventsService]
})
export class EventsModule {}
App module
import { Module } from '#nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { EventsController } from './events/events.controller';
import { EventsService } from './events/events.service';
import { EventsModule } from './events/events.module';
import { DatabaseModule } from './events/database.module';
#Module({
imports: [],
controllers: [AppController, EventsController],
providers: [AppService, EventsService],
})
export class AppModule {}
If I import DatabaseModule inside of AppModule everything works. My question is, why is this required? My understanding thus far is that Nestjs builds a dependency tree, which in this case should look something like AppModule => EventService => DatabaseService. AppModule doesn't directly access DatabaseService, and therefore shouldn't need to import it directly, so why is Nestjs failing to resolve this dependency?
that module isn't global, thus its providers aren't globally available. As you're registering the service EventsService again in AppModule, you need to import the DatabaseModule
I believe this is what you're trying to do (which is pretty much what the docs shows):
#Module({
imports: [EventsModule],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
than you won't have 2 instances of EventsService and EventsController anymore, only the one registered in EventsModule module.
I'm working on migrating my Angular 10 application to SSR using Angular universal and I'm facing issues with ngx-translate while translating parameterised values.
I have a translation
Bei Kauf von {quantity}
where this quantity is an attribute from our component and we normally translate it like
{{ 'LABEL' | translate: {quantity: 1} }} and we will see Bei Kauf von 1
But with SSR it's not at all translating. I see Bei Kauf von {quantity} on the page. I checked many forums and I don't see potential solutions. Any help would be much appreciated.
Here is my translate-server.loader.ts
import { join } from 'path';
import { Observable } from 'rxjs';
import { TranslateLoader } from '#ngx-translate/core';
import {
makeStateKey,
StateKey,
TransferState
} from '#angular/platform-browser';
import * as fs from 'fs';
export class TranslateServerLoader implements TranslateLoader {
constructor(
private transferState: TransferState,
private prefix: string = 'i18n',
private suffix: string = '.json'
) {}
public getTranslation(lang: string): Observable<any> {
return new Observable((observer) => {
const assets_folder = join(
process.cwd(),
'dist',
'my-app',
'browser',
'assets',
this.prefix
);
const jsonData = JSON.parse(
fs.readFileSync(`${assets_folder}/${lang}${this.suffix}`, 'utf8')
);
// Here we save the translations in the transfer-state
const key: StateKey<number> = makeStateKey<number>(
`transfer-translate-${lang}`
);
this.transferState.set(key, jsonData);
observer.next(jsonData);
observer.complete();
});
}
}
export function translateServerLoaderFactory(transferState: TransferState) {
return new TranslateServerLoader(transferState);
}
translate-browser.loader.ts
import { Observable } from 'rxjs';
import { TranslateLoader } from '#ngx-translate/core';
import {
makeStateKey,
StateKey,
TransferState
} from '#angular/platform-browser';
import { TranslateHttpLoader } from '#ngx-translate/http-loader';
import { HttpClient } from '#angular/common/http';
export class TranslateBrowserLoader implements TranslateLoader {
constructor(private http: HttpClient, private transferState: TransferState) {}
public getTranslation(lang: string): Observable<any> {
const key: StateKey<number> = makeStateKey<number>(
`transfer-translate-${lang}`
);
const data = this.transferState.get(key, null);
// First we are looking for the translations in transfer-state,
// if none found, http load as fallback
if (data) {
return new Observable((observer) => {
observer.next(data);
observer.complete();
});
} else {
return new TranslateHttpLoader(this.http).getTranslation(lang);
}
}
}
export function translateBrowserLoaderFactory(
httpClient: HttpClient,
transferState: TransferState
) {
return new TranslateBrowserLoader(httpClient, transferState);
}
app.server.module.ts
import { NgModule } from '#angular/core';
import { ServerModule, ServerTransferStateModule } from '#angular/platform-server';
import { FlexLayoutServerModule } from '#angular/flex-layout/server';
import { TranslateModule, TranslateLoader } from '#ngx-translate/core';
import { translateServerLoaderFactory } from './shared/loaders/translate-server.loader';
import { TransferState } from '#angular/platform-browser';
import { AppModule } from './app.module';
import { AppComponent } from './app.component';
import { EnvironmentService } from '~shared/services/environment.service';
#NgModule({
imports: [
AppModule,
ServerModule,
ServerTransferStateModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: translateServerLoaderFactory,
deps: [TransferState]
}
}),
FlexLayoutServerModule,
],
providers: [EnvironmentService],
bootstrap: [AppComponent],
})
export class AppServerModule {}
app.module.ts
imports: [
....,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: translateBrowserLoaderFactory,
deps: [HttpClient, TransferState]
}
}),
]
Note: It's working fine for all others which don't have any parameterised values in translations
It should be double curly brackets
Bei Kauf von {{quantity}}
wanna notice 2 more things, idk if it changed in last versions of Angular, but don't forget to register TransferState in app.module providers
providers: [
TransferState,
...
]
and for translate-server.loader.ts if you have some type errors for node.js use
/// <reference types="node" /> // add this line
import {join} from 'path';
at the top.
If it doesn't help, try to add
"types" : ["node", "express"],
to your tsconfig.json inside "compilerOptions"
It took me some time))
I've followed the example from the docs on how to create a basic config service.
At the bottom of the tutorial it says you can opt to declare it globally:
"Instead of importing ConfigModule repeatingly in all your modules, you can also declare ConfigModule as a global module."
So following the documentation for global modules I have:
Imported Global from #nestjs/common into ConfigModule.
Added the #Global() decorator to ConfigModule.
Imported ConfigModule into AppModule.
Added ConfigModule to the imports array.
So what's next? I have tried to inject ConfigService into AppService however it doesn't resolve.
app.module.ts:
import { Module } from '#nestjs/common';
import { AppService } from './app.service';
import { AppController } from './app.controller';
import { ConfigModule } from '../config/config.module';
#Module({
imports: [
ConfigModule,
],
controllers: [
AppController,
],
providers: [
AppService,
],
})
export class AppModule {}
app.service.ts:
import { Injectable } from '#nestjs/common';
#Injectable()
export class AppService {
private readonly config: ConfigService;
constructor(config: ConfigService) {
this.config = config;
}
getHello(): string {
return config.get('DB_NAME');
}
}
config.module.ts:
import { Module, Global } from '#nestjs/common';
import { ConfigService } from './config.service';
#Global()
#Module({
providers: [
{
provide: ConfigService,
useValue: new ConfigService(`${process.env.NODE_ENV}.env`),
},
],
exports: [
ConfigService,
],
})
export class ConfigModule {}
config.service.ts:
import * as dotenv from 'dotenv';
import * as fs from 'fs';
export class ConfigService {
private readonly envConfig: { [key: string]: string };
constructor(filePath: string) {
this.envConfig = dotenv.parse(fs.readFileSync(filePath));
}
get(key: string): string {
return this.envConfig[key];
}
}
I expect to be able to inject ConfigService and access it from any module.
You're missing the this qualifier in your AppService:
getHello(): string {
return this.config.get('DB_NAME');
^^^^^
}
Also, the import is missing:
import { ConfigService } from './config/config.service';
I am writing some unit tests for my component and i am getting this cryptic error message. I found a similar question at Angular 2 unit testing - getting error Failed to load 'ng:///DynamicTestModule/module.ngfactory.js' but the answers did not help me solve my issue. I am angular 4.3.2
Here's the component i am writing the test for:
import {Component} from '#angular/core';
import {Router} from '#angular/router';
import {NotificationService} from '../common/notification/notification.service';
import {SessionService} from '../common/session/session.service';
import {Login} from './login.model';
#Component({
selector: 'cc-login-form',
templateUrl: './login.component.html',
styleUrls: ['./login.component.scss'],
})
export class LoginComponent {
model: Login = new Login('', '');
constructor(private sessionService: SessionService,
private router: Router,
private notificationService: NotificationService) {
}
onSubmit() {
this.sessionService
.login(this.model.email, this.model.password)
.subscribe(
sessionInfo => {
this.notificationService.showSuccess('notification.successfully.logged.in');
this.router.navigate([`/cc/list`]);
},
error => this.notificationService.showError('notification.invalid.login')
);
}
}
And here is the test file:
import {async, ComponentFixture, TestBed} from '#angular/core/testing';
import {FormsModule} from '#angular/forms';
import {Router} from '#angular/router';
import {TranslateModule, TranslateService} from '#ngx-translate/core';
import {NotificationService} from '../common/notification/notification.service';
import {NotificationServiceStub} from '../common/notification/tests/NotificationServiceStub';
import {SessionService} from '../common/session/session.service';
import {SessionServiceStub} from '../common/session/tests/SessionServiceStub';
import {RouterStub} from '../common/tests/RouterStub';
import {TranslateServiceStub} from '../common/translate/tests/TranslateServiceStub';
import {LoginComponent} from './login.component';
describe('LoginComponent', () => {
let component: LoginComponent;
let fixture: ComponentFixture<LoginComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
FormsModule,
TranslateModule
],
declarations: [LoginComponent],
providers: [
{provide: SessionService, useClass: SessionServiceStub},
{provide: Router, useClass: RouterStub},
{provide: NotificationService, useClass: NotificationServiceStub},
{provide: TranslateService, useClass: TranslateServiceStub},
]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(LoginComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
});
When running the test i get the following on chrome console:
zone.js:2642 XMLHttpRequest cannot load ng:///DynamicTestModule/LoginComponent.ngfactory.js. Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, https.
(anonymous) # zone.js:2642
zone.js:195 Uncaught DOMException: Failed to execute 'send' on 'XMLHttpRequest': Failed to load 'ng:///DynamicTestModule/LoginComponent.ngfactory.js'.
at http://localhost:9876/_karma_webpack_/webpack:/Users/pedrompg/Documents/quandoo/fe/chains-center/~/zone.js/dist/zone.js:2642:1
at XMLHttpRequest.proto.(anonymous function) [as send] (
Any can help me with that?
EDIT - 1
Here's the services/stubs implementation
SessionServiceStub
export class SessionServiceStub implements ISessionService {
login(login: string, password: string): Observable<any> {
return Observable.of({merchantId: 123});
}
logout(): Observable<any> {
throw new Error('Method not implemented.');
}
validateSessionToken(): Observable<any> {
throw new Error('Method not implemented.');
}
}
SessionService
#Injectable()
export class SessionService implements ISessionService {
constructor(private http: CcHttpClient, private router: Router, private localSessionService: LocalSessionService) {
}
login(login: string, password: string): Observable<any> {
return this.http.post(`api/sessions`, {login: login, password: password}).map((res: Object) => {
this.localSessionService.createSession(res);
return res;
});
}
}
RouterStub
export class RouterStub {
navigate(commands: any[], extras?: NavigationExtras): Promise<boolean> {
return Promise.resolve(true);
};
}
TranslationServiceStub
export class TranslateServiceStub {
instant(key: string | Array<string>, interpolateParams?: Object): string | any {
return 'translation';
};
}
NotificationServiceStub
export class NotificationServiceStub implements INotificationService {
showToast(type, text, title, defaultTitle): Promise<Toast> {
return Promise.resolve(null);
}
showSuccess(msg, title?): Promise<Toast> {
return Promise.resolve(null);
}
showError(msg, title?): Promise<Toast> {
return Promise.resolve(null);
}
}
EDIT 2
Changing my TestBed config to the following removed the error but brought a new one:
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
FormsModule,
HttpClientModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: HttpTranslateLoaderFactory,
deps: [HttpClient]
}
})
],
declarations: [LoginComponent],
providers: [
{provide: SessionService, useClass: SessionServiceStub},
{provide: Router, useClass: RouterStub},
{provide: NotificationService, useClass: NotificationServiceStub},
]
})
.compileComponents();
}));
Now the error message is
TypeError: Cannot read property 'assertPresent' of undefined
at resetFakeAsyncZone home/pedrompg/Documents/quandoo/fe/chains-center/~/#angular/core/#angular/core/testing.es5.js:304:1)
at Object.<anonymous> home/pedrompg/Documents/quandoo/fe/chains-center/~/#angular/core/#angular/core/testing.es5.js:1001:1)
at ZoneQueueRunner.webpackJsonp.../../../../zone.js/dist/jasmine-patch.js.jasmine.QueueRunner.ZoneQueueRunner.execute home/pedrompg/Documents/quandoo/fe/chains-center/~/zone.js/dist/jasmine-patch.js:132:1)
Which happens at this function:
function resetFakeAsyncZone() {
_fakeAsyncTestZoneSpec = null;
ProxyZoneSpec.assertPresent().resetDelegate(); //ProxyZoneSpec is undefined here for whatever reason
}
This is a problem with the Angular Cli version 1.2.2 or newer. Run your test with --sourcemaps=false and you will get the right error messages.
In Angular 4-5
ng test --sourcemaps=false
or
ng test -sm=false
In Angular 6+
ng test --source-map=false
See details here: https://github.com/angular/angular-cli/issues/7296
I just ran into this error and the problem was my mocks.
In the component.ngOnInit i used this.route.paramMap.subscribe(...)
where route is an ActivatedRoute instance
In my test i provided a mock service like this :
providers: [
{ provide: ActivatedRoute, useValue: { snapshot: { params: { id: 1 } } } }
]
And in fact i missed to mock the paramMap method
Then i fix it adding a paramMap properties like this
providers: [
{ provide: ActivatedRoute, useValue: { snapshot: { params: { id: 1 } }, paramMap: Observable.of({get: () => 1}) } }
]
Then i don't have anymore this stupid error.
So for you, i expect the class SessionServiceStub to be incomplete or erroneous. Does it get a login method that return an Observable ?
If it's not the problem you can check the NotificationServiceStub
You should use a debugger (with Webstorm it's easy to debug step-by-step) to help you.
I encountered the same issue using angular-cli 6, so to get the right error message one should use the following:
ng test --source-map=false
Maybe it will help someone :) .
Been chasing this for hours. Finally discovered that the problem was simply that I had imported HttpClientModule, but not HttpClient:
import { HttpClient, HttpClientModule } from '#angular/common/http';
All those CORS errors, and '[Script Loader]', DOMException{stack: 'Error: Failed to execute 'send' on 'XMLHttpRequest' stuff, and it came down to just not having HttpClient!