Javascript

What is the NodeJS equivalent of Python’s if name == “__main__”

Whereas in Python you would use

if name == "__main__":
    # TODO Your code goes here

in NodeJS you can simply use

if (require.main === module) {
    // TODO Your code goes here
}

 

Posted by Uli Köhler in Javascript, NodeJS

How to set Koa HTTP response code

In order to set the HTTP response code using Koa, use ctx.status = ...

ctx.status = 403;

Full example:

const Koa = require("koa");

const app = new Koa();

app.use(async (ctx, next) => {
  ctx.status = 403;
  ctx.body = "Forbidden";
});

app.listen(3000);

 

Posted by Uli Köhler in Javascript, NodeJS

How to fix Angular ROR Error: ngIfElse must be a TemplateRef, but received ‘[object HTMLDivElement]’.

Problem:

Your Angular template looks like this:

<div *ngIf="files.length > 0 ; else noFiles">
    <!-- ... -->
</div>
<div #noFiles>
    <!-- ... -->
</div>

but when you open it in the browser, you see the following stacktrace:

c_my_module.js:2 ERROR Error: ngIfElse must be a TemplateRef, but received '[object HTMLDivElement]'.
    at assertTemplate (common.mjs:3392:15)
    at set ngIfElse [as ngIfElse] (common.mjs:3328:9)
    at setInputsForProperty (core.mjs:11741:34)
    at elementPropertyInternal (core.mjs:10874:9)
    at ɵɵproperty (core.mjs:13637:9)
    at PlanungsunterlagenDashboardComponent_Template (planungsunterlagen-dashboard.component.html:8:55)
    at executeTemplate (core.mjs:10534:9)
    at refreshView (core.mjs:10419:13)
    at refreshComponent (core.mjs:11480:13)
    at refreshChildComponents (core.mjs:10210:9)

Solution:

The error message is telling you that #noFiles is a <div> but it must be a <ng-template>:

In order to fix it, change it to a ng-template:

<ng-template #noFiles>
    <!-- ... -->
</ng-template>

If you need a <div> specifically, you need to place it inside the ng-template:

<ng-template #noFiles>
    <div>
        <!-- ... -->
    </div>
</ng-template>

 

Posted by Uli Köhler in Angular, Javascript

How to fix docker NodeJS container not shutting down

When you are running a NodeJS server inside a docker container, you will often encouter the issue that the container does not shutdown properly but taking a long time (several minutes) to shutdown.

In order to fix this, add the following code to your main NodeJS file (typically you should add it at the top of the file to make sure nothing prevents it from getting executed)

process.on('SIGTERM', function() {
  console.log('SIGTERM received, shutting down...');
  process.exit(0);
});
process.on('SIGINT', function() {
  console.log('SIGINT received, shutting down...');
  process.exit(0);
});

This will force NodeJS to exit (more or less immediately) once either SIGINT or SIGTERM is received. Typically, Docker sends SIGTERM on container shutdown.

Background information

See this GitHub guide

Posted by Uli Köhler in NodeJS

How to fix Angular “ng build” not producing index.html

Unless you have errors in your build (which is clearly visible from looking at the ng build output), the reason why ng build doesn’t produce an index.html is that the resulting bundle exceeds the maximum allowed size.

This is evident from the output such as

Error: bundle initial exceeded maximum budget. Budget 1.00 MB was not met by 3.73 kB with a total of 1.00 MB.

In order to fix it, either shrink your application by removing unneeded features or libraries or splitting off certain features into extra modules or, at least temporarily, increase the allowable budget.

