1

while trying to create a simple application on Angular and Apollo, I stumbled upon a problem when I try to build the app using ng build although the app works perfectly on localhost:4200. The app connects to a Drupal 8 GraphQL server and the connection works.

This is the error while building:

ERROR in app/idea-list/idea-list.component.ts(27,37): error TS2339: Property 'nodeQuery' does not exist on type '{}'. app/idea-list/idea-list.component.ts(28,36): error TS2339: Property 'loading' does not exist on type '{}'.

This is the graphql.ts file:

import {Article} from './type';
import gql from 'graphql-tag';

export const ALL_ARTICLES_QUERY = gql `
query {
   nodeQuery {
    entities {
      title: entityLabel
      ... on NodeArticle {
        body {
          value
        }
      }
    }
  }
}
`;

export interface AllArticlesQueryResponse {
    allArticles: Article[];
    loading: boolean;
}

This is the result from the GraphQL server:

{
  "data": {
    "nodeQuery": {
      "entities": [
        {
          "title": "Article title.",
          "body": {
            "value": "<p>Article body value.</p>\r\n"
          }
        }
      ]
    }
  }
}

And this is the app/idea-list/idea-list.component.ts file:

import { Component, OnInit } from '@angular/core';
import { Apollo } from 'apollo-angular';
import {Article } from '../type';
import {ALL_ARTICLES_QUERY, AllArticlesQueryResponse} from '../graphql';

@Component({
  selector: 'app-articles-list',
  templateUrl: './articles-list.component.html',
  styleUrls: ['./articles-list.component.css']
})
export class ArticlesListComponent implements OnInit {

  allArticles: Article[] = [];
  loading: boolean = true;

  constructor(private apollo: Apollo) { }

  ngOnInit() {
    this.apollo.watchQuery({
        query: ALL_ARTICLES_QUERY
    }).valueChanges.subscribe((response) => {
      console.log(response);
      this.allArticles = response.data.nodeQuery.entities;
      this.loading = response.data.loading;
    });
  }
}

UPDATE: There is no error in browser's console and the app works fine. However, while building the app (when I change something in the code), this is happening in terminal window:

webpack: Compiling...
Date: 2018-04-25T09:09:23.622Z - Hash: b0a12071eaf44c16d874 - Time: 626ms
4 unchanged chunks
chunk {main} main.bundle.js (main) 36.3 kB [initial] [rendered]

webpack: Compiled successfully.
ERROR in src/app/articles-list/articles-list.component.ts(22,40): error TS2339: Property 'nodeQuery' does not exist on type '{}'.
src/app/articles-list/articles-list.component.ts(23,36): error TS2339: Property 'loading' does not exist on type '{}'.

The same error prevents me from building the app (ng build).

Additional info (config files):

tsconfig.ts

{
  "compileOnSave": false,
  "compilerOptions": {
    "outDir": "./dist/out-tsc",
    "sourceMap": true,
    "declaration": false,
    "moduleResolution": "node",
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "target": "es5",
    "typeRoots": [
      "node_modules/@types"
    ],
    "lib": [
      "es2017",
      "dom",
      "esnext"
    ]
  }
}

app/tsconfig.app.json

{
  "extends": "../tsconfig.json",
  "compilerOptions": {
    "outDir": "../out-tsc/app",
    "baseUrl": "./",
    "module": "es2015",
    "types": []
  },
  "exclude": [
    "test.ts",
    "**/*.spec.ts"
  ]
}

package.json

{
  "name": "petiangular",
  "version": "0.0.0",
  "license": "MIT",
  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build --prod",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e"
  },
  "private": true,
  "dependencies": {
    "@angular/animations": "^5.2.0",
    "@angular/common": "^5.2.0",
    "@angular/compiler": "^5.2.0",
    "@angular/core": "^5.2.0",
    "@angular/forms": "^5.2.0",
    "@angular/http": "^5.2.0",
    "@angular/platform-browser": "^5.2.0",
    "@angular/platform-browser-dynamic": "^5.2.0",
    "@angular/router": "^5.2.0",
    "apollo-angular": "^1.0.1",
    "apollo-angular-link-http": "^1.0.3",
    "apollo-cache-inmemory": "^1.1.12",
    "apollo-client": "^2.2.8",
    "bootstrap": "^3.3.7",
    "core-js": "^2.4.1",
    "graphql": "^0.13.2",
    "graphql-tag": "^2.9.1",
    "rxjs": "^5.5.6",
    "zone.js": "^0.8.19"
  },
  "devDependencies": {
    "@angular/cli": "~1.7.4",
    "@angular/compiler-cli": "^5.2.0",
    "@angular/language-service": "^5.2.0",
    "@types/jasmine": "~2.8.3",
    "@types/jasminewd2": "~2.0.2",
    "@types/node": "~6.0.60",
    "codelyzer": "^4.0.1",
    "jasmine-core": "~2.8.0",
    "jasmine-spec-reporter": "~4.2.1",
    "karma": "~2.0.0",
    "karma-chrome-launcher": "~2.2.0",
    "karma-coverage-istanbul-reporter": "^1.2.1",
    "karma-jasmine": "~1.1.0",
    "karma-jasmine-html-reporter": "^0.2.2",
    "protractor": "~5.1.2",
    "ts-node": "~4.1.0",
    "tslint": "~5.9.1",
    "typescript": "~2.5.3"
  }
}

.angular.cli.json

