carbon-tutorial-angular
carbon-tutorial-angular copied to clipboard
Step 3 - [SOLVED] Everything that goes wrong after adding Apollo
I created this ticket to help fellow coders/learners with the issues I faced with completing step 3.
Many of you may not face any such issue depending on the version of node, angular, and other packages, so first thing first, here's my package.json.
node --version
v14.14.0
npm -version
6.14.8
package.json
{
"name": "carbon-angular-starter",
"version": "0.1.0",
"license": "",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build --prod --aot",
"test": "ng test",
"test:once": "ng test --watch=false",
"lint": "ng lint",
"e2e": "ng e2e"
},
"engines": {
"node": ">= 10.9"
},
"private": true,
"dependencies": {
"@angular/animations": "^8.2.0",
"@angular/common": "^8.2.0",
"@angular/compiler": "^8.2.0",
"@angular/core": "^8.2.0",
"@angular/forms": "^8.2.0",
"@angular/platform-browser": "^8.2.0",
"@angular/platform-browser-dynamic": "^8.2.0",
"@angular/router": "^8.2.0",
"@carbon/icons-angular": "^10.4.0",
"apollo-angular": "^2.0.4",
"carbon-components": "^10.7.0",
"carbon-components-angular": "^3.21.2",
"core-js": "^2.5.2",
"rxjs": "^6.4.0",
"tslib": "^1.9.0",
"zone.js": "~0.9.1",
"@apollo/client": "^3.0.0",
"graphql": "^15.0.0"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.802.1",
"@angular/cli": "^8.2.1",
"@angular/compiler-cli": "^8.2.0",
"@types/graphql": "^14.5.0",
"@types/jasmine": "^2.8.2",
"@types/node": "~8.0.53",
"codelyzer": "^5.0.1",
"jasmine-core": "~3.4.0",
"jasmine-spec-reporter": "~4.2.1",
"karma": "^3.1.1",
"karma-chrome-launcher": "~2.2.0",
"karma-cli": "2.0.0",
"karma-coverage-istanbul-reporter": "^2.0.5",
"karma-jasmine": "~2.0.1",
"karma-jasmine-html-reporter": "^1.4.0",
"node-sass": "^4.12.0",
"postcss-loader": "3.0.0",
"protractor": "^5.4.2",
"ts-node": "~8.0.3",
"tslint": "^5.15.0",
"typescript": "3.5.3"
}
}
- The first issue I faced was with the following few errors, in no particular order. The code won't build.
ERROR in ../node_modules/@apollo/client/core/ObservableQuery.d.ts:22:9 - error TS1086: An accessor cannot be declared in an ambient context.
22 get variables(): TVariables | undefined;
~~~~~~~~~
../node_modules/@apollo/client/utilities/observables/Observable.d.ts:1:8 - error TS1259: Module '"/Users/pooshonbanerjee/Documents/Pooshon/IBM Projects/2020/IBM Carbon Design - Angular/carbon-tutorial-angular/node_modules/@types/zen-observable/index"' can only be default-imported using the 'allowSyntheticDefaultImports' flag
1 import Observable from 'zen-observable';
~~~~~~~~~~
../node_modules/@types/zen-observable/index.d.ts:69:1
69 export = Observable;
~~~~~~~~~~~~~~~~~~~~
This module is declared with using 'export =', and can only be used with a default import when using the 'allowSyntheticDefaultImports' flag.
../node_modules/apollo-angular/apollo.d.ts:32:9 - error TS1086: An accessor cannot be declared in an ambient context.
32 get client(): ApolloClient<TCacheShape>;
~~~~~~
../node_modules/apollo-angular/apollo.d.ts:39:9 - error TS1086: An accessor cannot be declared in an ambient context.
39 set client(client: ApolloClient<TCacheShape>);
~~~~~~
../node_modules/graphql/subscription/subscribe.d.ts:41:12 - error TS2304: Cannot find name 'AsyncIterableIterator'.
41 ): Promise<AsyncIterableIterator<ExecutionResult> | ExecutionResult>;
~~~~~~~~~~~~~~~~~~~~~
../node_modules/graphql/subscription/subscribe.d.ts:52:12 - error TS2304: Cannot find name 'AsyncIterableIterator'.
52 ): Promise<AsyncIterableIterator<ExecutionResult> | ExecutionResult>;
~~~~~~~~~~~~~~~~~~~~~
../node_modules/graphql/subscription/subscribe.d.ts:80:12 - error TS2304: Cannot find name 'AsyncIterable'.
80 ): Promise<AsyncIterable<any> | ExecutionResult>;
~~~~~~~~~~~~~
To solve this you have to modify the typings.d.ts file like it said on the tutorial for step 3, by commenting the module variable declaration.
But the next part about modifying tsconfig.json doesn't work since angular cli >= 7 ignores that file, so instead you have to make the following changes in the tsconfig.app.json.
allowSyntheticDefaultImports, esModuleInterop, strictNullChecks, skipLibCheck, esnext.asynciterable
{
"compilerOptions": {
"sourceMap": true,
"declaration": false,
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"strictNullChecks": false,
"skipLibCheck": true,
"lib": [
"es2016",
"dom",
"esnext.asynciterable"
],
"outDir": "../out-tsc/app",
"target": "es2015",
"module": "esnext",
"baseUrl": "",
"types": []
},
"include": [
"**/*"
],
"exclude": [
"test.ts",
"**/*.spec.ts"
]
}
This will get the code to build with no errors, but that won't be the end of it. When you run ng serve, you will see that the /repo route will come up blank and the dev console will show a long list of errors, beginning with No provider for Apollo!.
To solve this, do the following, and this is not mentioned in the training anywhere:
graphql.modue.ts
import { NgModule } from '@angular/core';
import { APOLLO_OPTIONS } from 'apollo-angular';
import { ApolloClientOptions, InMemoryCache } from '@apollo/client/core';
// import { InMemoryCache } from '@apollo/client/core';
import { Apollo } from 'apollo-angular';
import { HttpLink } from 'apollo-angular/http';
import { environment } from '../environments/environment';
import { HttpHeaders } from '@angular/common/http';
const uri = 'https://api.github.com/graphql'; // <-- add the URL of the GraphQL server here
// export function createApollo(httpLink: HttpLink): ApolloClientOptions<any> {
export function createApollo(httpLink: HttpLink) {
return {
link: httpLink.create({uri,
headers: new HttpHeaders({
Authorization: `Bearer ${environment.githubPersonalAccessToken}`,
}),
}),
cache: new InMemoryCache(),
};
}
@NgModule({
providers: [ Apollo, HttpLink,
{
provide: APOLLO_OPTIONS,
useFactory: createApollo,
deps: [HttpLink],
},
],
})
export class GraphQLModule {}
Add provider for both Apollo and HttpLink under @NgModule. Make sure your imports match my imports above.
In repo-table-component.ts, change the Apollo import statement to ...
import { Apollo, gql } from 'apollo-angular';
Since Apollo version 2.x, gql is a part of apollo-angular.
Now this solves the dev console errors too. If you have not yet gotten to the part where you add the Skeleton and Pagination, then you won't see any real data yet because there is a slight mistake there as well.
Consider this part.