In order to do that, open angular.json and look for the "budgets": {... section:

"budgets": [
  {
    "type": "initial",
    "maximumWarning": "500kb",
    "maximumError": "1mb"
  },
  {
    "type": "anyComponentStyle",
    "maximumWarning": "2kb",
    "maximumError": "4kb"
  }
],

Increase the maximumError value for "type": "initial" (the first entry in the list shown above).

For example, you could increase it from 1mb to 4mb to fix your build.

After that, run your ng build command again and index.html should be generated.

Be aware, however, that having huge JS bundles can really slow down your web application especially for mobile users.

Posted by Uli Köhler in Angular

Angular HttpClient: Observable with data continous data stream using interval requests to API endpoint

The following example requests /api/data every second, creating an Observable where you can subscribe to the continous value stream.

import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { interval, mergeMap, Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class DataService {

  constructor(private http: HttpClient) { }

  continousDataStream(_interval=1000): Observable<any> {
    return interval(_interval).pipe(mergeMap(_ => {
        return this.http.get<any>(`/api/data`);
    }));
  }
}

 

Posted by Uli Köhler in Angular

How to fix NodeJS Error: EXDEV: cross-device link not permitted, rename …

Problem:

While trying to rename a file using fs.move() in NodeJS, you see an error message such as

Error: EXDEV: cross-device link not permitted, rename '/tmp/upload_8a8c71784abf9942b40c1359935b1997' -> 'myfile.pdf'

Solution:

This error occurs because you are moving a file from one drive (/tmp in our example above) to another drive (the current directory in our example above). On most platforms, this can’t be done using a simple rename operation.

Instead, fs.copyFile() the file.

For example, instead of

await fs.move('/tmp/upload_8a8c71784abf9942b40c1359935b1997', 'myfile.pdf')

first, copy the file:

await fs.copyFile('/tmp/upload_8a8c71784abf9942b40c1359935b1997', 'myfile.pdf')

and then – if desired – remove the source file to mirror the behaviour of os.move() as closely as possible.

await fs.remove('/tmp/upload_8a8c71784abf9942b40c1359935b1997', 'myfile.pdf')
await fs.unlink('/tmp/upload_8a8c71784abf9942b40c1359935b1997')

 

Posted by Uli Köhler in NodeJS

How to get filesize in NodeJS / TypeScript using Promisese

First, import stat from the NodeJS standard library:

import { stat } from "node:fs/promises";

Async-await style

// Async-await style:
const statResult = await stat("myfile.pdf");
const fileSizeInBytes = statResult.size;

Promise.then() style

stat("myfile.pdf").then(statResult => {
    const fileSizeInBytes = statResult.size;
    // TODO your code goes here
});

 

Posted by Uli Köhler in NodeJS, Typescript

How I fixed WordPress category page title: Category <span>…</span>

One one of my WordPress sites, the title of category pages was literally

Category <span>My category</span>

Searching through the theme source code didn’t yield a solution quickly, so I added this custom JS using a plugin:

jQuery(document).ready(function( $ ){
    if(jQuery("h1.page-title").text().includes("Category: <span>")) {
        var input = jQuery("h1.page-title").text();
        var output = /<span>(.*)<\/span>/g.exec(input)[1];
        jQuery("h1.page-title").text(output);
    }
});

This will just replace the title by the content of the span tag. You might need to adjust the CSS classes for your theme.

Posted by Uli Köhler in Javascript, Wordpress

How to fix Angular NullInjectorError: No provider for HttpClient!

In case you see the following error for your Angular application in your JS console:

main.ts:6 ERROR NullInjectorError: R3InjectorError(AppModule)[MyService -> HttpClient -> HttpClient -> HttpClient]: 
  NullInjectorError: No provider for HttpClient!
    at NullInjector.get (core.mjs:7599:27)
    at R3Injector.get (core.mjs:8020:33)
    at R3Injector.get (core.mjs:8020:33)
    at R3Injector.get (core.mjs:8020:33)
    at injectInjectorOnly (core.mjs:634:33)
    at Module.ɵɵinject (core.mjs:638:60)
    at Object.MyService_Factory [as factory] (my.service.ts:8:38)
    at R3Injector.hydrate (core.mjs:8121:35)
    at R3Injector.get (core.mjs:8009:33)
    at ChainedInjector.get (core.mjs:12179:36)

you need to add HttpClientModule to your app.module.ts.

First, import HttpClientModule at the top of app.module.ts:

import { HttpClientModule } from '@angular/common/http';

After that, add

HttpClientModule,

to the imports: [...] section of your @NgModule, for example:

@NgModule({
  declarations: [
    AppComponent,
  ],
  imports: [
    HttpClientModule,
    BrowserModule,
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

After that, your error should have disappeared

Posted by Uli Köhler in Angular, Typescript

Angular HttpClient JSON service minimal example

Using Angular’sHttpClient  in a service to fetch a JSON is pretty straightforward. In this example, we’ll fetch a JSON array.

Note that it’s recommended to create your own TypeScript interface for improved static typing, but nevertheless starting with any[] is often a good idea since it allows you to get started quickly.

public loadMyJSONArray(): Observable<any[]> {
  return this.http.get<any[]>(`/api/my-api`);
}

Full service example

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class MyService {

  constructor(private http: HttpClient) {
  }

  public loadMyJSONArray(): Observable<any[]> {
    return this.http.get<any[]>(`/api/my-api`);
  }

}

 

 

Posted by Uli Köhler in Angular, Typescript

How to escape double quotes (“) in stirng

Note: This solution does not deal with double quotes which are escaped already correctly, plus it does not correctly escape backslashes. However it can still serve as a starting point for doing customized escaping.

'foo"bar"gas'.replace(/"/g, "\\\"") # Results in foo\"bar\"gas

 

Posted by Uli Köhler in Javascript

Remove all slashes from string using pure Javascript

"foo/bar/gas".replace(/\//g, "") # foobargas

 

Posted by Uli Köhler in Javascript

Simple tsconfig.json template for NodeJS

Use this tsconfig.json as a starting point to setup your NodeJS TypeScript project:

{
  "compilerOptions": {
    "strict": false,
    "target": "es2020",
    "module": "commonjs",
    "moduleResolution": "node",
    "esModuleInterop": true,
    "sourceMap": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "skipLibCheck": true,
    "outDir": "./build",
    "rootDir": "./",
    "lib": [
      "esnext"
    ],
    "forceConsistentCasingInFileNames": true
  },
  "include": [
    "**/*.ts",
    "types/*.ts"
  ],
  "exclude": [
    "node_modules"
  ]
}

 

Posted by Uli Köhler in NodeJS, Typescript

How to import @koa/router in Typescript

import Router from '@koa/router';
import Koa from 'koa';

const app = new Koa();
const router = new Router();

 

Posted by Uli Köhler in NodeJS, Typescript

How to import Koa in Typescript

import Koa from 'koa';

const app = new Koa();

/* TODO your code goes here */

 

Posted by Uli Köhler in NodeJS, Typescript

Minimal Koa Typescript example using @koa/router and koa-body

This example showcases how to make a simple Hello World server using Koa in Typescript.

import Router from '@koa/router';
import koaBody from 'koa-body';
import Koa from 'koa';

const router = new Router();
const app = new Koa();

app.use(koaBody());

router.get('/', (ctx)  => {
    ctx.body = "Hello world";
});

app
    .use(router.routes())
    .use(router.allowedMethods());

if (!module.parent) app.listen(3000);
{
  "compilerOptions": {
    "strict": true,
    "target": "es2020",
    "module": "commonjs",
    "moduleResolution": "node",
    "esModuleInterop": true,
    "sourceMap": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "skipLibCheck": true,
    "outDir": "./build",
    "rootDir": "./",
    "lib": [
      "esnext"
    ],
    "forceConsistentCasingInFileNames": true
  },
  "include": [
    "**/*.ts",
    "types/*.ts"
  ],
  "exclude": [
    "node_modules"
  ]
}
{
  "name": "KoaTypescriptTest",
  "version": "1.0.0",
  "description": "",
  "scripts": {
    "start": "./node_modules/.bin/tsnd --respawn server.ts",
    "build": "./node_modules/.bin/tsc -b tsconfig.json"
  },
  "dependencies": {
    "@koa/router": "^12.0.0",
    "koa": "^2.14.1",
    "koa-body": "^6.0.1"
  },
  "devDependencies": {
    "@types/koa": "^2.13.5",
    "@types/koa__router": "^12.0.0",
    "@types/node": "^18.11.17",
    "ts-node-dev": "^1.1.8",
    "typescript": "^4.9.4"
  }
}

 

Posted by Uli Köhler in NodeJS, Typescript

How to auto-fill & auto-submit Google Search Console sitemap using Tapermonkey

When using Google Search Console, you might want to submit your sitemap manually from time to time to accelerate indexing of new content on your website.

The following Tapermonkey script will automatically fill in sitemap.xml and click the Send button. Depending on your language setting, you will need to adjust the document.querySelector() calls and modify the script header according to your domain (mydomain.com in this example):

// ==UserScript==
// @name         SearchConsoleAutoFillSitemap
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  ?
// @author       You
// @match        https://search.google.com/search-console/sitemaps?resource_id=https%3A%2F%2Fmydomain.com%2F&hl=de
// @icon         https://www.google.com/s2/favicons?sz=64&domain=tampermonkey.net
// @grant        none
// ==/UserScript==
(function() {
    'use strict';
    // Simulate input
    let elem = document.querySelector("input[aria-label='Sitemap-URL eingeben']");
    elem.value = "sitemap.xml";
    elem.dispatchEvent(new Event('input', {
        bubbles: true,
        cancelable: true,
    }));
    // Click "Send" button
    setTimeout(() => {
        console.log("Clicking Send button");
        document.querySelector("c-wiz[data-node-index='2;0'] div[tabindex='0']").click();
    }, 500);
})();

 

Posted by Uli Köhler in Javascript

Javascript: How to send event after changing programmatically without JQuery

This snippet will change an input programmatically and then dispatch an input event which will tell the javascript code running on the page that the input has changed.

let myinput = document.querySelector("input#myinput");
// Set value
myinput.value = "newvalue";
// Simulate input event
myinput.dispatchEvent(new Event('input', {
    bubbles: true,
    cancelable: true,
}));

 

Posted by Uli Köhler in Javascript

How to set value of input in Tapermonkey script

This Tapermonkey script will find <input id="myinput"> and set its value to newvalue.

(function() {
    'use strict';
    document.querySelector("input#myinput").value = "newvalue";
})();

You’ll need to add a suitable header – it’s often best just to use the default template and modify only the URL.

Posted by Uli Köhler in Javascript