Angular - Apollo: Client has not been defined yet - javascript

I'm using apollo client for graphql. I set up the client in AppApolloModule that I'm importing in AppModule. I'm making a query in a service which is also imported right in the AppModule. Although the service runs before the AppApolloModule runs and hence apollo is not initialized when the query is made and I get this error
Error: Client has not been defined yet
AppApolloModule
imports ....
export class AppApolloModule {
constructor(
apollo: Apollo,
httpLink: HttpLink,
private userService: UserService
) {
console.log("apollo module")
apollo.create({
link: httpLink.create({ uri: `${environment.apiBase}/graphql?${this.myService.token}`}),
cache: new InMemoryCache()
})
}
}
App Module
import { AppApolloModule } from './app.apollo.module';
import { MyService } from './services/my.service';
export class AppModule {
constructor() {
console.log("app module")
}
}
I don't get the two consoles app module and apollo module, since the service runs first, it doesn't find any initialized apollo app and thus breaks the code.
How can I make apollo run before the service or any services for that matter in an efficient and standard way?

This will solve the issue nicely:
import {NgModule} from '#angular/core';
import {HttpClientModule} from '#angular/common/http';
import {ApolloModule, APOLLO_OPTIONS} from 'apollo-angular';
import {HttpLink, HttpLinkModule} from 'apollo-angular-link-http';
import {InMemoryCache} from 'apollo-cache-inmemory';
export function createApollo(httpLink: HttpLink) {
return {
link: httpLink.create({uri: 'https://api.example.com/graphql'}),
cache: new InMemoryCache(),
};
}
#NgModule({
imports: [HttpClientModule, ApolloModule, HttpLinkModule],
providers: [
{
provide: APOLLO_OPTIONS,
useFactory: createApollo,
deps: [HttpLink],
},
],
})
class AppModule {}

The answer by #wendellmva didn't work for me. What did work was the solution suggested in this repo:
https://github.com/patricknazar/angular-lazy-loading-apollo-client
which is basically to put Apollo initialization in a separate, shared module, and include it in your main app module with forRoot().

I have the same issue an the docs from Apollo helped me. Go to 'https://www.apollographql.com/docs/angular/basics/setup/' or copy this:
import { HttpClientModule } from "#angular/common/http";
import { ApolloModule, APOLLO_OPTIONS } from "apollo-angular";
import { HttpLinkModule, HttpLink } from "apollo-angular-link-http";
import { InMemoryCache } from "apollo-cache-inmemory";
#NgModule({
imports: [
BrowserModule,
HttpClientModule,
ApolloModule,
HttpLinkModule
],
providers: [{
provide: APOLLO_OPTIONS,
useFactory: (httpLink: HttpLink) => {
return {
cache: new InMemoryCache(),
link: httpLink.create({
uri: "https://o5x5jzoo7z.sse.codesandbox.io/graphql"
})
}
},
deps: [HttpLink]
}],
})
export class AppModule {}

What worked for me was deleting the .angular folder and serving the application again.

Related

Clean solution to ensure a singleton service is created on app initialization

I want to ensure a singleton service is created on application boot. I could add it as injection parameter to my AppComponent and not use it at all, but that looks a bit dirty. Right now I'm going with this solution:
import { APP_INITIALIZER, ModuleWithProviders, NgModule } from '#angular/core';
import { NavigationService } from './navigation.service';
#NgModule()
export class NavigationServiceModule {
public static forRoot(): ModuleWithProviders<NavigationServiceModule> {
return {
ngModule: NavigationServiceModule,
providers: [
{
provide: APP_INITIALIZER,
deps: [NavigationService],
multi: true,
useFactory: () => () => { }
}
]
}
}
}
But don't really love it, too. Any ideas how this could be achieved best?
Just use the default service setup and don't add it to the providers array - only one instance will be created (unless you explicitly provide it outside of constructors)
#Injectable({
providedIn: 'root'
})
export class NavigationService

NestJS Dependency Injection Error: provider/module is not part of currently processed module

