I have some service in which I inject the CACHE_MANAGER in the constructor
import { CACHE_MANAGER, Inject, Injectable } from '#nestjs/common';
import { Cache } from 'cache-manager';
...
export class ManagerService {
constructor(#Inject(CACHE_MANAGER) private cacheManager: Cache) {}
...
}
That gives me an error when I test the modules the import those service
Nest can't resolve dependencies of the ManagerService (?). Please make sure that the argument CACHE_MANAGER at index [0] is available in the Web3ManagerService context.
I'm relatively new to NestJs so I really cannot figure out how to solve it
To inject the cache manager provider under the CACHE_MANAGER, you need to import the nestjs module that creates this provider into the module that has the ManagerService
#Module({
imports: [CacheModule.register()], // <<<<
providers: [ManagerService],
})
export class AppModule {}
like the docs shows https://docs.nestjs.com/techniques/caching
I just had the same problem and resolved it this way:
manager.service.spec.ts
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [ManagerService, { provide: CACHE_MANAGER, useValue: {} }],
}).compile();
service = module.get<ManagerService>(ManagerService);
});
Set isGlobal: true:
CacheModule.register({
url: process.env.REDIS_URL,
db: 0,
port: parseInt(process.env.REDIS_PORT),
password: process.env.REDIS_PASSWORD,
isGlobal: true, //<<<<<<<
})
I've created a library with the new CLI 6.x. I've also created an Angular service inside it and I would like to use different URLs for my dev and prod environment. So I created an environment.ts and an environment.prod.ts file in my library folder.
//dev
export const environment = {
production: false,
url: 'dev-url'
};
//prod
export const environment = {
production: true,
url: 'prod-url'
};
I also added the 'fileReplacements' property to the angular.json file:
"configurations": {
"production": {
"fileReplacements": [{
"replace": "projects/tk-shared/src/environments/environment.ts",
"with": "projects/tk-shared/src/environments/environment.prod.ts"
}],
"project": "projects/tk-shared/ng-package.prod.json"
}
}
Compiling the library with the ng build tk-shared works and uses the dev settings, however when compiling with ng build --prod tk-shared I get the following error:
Schema validation failed with the following errors: Data path "" should NOT have additional properties(fileReplacements).
My tip is that the reason is that tk-shared has the projectType: "library" property in angular.json.
Anyway, is it possible to use environment files in a library?
Thanks, #R. Richards for pointing me to the right solution!
These two sources helped me figure out how to do this injection correctly: LINK1 and LINK2.
So what I did ...
Created an InjectionToken and modified my TkSharedModule:
export const BASE_URL = new InjectionToken<string>('BASE_URL');
//...
export class TkSharedModule {
static forRoot(host: string) {
return {
ngModule: TkSharedModule,
providers: [{
provide: BASE_URL,
useValue: host
}]
}
}
}
Provided a value for this token from the environment files in my AppModule:
import {environment} from '../environments/environment';
//...
#NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
TkSharedModule.forRoot(environment.url)
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
And finally injected this value in my service:
//MyServiceService
import { BASE_URL } from '../tk-shared.module';
constructor(#Inject(BASE_URL) BASE_URL: string) {
console.log('base url', BASE_URL);
}
I have a Angular service that looks something like this:
import {environment} from '../environment'
....
public something() {
if(environment.production)
{
// do stuf
} else {
// do something else
}
}
Now i want to test both cases (dev and prod environemnt). How do I "mock" when the environment is imported ?
So i came up with a solution where I didn't have to check for the environment in my service:
I used the useFactory:
NgModule({
declarations: [],
imports: [],
providers: [
{
provide: Service,
useFactory: (httpClient: HttpClient) => {
if (environment.production) {
return new MyCustomService(httpClient);
} else {
return new Service();
}
},
deps: [HttpClient],
}
],
bootstrap: [AppComponent]
})
This way i manage which service is provided when the application is started up
I am trying to lazy load a feature module from another server.I have managed to export the chunk of lazyloaded module from the node server.By referring to Solution: load independently compiled Webpack 2 bundles dynamically. On running it, browser successfully loads the script from the server, But fails to navigate to the new location and getting an error
ERROR TypeError: Cannot read property 'LazyModule' of undefined
at eval (app.module.ts:14)
at HTMLScriptElement.script.onload [as __zone_symbol__ON_PROPERTYload
(app.module.ts:58)
app.module
const appRoutes: Routes = [
{
path: 'Module1', loadChildren: () => new Promise((resolve, reject) => {
loadPlugin('http://localhost:4000/chunk.js', (exports) => {
console.log(exports);
resolve(exports.LazyModule);
});
})
},
{ path: '*', component: AppComponent }
];
#NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
RouterModule.forRoot(appRoutes),
HttpClientModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
function loadPlugin(pluginUri, mainCallback) {
installMainCallback(pluginUri, mainCallback);
loadPluginChunk(pluginUri, mainCallback);
}
function installMainCallback(pluginUri, mainCallback) {
var _pluginIdent = pluginIdent(pluginUri)
window[_pluginIdent] = function (exports) {
delete window[_pluginIdent]
mainCallback(exports)
}
}
function loadPluginChunk(pluginUri, callback) {
return loadScript(pluginUri, callback)
}
function loadScript(url, callback) {
var script = document.createElement('script')
script.src = url
script.onload = function () {
document.head.removeChild(script)
callback && callback()
}
document.head.appendChild(script)
}
function pluginIdent(pluginUri) {
return '_' + pluginUri.replace(/\./g, '_')
}
By referring to the error log, I guess that the error is due to lazyloaded module is invoked nowhere. What should I return to LoadChildren to invoke it?
The problem is that in routing i have to click twice to trigger ngOnInit code.
The weird thing is, if I have two routes: A and B, and I clicked on A first, it will trigger the constructor only, and if I clicked on B after it, it will trigger A's onInit before calling B's constructor.
using angular 2.0.0-rc.4 and routes 3.0.0-beta.2
error displayed on page load:
vendors.js:2291 Unhandled promise rejection Error: Cannot match any routes: ''
at Observable._subscribe (http://localhost:54037/js/app.js:19280:28)
at Observable.subscribe (http://localhost:54037/js/app.js:56291:60)
at Observable._subscribe (http://localhost:54037/js/app.js:56328:26)
at MergeMapOperator.call (http://localhost:54037/js/app.js:26178:21)
at Observable.subscribe (http://localhost:54037/js/app.js:56291:36)
at Observable._subscribe (http://localhost:54037/js/app.js:56328:26)
at MergeMapOperator.call (http://localhost:54037/js/app.js:26178:21)
at Observable.subscribe (http://localhost:54037/js/app.js:56291:36)
at Observable._subscribe (http://localhost:54037/js/app.js:56328:26)
at MapOperator.call (http://localhost:54037/js/app.js:56831:21)
gulp file
/// <binding Clean='default, clean, resources' />
/*
This file in the main entry point for defining Gulp tasks and using Gulp plugins.
Click here to learn more. http://go.microsoft.com/fwlink/?LinkId=518007
*/
var gulp = require('gulp');
var sourcemaps = require('gulp-sourcemaps');
var concat = require('gulp-concat');
var uglify = require('gulp-uglify');
var typescript = require('gulp-typescript');
var systemjsBuilder = require('systemjs-builder');
const del = require("del");
// Compile TypeScript app to JS
gulp.task('compile:ts', function () {
return gulp
.src([
"appTS/**/*.ts",
"typings/*.d.ts"
])
.pipe(sourcemaps.init())
.pipe(typescript({
"module": "system",
"moduleResolution": "node",
"outDir": "app",
"target": "ES5"
}))
.pipe(sourcemaps.write('.'))
.pipe(gulp.dest('app'));
});
// Generate systemjs-based bundle (app/app.js)
gulp.task('bundle:app', function () {
var builder = new systemjsBuilder('./', './system.config.js');
return builder.buildStatic('app', 'wwwroot/js/app.js');
});
// Copy and bundle dependencies into one file (vendor/vendors.js)
// system.config.js can also bundled for convenience
gulp.task('bundle:vendor', function () {
return gulp.src([
'node_modules/core-js/client/shim.min.js',
'node_modules/systemjs/dist/system-polyfills.js',
'node_modules/reflect-metadata/Reflect.js',
'node_modules/zone.js/dist/zone.js',
'node_modules/systemjs/dist/system.js',
'system.config.js'
])
.pipe(concat('vendors.js'))
.pipe(gulp.dest('build'));
});
// Copy dependencies loaded through SystemJS into dir from node_modules
gulp.task('copy:vendor', function () {
return gulp.src([
'node_modules/rxjs/bundles/Rx.js',
'node_modules/#angular/**/*'
])
.pipe(gulp.dest('build'));
});
gulp.task('vendor', ['bundle:vendor', 'copy:vendor']);
gulp.task('app', ['compile:ts', 'bundle:app']);
// Bundle dependencies and app into one file (app.bundle.js)
gulp.task('bundle', ['vendor', 'app'], function () {
return gulp.src([
'build/app.js',
'build/vendors.js'
])
.pipe(concat('app.bundle.js'))
.pipe(gulp.dest('wwwroot/js/app'));
});
/**
* Copy all resources that are not TypeScript files into build directory.
*/
gulp.task("resources", () => {
return gulp.src(["Scripts/app/**/*", "!**/*.ts"])
.pipe(gulp.dest("wwwroot/app"));
});
/**
* Remove build directory.
*/
gulp.task('clean', (cb) => {
return del(["build"], cb);
});
gulp.task('default', ['bundle']);
app.routes
import { provideRouter, RouterConfig } from '#angular/router';
import { MediaItemFormComponent } from './media-item-form.component';
import { MediaItemListComponent } from './media-item-list.component';
export const routes: RouterConfig = [
{ path: 'list', component: MediaItemListComponent },
{ path: 'add', component: MediaItemFormComponent }
];
export const APP_ROUTER_PROVIDERS = [
provideRouter(routes)
];
list component
import {Component, Inject, OnInit } from '#angular/core';
import 'rxjs/Rx';
import {MediaItemComponent} from './media-item.component';
import {CategoryListPipe} from './category-list.pipe';
import {MediaItemService} from './media-item.service';
#Component({
selector: 'media-item-list',
directives: [MediaItemComponent],
pipes: [CategoryListPipe],
providers: [MediaItemService],
templateUrl: 'app/media-item-list.component.html',
styleUrls: ['app/media-item-list.component.css']
})
export class MediaItemListComponent implements OnInit {
mediaItems;
constructor(private mediaItemService: MediaItemService) {
console.log("constructor MediaItemList");
}
ngOnInit() {
console.log("ngOnInit MediaItemList");
this.getMediaItem();
}
onMediaItemDeleted(mediaItem) {
this.mediaItemService.delete(mediaItem)
.subscribe(() => {
this.getMediaItem();
});
}
getMediaItem() {
this.mediaItemService.get().subscribe(mediaitems => {
this.mediaItems = mediaitems;
},
function (error) { console.log("Error happened" + error) },
function () {
}
);
}
}
system.js
// map tells the System loader where to look for things
var map = {
'app': 'Scripts/app',
'rxjs': 'node_modules/rxjs',
'#angular': 'node_modules/#angular'
};
// packages tells the System loader how to load when no filename and/or no extension
var packages = {
'app': { main: 'main', defaultExtension: 'js' },
'rxjs': { defaultExtension: 'js' },
};
var packageNames = [
'#angular/common',
'#angular/compiler',
'#angular/core',
'#angular/forms',
'#angular/http',
'#angular/platform-browser',
'#angular/platform-browser-dynamic',
'#angular/router',
'#angular/testing',
'#angular/upgrade',
];
// add package entries for angular packages in the form '#angular/common': { main: 'index.js', defaultExtension: 'js' }
packageNames.forEach(function (pkgName) {
packages[pkgName] = { main: 'index.js', defaultExtension: 'js' };
});
System.config({
map: map,
packages: packages
});
index.html
<html>
<head>
<title>MeWL</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<base href="/" />
<link href="resets.css" rel="stylesheet">
<script src="js/vendors.js"></script>
<script src="js/app.js"></script>
<style>
body {
margin: 0px;
padding: 0px;
background-color: #32435b;
}
</style>
</head>
<body>
<media-tracker-app>Loading...</media-tracker-app>
</body>
</html>
Update:
I'll include html of list and the component nested inside if it helps
<media-item
*ngFor="let mediaItem of mediaItems"
[mediaItemToWatch] ="mediaItem"
(deleted)="onMediaItemDeleted($event)"
[ngClass]="{'medium-movies': mediaItem.medium === 'Movies', 'medium- series' : mediaItem.medium === 'Series'}" ></media-item>
MediaItem html:
<h2>{{mediaItem.name }}</h2>
<div>{{mediaItem.category}}</div>
<div>{{mediaItem.year}}</div>
<div class="tools">
<a class="delete" (click)="onDelete()">
remove
</a>
<a class="details">
watch
</a>
</div>
Media Item ts:
import {Component, Input, Output, EventEmitter} from '#angular/core';
import {FavoriteDirective} from './favorite.directive';
#Component({
selector: 'media-item',
directives: [FavoriteDirective],
templateUrl: 'app/media-item.component.html',
styleUrls: ['app/media-item.component.css']
})
export class MediaItemComponent {
#Input('mediaItemToWatch') mediaItem;
#Output('deleted') delete = new EventEmitter();
onDelete() {
this.delete.emit(this.mediaItem);
}
}
It seems
vendors.js:2291 Unhandled promise rejection Error: Cannot match any routes: ''
causes change detection to not run
To avoid this error add a route for the '' path like
{ path: '', redirectTo: '/list', pathMatch: 'full' }
or
{ path: '', component: DummyComponent, pathMatch: 'full' }
I think better answer is to add "onSameUrlNavigation" option on
RouterModule.forRoot(
appRoutes,
{
useHash: false,
anchorScrolling: "enabled",
onSameUrlNavigation: "reload",
enableTracing: true,
scrollPositionRestoration: "enabled"
})