{
  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
  "project": {
    "name": "petiangular"
  },
  "apps": [
    {
      "root": "src",
      "outDir": "dist",
      "assets": [
        "assets",
        "favicon.ico"
      ],
      "index": "index.html",
      "main": "main.ts",
      "polyfills": "polyfills.ts",
      "test": "test.ts",
      "tsconfig": "tsconfig.app.json",
      "testTsconfig": "tsconfig.spec.json",
      "prefix": "app",
      "styles": [
        "../node_modules/bootstrap/dist/css/bootstrap.css",
        "styles.css"
      ],
      "scripts": [],
      "environmentSource": "environments/environment.ts",
      "environments": {
        "dev": "environments/environment.ts",
        "prod": "environments/environment.prod.ts"
      }
    }
  ],
  "e2e": {
    "protractor": {
      "config": "./protractor.conf.js"
    }
  },
  "lint": [
    {
      "project": "src/tsconfig.app.json",
      "exclude": "**/node_modules/**"
    },
    {
      "project": "src/tsconfig.spec.json",
      "exclude": "**/node_modules/**"
    },
    {
      "project": "e2e/tsconfig.e2e.json",
      "exclude": "**/node_modules/**"
    }
  ],
  "test": {
    "karma": {
      "config": "./karma.conf.js"
    }
  },
  "defaults": {
    "styleExt": "css",
    "component": {}
  }
}
petiar
  • 1,047
  • 2
  • 14
  • 31
  • I don't understand? does everything work even the data in the graph but you get and error in the browser console anyways but that error doesn't break the execution either and it keeps the website keeps running fine? I'll help get rid of it, I just want to know what we're dealing with. – tatsu Apr 25 '18 at 08:49
  • Thanks a lot, @tatsu. I have updated my question. Basically, the app works fine, there is no error in console, it only throws the error when compiling/building even though it works alright in the browser. – petiar Apr 25 '18 at 09:15
  • oh! ok. I get it. – tatsu Apr 25 '18 at 09:18

2 Answers2

0

This compilation error could be due to many things but I suspect it's occurring during transpiling.

And the bug is very likely originating from the Drupal 8 GraphQL.

But it's possible that by fiddling around with ECMAscript levels and/or JIT vs AOT compilation settings you may get rid of it.

But first I would recommend you try deploying the app.

Do a production build and see if then the slight hitch actually becomes a showstopper and crashes your compilation process.

If it doesn't then you may simply report it to Drupal 8 GraphQL here.

and after that stop caring about it since it doesn't affect your app's lifecycle and doesn't show up in the browser console (ergo it literally will not exist for the end user).

If it does crash a production compilation then we'll try different compilation options.

tatsu
  • 2,316
  • 7
  • 43
  • 87
  • Thanks for your help! How would the bug come from D8 GQL when the data provided are coming through correctly? Anyway, as I mentioned above, the deployment does not work. After running the `ng build --prod` command I am getting those errors mentioned above. No dist folder is created. – petiar Apr 25 '18 at 09:31
  • alright so the bug is fatal when switching to production, it's as I feared. (and as for the bug still being on D8 GQL despite the data coming through directly, I don't know how their code works but from your pasted code the bug doesn't seem to me to be at an angular level). try going into `//tsconfig.json` and `//src/tsconfig.app.json` and add those to your Question as well as your full `//package.json` and `//.angular-cli.json` – tatsu Apr 25 '18 at 09:45
  • Done. Thank you. – petiar Apr 25 '18 at 12:24
  • shit you have the right values everywhere x) I'm afraid this is where I run out of ideas. Try importing `AfterViewInit`, adding it to the `implements` section, and moving your code in `ngOnInit` to `ngAfterViewInit`, and why not adding a `setTimeout(() => {your code}, 2000);` while you're at it. It could help confirm that this has to do with the value being uninstaciated on one of the earlier calls. – tatsu Apr 25 '18 at 12:50
  • @petiar this https://stackoverflow.com/questions/48809849 is the issue I had which your issue reminds me of. as you can see I tried compiling for production with JIT compilation forced. maybe you can try this. try also messing with all the ECMAscript values except for target in tsconfig.json – tatsu Apr 25 '18 at 15:41
0

you do few things incorrectly so let me explain.

First of all, it's a TypeScript issue and it has nothing to do with runtime javascript issues so that's why you see it in the terminal. TypeScript warns you that a property nodeQuery is not available in an object. That's because for TypeScript, this object is empty, has no properties.

import {Article} from '../type';
import {ALL_ARTICLES_QUERY, AllArticlesQueryResponse} from '../graphql';

export class ArticlesListComponent implements OnInit {

  allArticles: Article[] = [];
  loading: boolean = true;

  constructor(private apollo: Apollo) { }

  ngOnInit() {
    this.apollo.watchQuery({
        query: ALL_ARTICLES_QUERY
    }).valueChanges.subscribe((response) => {
      // response.data is {} (for typescript)
      // it means it's an empty object which has no properties
      this.allArticles = response.data.nodeQuery.entities;

      // another issues is that `loading` should be under `response` not `response.data`
      // it should be `response.loading`
      this.loading = response.data.loading;
    });
  }
}

Apollo doesn't know what what is the interface of your result but it allows you to do define it. I see you have an interface defined so let's use it :)

import {Article} from '../type';
import {ALL_ARTICLES_QUERY, AllArticlesQueryResponse} from '../graphql';

export class ArticlesListComponent implements OnInit {

  allArticles: Article[] = [];
  loading: boolean = true;

  constructor(private apollo: Apollo) { }

  ngOnInit() {
    // we define it as a first generic type of watchQuery method
    this.apollo.watchQuery<AllArticlesQueryResponse>({
        query: ALL_ARTICLES_QUERY
    }).valueChanges.subscribe((response) => {
      this.allArticles = response.data.nodeQuery.entities;
      this.loading = response.loading;
    });
  }
}

You don't have to write interface by hand, there are tools that does that for you. Read my answer here: https://stackoverflow.com/a/52454671/4035768

Kamil Kisiela
  • 474
  • 4
  • 5