I've just started with NestJS and I'm wondering what the best way to solve this error is
Nest cannot export a provider/module that is not a part of the
currently processed module (UsersModule). Please verify whether the
exported UsersService is available in this particular context.
I am following along with the documentation here and I seem to have hit a brick wall. My app won't compile so I can't test any further. I'll add some of the files as examples.
auth.module.ts
import { Module } from '#nestjs/common';
import { UsersModule } from 'src/users/users.module';
import { AuthController } from './controllers/auth/auth.controller';
import { AuthService } from './services/auth/auth.service';
#Module({
imports: [UsersModule],
controllers: [AuthController],
providers: [AuthService],
exports: [AuthService],
})
export class AuthModule {}
users.module.ts
import { Module } from '#nestjs/common';
import { MongooseModule } from '#nestjs/mongoose';
import { UsersController } from './controllers/users/users.controller';
import { User, UserSchema } from './schemas/user.schema';
import { UsersService } from './services/users/users.service';
#Module({
imports: [
MongooseModule.forFeature([{ name: User.name, schema: UserSchema }]),
],
controllers: [UsersController],
providers: [
{
provide: 'USERS_SERVICE',
useClass: UsersService,
},
],
exports: [UsersService],
})
export class UsersModule {}
users.service.ts
import { Injectable } from '#nestjs/common';
import { InjectModel } from '#nestjs/mongoose';
import { Model } from 'mongoose';
import { CreateUserDto } from 'src/users/dto/user.dto';
import { User, UserDocument } from 'src/users/schemas/user.schema';
import { IUserService } from './user';
#Injectable()
export class UsersService implements IUserService {
constructor(#InjectModel(User.name) private userModel: Model<UserDocument>) {}
// async code for database modification
}
}
Any help would be greatly appreciated! Many thanks!
The token of UsersService provider is the string 'UsersService' (or UsersService.name) not 'USERS_SERVICE'
So just use the short-hand syntax providers: [UsersService] or the right token in provide:.

Why Angular always redirecting to main page?