The statement this.prepareData(response.data.organization.repositories.nodes); alone will not make any difference, and you'll still see the static rows you added to ngOnInit() since the data for the model is never populated with the response data after the query has been executed. So to make this work, you have to modify this line to the following:
this.model.data = this.prepareData(response.data.organization.repositories.nodes);
Now, you'll see the data Apollo queries from the Git API.
Remember, when you delete the static rows from ngOnInit() as instructed, keep the model.header and keep the model declaration, you'll need those at the pagination step. Only delete the static rows.
Once you add the skeleton part, this /repo page will again come up blank, so do not worry, carry on to the end and complete everything with the pagination part. This will add the missing pieces from the skeleton step and you will see everything nicely packed with a pagination component.
Now that the running part is over, you'd move to ng lint --fix.
This gave me a lot of repo-table.component.ts:36:1 - tab indentation expected errors pointing to the gql query section which it could not fix, so I had to manually go into the editor and replace the spaces with tabs. I hope you don't get the same, but if you do, follow my footsteps and re-indent with tabs.
The second nightmare started here, with npm test, it repeated all the previous build errors and then some. And since I have little idea on how this badge is awarded, I believe that every pull request must pass the tests on GitHub to get approved, and so I had to make sure my code passes npm test on my computer first in order to pass on GitHub.
So I did the following. I will show you the errors first, one by one, with the solution.
First it was this...