Always I trying to GET '/' it shows static-root-component (component of my main page),
but when it is '/welcome' page immediately redirecting to '/' and also loading static-root-component instead of welcome-component
Initially I wanted to redirect users to welcome page if they aren't authorized, but login status only can be checked within JavaScript. After JS got info about login status it decides to redirect using location.replace("/welcome"), but... Angular again goes to '/'
"Funny" fact: there isn't any routing problems during debug with ng serve but it always happens with ng build
I don't know what's gone wrong and there is app.module.ts:
import { NgModule } from '#angular/core';
import { RouterModule, Routes } from '#angular/router';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { StaticRootComponent } from './static-root/static-root.component';
import { WelcomeComponent } from './welcome/welcome.component';
import { HttpClientModule } from '#angular/common/http';
import { HttpService } from './http.service';
import { BrowserAnimationsModule } from '#angular/platform-browser/animations';
const appRoute: Routes = [
{ path: '', component: StaticRootComponent, pathMatch: 'full' },
{ path: 'welcome', component: WelcomeComponent }
];
#NgModule({
declarations: [
AppComponent,
StaticRootComponent,
WelcomeComponent
],
imports: [
BrowserModule,
AppRoutingModule,
BrowserAnimationsModule,
RouterModule.forRoot(appRoute),
HttpClientModule
],
providers: [HttpService],
bootstrap: [AppComponent]
})
export class AppModule { }
I can drop any other Angular file if needed
In your code, change like below
const appRoute: Routes = [
{ path: '', component: StaticRootComponent },
{ path: 'welcome', component: WelcomeComponent },
{ path: '**', redirectTo: '' }
];
In the component file, inject this like below
import { Router } from '#angular/router';
constructor(
private router: Router
) {}
When you want do navigation use the below code instead of location.replace("/welcome")
this.router.navigate(['/welcome']);
Check the Module you trying to instantiate in the constructor of the Component linked to the Routing Path you are trying to access
In this case:
Our Component: example.component.ts
Our Module: HttpClientModule that contains HttpClient
Our Routing Path: "/example"
and make sure that Module is already existing in the app.module.ts , here is an example:
example.component.ts
import {HttpClient} from '#angular/common/http'; //child of HttpClientModule
#Component({selector: 'app-example',templateUrl: './example.component.html', styleUrls: ['./example.component.css']})
export class ExampleComponent{
constructor(private httpClient: HttpClient) { }
}
now let's see both examples of app module with and without the Module import and see the difference
app.module.ts
Without including HttpClientModule in imports array
import { AppComponent } from './app.component';
#NgModule({
declarations: [...],
imports: [BrowserModule,...],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
In this case loading the "/example" path will redirect you to the main page path which is usually "/" and that's because example.component.ts is using HttpClient (child of HttpClientModule) but not finding it in app.module.ts .
app.module.ts
Including HttpClientModule in imports array
import { AppComponent } from './app.component';
import { HttpClientModule} from '#angular/common/http'; //Import that module you willing to use
#NgModule({
declarations: [...],
imports: [BrowserModule,HttpClientModule,...], //add the module we currently using
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
In this case loading the "/example" path will work properly since we added the required module in app.module.ts.
If that's your case that would definitely fix your problem unless you have something else forcing the redirection to home page "index.html" or any other path, else if that didn't fix your problem read the following notes:
Make sure to check there are no redirections in the app-routing.module.ts routes array like this
const routes: Routes = [
{path: 'example', component: ExampleComponent},
{ path: '/example', redirectTo: '' }
];
it should only be like so
const routes: Routes = [
{path: 'example', component: ExampleComponent}
];
Also make sure there is no routing behaviour causing the redirection like #angular/router through something like this.router.navigate(['/'])
PS: SAME ISSUE COULD IMPLY IF YOU USING A SERVICE THAT'S USING A MODULE WHICH IS NOT ADDED TO MODULE IMPORTS IN app.module.ts
My project based on MEAN (Mongo, Express, Angular and NODEJS)... The last was a source of problem
#Shakthifuture, you said you want to see full code and I started answering:
"What you wanna to see else? My data and server files doesn't affec..."
and I've starting think "what if affect?": routing in whole of project works by Angular, but all new connection to the site pass NodeJS and Express, so I forgot about 404 case...
THE PROBLEM:
In server script file index.js of project's root folder a long time ago I've added code about what to do if entered path not found:
app.use(function (req, res, next) {
res.redirect('/');
// IF 404 NOT FOUND
});
and above of it something like:
app.get('/', function (req, res) {
res.sendFile(`${__dirname}/angular/index.html`)
});
// send index if path is '/'
but nothing for '/welcome', that's why redirecting happens
THE SOLUTION:
let's add the '/welcome' handler:
app.get('/welcome', function (req, res) {
res.sendFile(`${__dirname}/angular/index.html`)
});
(again index.html due to SPA)

How to resolve ERROR NetworkError at XMLHttpRequest.send (...dist\fxcore\server\main.js:200768:19)