Which I solved by modifying tsconfig.spec.json to the following. Same changes as tsconfig.app.json.
{
"compilerOptions": {
"sourceMap": true,
"declaration": false,
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"strictNullChecks": false,
"skipLibCheck": true,
"resolveJsonModule": true,
"lib": [
"dom",
"es2016",
"esnext.asynciterable"
],
"outDir": "../out-tsc/spec",
"module": "esnext",
"target": "es2015",
"baseUrl": "",
"types": [
"jasmine",
"node"
]
},
"files": [
"test.ts",
"polyfills.ts"
],
"include": [
"**/*.spec.ts"
]
}
Then there was a long list of errors about ibm-pagination tag. Shown below, in part...

Which I solved by importing PaginationModule to both repo-table.component.spec.ts and repo-page.component.spec.ts.
Also, after importing this module, you need to add it to the imports: section of TestBed.configureTestingModule.
Don't worry, at the end I will show both the files completely, so you can compare and make changes.
Next there was No provider for Apollo! errors.

Which I fixed by adding ...
import { Apollo } from 'apollo-angular';
to both repo-table.component.spec.ts and repo-page.component.spec.ts.
Also I had to add a providers: section and expose this module to TestBed.configureTestingModule.
providers: [Apollo]
Next there was Error: Client has not been defined yet

Which I fixed by adding ...
import { GraphQLModule } from '../../graphql.module';
to both repo-table.component.spec.ts and repo-page.component.spec.ts. And added GraphQLModule to the imports: section of TestBed.configureTestingModule.
Next there was NullInjectorError: No provider for HttpClient!

Which I solved by adding...
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
to both repo-table.component.spec.ts and repo-page.component.spec.ts. And, adding HttpClientTestingModule to imports: and adding HttpClient to providers" section like this...
providers: [Apollo, HttpClient]
All this got me here ...

If you are wondering about ChromeHeadless have not captured in 120000 ms, killing.. I increased the duration from 1 minutes to 2 minutes since the styles.scss takes long to compile and by that time Chromeheadless times out.
I made that change in karma.conf.js. I also changed autoWatch to false and singleRun to true.
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: false,
browsers: ['ChromeHeadless'],
singleRun: true,
captureTimeout: 120000
So I guess that's it, and here are the files like I promised.
repo-table.component.spec.ts
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { RepoTableComponent } from './repo-table.component';
import { TableModule, LinkModule, PaginationModule } from 'carbon-components-angular';
import { Apollo } from 'apollo-angular';
import { GraphQLModule } from '../../graphql.module';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
describe('RepoTableComponent', () => {
let component: RepoTableComponent;
let fixture: ComponentFixture<RepoTableComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ RepoTableComponent ],
imports: [
TableModule,
LinkModule,
PaginationModule,
GraphQLModule,
HttpClientTestingModule
],
providers: [Apollo, HttpClient]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(RepoTableComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
repo-page.component.spec.js
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { RepoPageComponent } from './repo-page.component';
import { GridModule, TableModule, LinkModule, PaginationModule } from 'carbon-components-angular';
import { RepoTableComponent } from '../repo-table/repo-table.component';
import { Apollo } from 'apollo-angular';
import { GraphQLModule } from '../../graphql.module';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
describe('RepoPageComponent', () => {
let component: RepoPageComponent;
let fixture: ComponentFixture<RepoPageComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ RepoPageComponent, RepoTableComponent ],
imports: [
GridModule,
TableModule,
LinkModule,
PaginationModule,
GraphQLModule,
HttpClientTestingModule
],
providers: [Apollo, HttpClient]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(RepoPageComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
I really hope this helps other people since to get here it took me 3 days of headbanging, and I almost gave up.