I am new to Angular. I just finished developing my angular web application. When I use ng serve to serve my application during production, everything works fine. I added angular universal. Now when I run any of npm run dev:ssr or npm run build:ssr && npm run serve:ssr, my application will refuse to open, throwing NetworkError response in the console. I noticed this error occurs for the number of times http requests where sent via class 'constructors(){..}'. I have browsed through several solution but couldn't get a clue of what I'm not doing right. My backend is developed with nodejs and express. I'll appreciate any help I can get.
Here is a full example of the error response I always get in the console.
ERROR NetworkError
at XMLHttpRequest.send (C:\Users\MRBEN\Desktop\Angular\fxcore\dist\fxcore\server\main.js:200768:19)
at Observable._subscribe (C:\Users\MRBEN\Desktop\Angular\fxcore\dist\fxcore\server\main.js:19025:17)
at Observable._trySubscribe (C:\Users\MRBEN\Desktop\Angular\fxcore\dist\fxcore\server\main.js:186304:25)
at Observable.subscribe (C:\Users\MRBEN\Desktop\Angular\fxcore\dist\fxcore\server\main.js:186290:22)
at scheduleTask (C:\Users\MRBEN\Desktop\Angular\fxcore\dist\fxcore\server\main.js:105897:32)
at Observable._subscribe (C:\Users\MRBEN\Desktop\Angular\fxcore\dist\fxcore\server\main.js:105959:13)
at Observable._trySubscribe (C:\Users\MRBEN\Desktop\Angular\fxcore\dist\fxcore\server\main.js:186304:25)
at Observable.subscribe (C:\Users\MRBEN\Desktop\Angular\fxcore\dist\fxcore\server\main.js:186290:22)
at subscribeToResult (C:\Users\MRBEN\Desktop\Angular\fxcore\dist\fxcore\server\main.js:196385:23)
at MergeMapSubscriber._innerSub (C:\Users\MRBEN\Desktop\Angular\fxcore\dist\fxcore\server\main.js:191575:116)```
I was still getting this ERROR NetworkError but I found another way to make this error go away. I think this answer is relevant since I was getting the same error posted above. If this can help anyone with that same server error then that's great.
If the api request is made to the server OnInit when reloading check isPlatformBrowser first when using ng-universal example.
import { Component, OnInit, PLATFORM_ID, Inject } from '#angular/core';
import { isPlatformBrowser } from '#angular/common';
import { HttpClient, HttpHeaders } from '#angular/common/http';
export class HomeComponent implements OnInit {
public testBrowser : boolean;
public data : any;
constructor(private http: HttpClient, #Inject(PLATFORM_ID) platformId: string) {
this.testBrowser = isPlatformBrowser(platformId);
}
ngOnInit() {
if (this.testBrowser) {
//avoid server NETWORK error
this.data = this.http.get('/api');
}
}
}
I was getting this same error trying to make server calls from the client before checking isPlatformBrowser === true first OnInit and this solved my problem. Hopefully this can help this bug.
For reference this answer helped me squash this long standing bug. https://stackoverflow.com/a/46893433/4684183
I am getting the same error. Try to remove TransferHttpCacheModule from your app.module and create your own custom http transfer interceptor file.
I made a file called transfer-state.interceptor.ts and then added it to app.module providers:[] to handle this. The examples below will show how I hooked it up. I am not sure if this will definitely work for you but it did make that error go away for me.
//app.module.ts
import { BrowserModule, BrowserTransferStateModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { HttpClientModule, HTTP_INTERCEPTORS } from "#angular/common/http";
//import {TransferHttpCacheModule } from '#nguniversal/common';
import { AppRoutingModule } from './app-routing/app-routing.module';
import { AppComponent } from './app.component';
import { HomeComponent } from './modules/home/home.component';
import { SliderComponent } from './components/slider/slider.component';
import { WindowRefService } from './services/window-ref.service';
//import { TransferHttpInterceptorService } from './services/transfer-http-interceptor.service';
import { TransferStateInterceptor } from './interceptors/transfer-state.interceptor';
import { ServiceWorkerModule } from '#angular/service-worker';
import { environment } from '../environments/environment';
#NgModule({
declarations: [
AppComponent,
HomeComponent,
SliderComponent
],
imports: [
BrowserModule.withServerTransition({ appId: 'serverApp' }),
BrowserTransferStateModule,
AppRoutingModule,
HttpClientModule,
ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production })
],
providers: [
WindowRefService,
{
provide: HTTP_INTERCEPTORS,
useClass: TransferStateInterceptor,
multi: true
}
],
bootstrap: [AppComponent]
})
export class AppModule { }
This is one version of a custom transfer state file but there are a few ways to do this if this one doesn't work.
//transfer-state.interceptor.ts
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse } from '#angular/common/http';
import { Inject, Injectable, PLATFORM_ID } from '#angular/core';
import { Observable, of } from 'rxjs';
import { StateKey, TransferState, makeStateKey } from '#angular/platform-browser';
import { isPlatformBrowser, isPlatformServer } from '#angular/common';
import { tap } from 'rxjs/operators';
#Injectable()
export class TransferStateInterceptor implements HttpInterceptor {
constructor(
private transferState: TransferState,
#Inject(PLATFORM_ID) private platformId: any,
) {}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// For this demo application, we will only worry about State Transfer for get requests.
if (request.method !== 'GET') {
return next.handle(request);
}
// Use the request url as the key.
const stateKey: StateKey<string> = makeStateKey<string>(request.url);
// For any http requests made on the server, store the response in State Transfer.
if (isPlatformServer(this.platformId)) {
return next.handle(request).pipe(
tap((event: HttpResponse<any>) => {
this.transferState.set(stateKey, event.body);
})
);
}
// For any http requests made in the browser, first check State Transfer for a
// response corresponding to the request url.
if (isPlatformBrowser(this.platformId)) {
const transferStateResponse = this.transferState.get<any>(stateKey, null);
if (transferStateResponse) {
const response = new HttpResponse({ body: transferStateResponse, status: 200 });
// Remove the response from state transfer, so any future requests to
// the same url go to the network (this avoids us creating an
// implicit/unintentional caching mechanism).
this.transferState.remove(stateKey);
return of(response);
} else {
return next.handle(request);
}
}
}
}
If you want to add custom cache to this you can by installing memory-cache but I haven't tried that out yet. For more references these articles helped me out a lot and maybe they can help you too.
https://itnext.io/angular-universal-caching-transferstate-96eaaa386198
https://willtaylor.blog/angular-universal-for-angular-developers/
https://bcodes.io/blog/post/angular-universal-relative-to-absolute-http-interceptor
If you haven't you may need to add ServerTransferStateModule to your app.server.module file.
//app.server.module
import { NgModule } from '#angular/core';
import {
ServerModule,
ServerTransferStateModule
} from "#angular/platform-server";
import { AppModule } from './app.module';
import { AppComponent } from './app.component';
#NgModule({
imports: [
AppModule,
ServerModule,
ServerTransferStateModule
],
bootstrap: [AppComponent],
})
export class AppServerModule {}
good luck!
I was struggling with this error for days until I found this article About how to create a relative to absolute interceptor
here's the link
https://bcodes.io/blog/post/angular-universal-relative-to-absolute-http-interceptor
I created "universal-relative.interceptor.ts" file at my src folder
put this interceptor code in "universal-relative.interceptor.ts" file
import { HttpHandler, HttpInterceptor, HttpRequest } from '#angular/common/http';
import { Inject, Injectable, Optional } from '#angular/core';
import { REQUEST } from '#nguniversal/express-engine/tokens';
import { Request } from 'express';
// case insensitive check against config and value
const startsWithAny = (arr: string[] = []) => (value = '') => {
return arr.some(test => value.toLowerCase().startsWith(test.toLowerCase()));
};
// http, https, protocol relative
const isAbsoluteURL = startsWithAny(['http', '//']);
#Injectable()
export class UniversalRelativeInterceptor implements HttpInterceptor {
constructor(#Optional() #Inject(REQUEST) protected request: Request) {}
intercept(req: HttpRequest<any>, next: HttpHandler) {
if (this.request && !isAbsoluteURL(req.url)) {
const protocolHost = `${this.request.protocol}://${this.request.get(
'host'
)}`;
const pathSeparator = !req.url.startsWith('/') ? '/' : '';
const url = protocolHost + pathSeparator + req.url;
const serverRequest = req.clone({ url });
return next.handle(serverRequest);
} else {
return next.handle(req);
}
}
}
Go to your "app.server.module.ts" file
add your interceptor like this
import { NgModule } from '#angular/core';
import {
ServerModule,
ServerTransferStateModule,
} from "#angular/platform-server";
import { AppModule } from './app.module';
import { AppComponent } from './app.component';
import { UniversalRelativeInterceptor } from 'src/universal-relative.interceptor';
import { HTTP_INTERCEPTORS } from '#angular/common/http';
#NgModule({
imports: [AppModule, ServerModule, ServerTransferStateModule],
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: UniversalRelativeInterceptor,
multi: true,
},
],
bootstrap: [AppComponent],
})
export class AppServerModule {}
And the error was GONE!
For me simply the error was that my API variable was undefined, because of the Angular SSR life-cycle. The data was only available after the browser module loaded.
I was using something like
this.isBrowser$.subscribe(isBrowser => { ... });
to set the appropriate api endpoint.
As David replied in the original issue, in my case was the resourceUrl variable that I was using, was not absolute for production environment.
environment.ts
export const environment = {
resourceUrl: 'http://localhost:8082/api/site',
siteId: '1111'
};
Like you see, for development, I was using an absolute url "http://localhost:8082/api/site" for resourceUrl environment variable. Ofcourse this was working on development mode.
environment.prod.ts
export const environment = {
resourceUrl: '/api/site',
siteId: '1111'
};
In production mode I was using a relative url (/api/site), and this was causing the issue while running "serve:ssr" which is production.
return this.http.get<ISomething>(`${environment.resourceUrl}/home/${environment.siteId}`);
So I changed environment.prod.ts to use an absolute URL. Then the issue was gone.
I am adding this reply, since maybe someone doesnt look at David comment. Thanks David.
In case someone needs, if you are using ng-universal, and because the server side rendering caused the error, then you can simply use
if (typeof window === 'object') {
// your client side httpClient code
}

Routing Module loads before APP_INITIALIZER

I have a value that is from of a config file from static AppConfigService.
Described below:
reference code/article: https://blogs.msdn.microsoft.com/premier_developer/2018/03/01/angular-how-to-editable-config-files/
import { Injectable } from '#angular/core';
import { AppConfig } from './app-config';
import { HttpClient } from '#angular/common/http';
import { environment } from 'src/environments/environment';
#Injectable()
export class AppConfigService {
static settings: AppConfig;
constructor(private http: HttpClient) { }
load() {
console.log('is this getting fired before routing module check?');
const jsonFile = `assets/config/config.${environment.name}.json`;
return new Promise<void>((resolve, reject) => {
this.http.get(jsonFile)
.toPromise()
.then((response: AppConfig) => {
AppConfigService.settings = <AppConfig>response;
console.log(AppConfigService.settings);
resolve();
})
.catch((response: any) => {
reject(`Could not load file '${jsonFile}':
${JSON.stringify(response)}`);
});
});
}
}
This config gets loaded in my APP_INITIALIZER in the app.module.ts
providers: [
AppConfigService,
{
provide: APP_INITIALIZER,
useFactory: (appConfigService: AppConfigService) => () => {appConfigService.load() },
deps: [AppConfigService], multi: true
}
],
but my routing module, named AppRoutingModule is reading something out of my AppConfigService.settings variable which is crazy enough, UNDEFINED. My application crashes. I expect the APP_INITIALIZER to fire BEFORE AppRoutingModule but this is not the case:
Uncaught TypeError: Cannot read property 'oldUrl' of undefined
oldUrl is a property of AppConfigService.settings. I checked if AppConfigService.settings is set, it IS, properly AFTER routing module is fired but this is not what I want.
I checked some other sources for help. I used the following already as maybe a fix: https://github.com/angular/angular/issues/14615 and https://github.com/angular/angular/issues/14588
#component({})
class App {
constructor(router: Router, loginService: LoginService) {
loginService.initialize();
router.initialNavigation();
}
}
#NgModule({
imports: [
BrowserModule,
RouterModule.forRoot(routes, {initialNavigation: false})
],
declarations: [ App ],
bootstrap: [ App ],
providers: [ Guard, LoginService ]
})
export class AppModule {
}
Unfortunately, the above solution is not fixing my problem. I also tried to put in AppModule but alas, that didn't help either.
Any help is very welcome.
I've solved my App Initialization and Routing with NgRx listening the central state to know when the system is Loaded and activating the route Guards after that.
But for a direct solution, you need to add a Route Guard checking when your service is loaded. So, add a loaded: boolean flag in your Service, and check it from a Guard like this:
https://github.com/angular/angular/issues/14615#issuecomment-352993695
This is better handled with Observables tho, and I'm wiring all with NgRx in my Apps using Facades to facilitate everything:
https://gist.github.com/ThomasBurleson/38d067abad03b56f1c9caf28ff0f4ebd
Best regards.

Categories