Archive for the 'TypeScript' Category

February 12th, 2021
5:33 pm
Dynamic Typing issues with Typescript/Angular 11

Posted under Angular & TypeScript & Web
Tags ,

I was using a JSON file to hold external configuration for an agular application, using the pattern detailed previously here, and the loading mechanism detailed here.

The configuration pattern used allowed parameters to be overridden dynamically from query string parameters, so that for example you could choose an application theme in your URL.

I wanted to further extend this to allow sticky configuration parameters using localStorage, in particular to support a sticky pinnable title banner as detailed previously here. The aim was that both the query string support and the sticky localStorage support would be encapsulated in the configuration pattern and all transparently handled by the existing configuration service. The service would be enhanced with a new method call to allow persisting of a sticky parameter to localStorage and to the loaded config, for example for my sticky pinnable banner, to allow calling to persist the new pin state when the pin state was changed via the pin button. The previous pin state would then be loaded when the site was subsequently visited. Sticky parameters would override settings in the JSON file, and query string parameters would further override both of the former, and a query string parameter would also be sticky and would persist to localStorage, so for example the previous theme selected in the URL would be remembered.

One challenge was that whilst the configuration structure conformed to a Typescript interface, I needed to dynamically update configuration parameters from query string parameters or localStorage. This meant using dynamic keys e.g. based on the querystring parameters. Whilst this was initially possible by casting the config structure and using a map style lookup with config[keyVariable], it was not possible to dynamically infer the type of a configuration parameter from the interface, noting that both query string and localStorage parameters are strings. Often this is not a problem, as the default javascript behaviour will quite often cope, but therein lies a danger. In one case with a boolean, my query string/localStorage parameter was of course a string, and when written to the configuration using a dynamic key name as above, it was written as a string rather than the boolean expected in the interface. This meant that the value ‘false’ was in fact interpreted as true, due to the standard javascript truthy behaviour whereby a non empty string counts as true.

In the end, my solution was to have a subsection in the configuration, called dynamicParams, which contained all these dynamic parameters. It was therefore a requirement that all the dynamic parameters were strings and that client code reading the configuration had to be aware of that and convert as required.

One useful feature was to use Typescript intersection types for the dynamic parameters section, as detailed here. This allowed me to specify the dynamic parameter names explicitly, to allow them to be read in code directly as for example using config.dynamicParams.theme. I could also use programmatic access as above using config[keyVariable].

The section of the interface definition to do this was as follows:-

export interface AppConfig {
   appTitle: string;
   description: string;

   /* These parameters can be overridden from URL query parameters
    * and/or sticky local storage parameters
    * They must all be strings as are accessed by key programmatically */
   dynamicParams: {[key: string]: string | undefined} &
      {
         theme: string;
         pinBanner?: string;
         profileId?: string;
      };
   propertyDetail: {
      defaults: {
      photoLinkHoverText: string;
   }
   …

A particular point of note was that the intersection type using “&” was needed for this to work, rather than the union type “|”.

This solved the problem satisfactorily and all worked fine.

Comments Off on Dynamic Typing issues with Typescript/Angular 11

May 12th, 2020
9:35 am
Template Strings in configuration data – IE11 issues

Posted under Angular & TypeScript & Web

I was leaning towards using .js files rather than strict JSON for configuration data (and therefore loading them dynamically via a script tag). This allows some additional intelligence in the files that you could not get with JSON.

One point of interest was to use template strings via backticks to allow parameter substitution, as I have detailed in this post here.

The problem with this approach is that IE11 will not support it and cannot therefore load the .js file. Whilst this could be worked around by using typescript and transpiling, this is clearly not ideal and not the focus for a configuration file – I wanted it to be plain edited text.

As in the target use case I did not need placeholder substitution, my workaround was to use ordinary single quoted strings with continuation appended to each line as ” \”. This worked fine in IE11.

This does mean, however, that for substition and other advanced tricks, you would need to take the other approaches noted in the above post if you wanted to support IE11.

Comments Off on Template Strings in configuration data – IE11 issues

May 8th, 2020
7:40 am
Dynamic String Interpolation in Typescript/Angular

Posted under Angular & TypeScript & Web

There does not appear to be a truly dynamic string interpolator available, such that the string template with placeholders can be just a string obtained from anywhere, and then used as a template in the way that e.g. java does with String.format() and MessageFormat.format().

There are 2 ways around this:

1/ Using a lambda allows delayed evaluation of the standard string interpolator as you can pass parameters to the lambda.

If the configuration data containing the templates etc. is a .js file rather than pure JSON (in the way that I do my configuration), it is possible to actually store the lambdas in the configuration data in ‘JSON-like’ object literals. Obviously care is needed with error handling etc. when loading the .js file. This trick is detailed hereThis is a nice approach in that you can use all the standard string interpolation tricks, and also others such as ternary expressions etc., in your configuration data, making it as intelligent as you need to. The downer is that it is no longer pure JSON, but it can be loaded dynamically from a .js file. Another issue with this approach is that it is not supported by IE11, and so cannot be used if this is a factor, as per my other post on this here.

2/ Roll your own template interpolator. This approach is detailed here.

This allows your configuration/template data to be stored as pure JSON. However the downside is that you are rolling your own solution. As a comment points out in the above post, it is perhaps surprising that between Typescript and Angular there is no out-of-the-box solution for this. Whilst the built-in interpolation can be made flexible as detailed in 1/, the approach taken by Java using e.g. String.format() and MessageFormat.format(), whereby built in libraries provide the support at run time, does give more flexibility in this regard. Whilst other templating libraries might be used, such as Handlebars or Mustache, it is undesirable to load one of them as well in an Angular app as Angular has its own templating (albeit with the limitations discussed).

 

Comments Off on Dynamic String Interpolation in Typescript/Angular

June 6th, 2017
5:58 pm
Angular/Ionic–externalising application configuration

Posted under Angular & Ionic & TypeScript
Tags , , , ,

Key Goals

  1. The primary goal is to completely decouple configuration data from the build deployment. This is a key requirement in an enterprise setting, not least because it is vital that the very same build can be tested and signed off in a number of different development and test environments pointing at different server infrastructure.  If a different build was needed for each environment, there is always the risk of an incorrect build, so technically the final production build has never been tested and could break. A good discussion on this may be found on The Twelve Factor App site here.
  2. Upon investigating a pattern for this in Ionic and Angular, I found numerous posts citing different ways around the problem, some appearing quite complex. I was left feeling that this need was not something that had been baked in to the Framework design as a first class citizen – it was certainly not an up front and clear pattern in the documentation. Therefore I wanted a reusable pattern to use as a standard for all my Angular development.
  3. I am using angular-cli with webpack, which is the recommended approach. This produces simple static deployment bundles which are web server agnostic – just an html file which loads one or more js bundles, plus assets such as fonts and images. The pattern for externalising configuration aims to fit in with this and makes no assumptions about the ability to build configuration data dynamically server-side or inject configuration related information into the web server. For example, a common pattern when using Apache Tomcat with Java applications is to inject a configuration location as a JVM parameter into tomcat, which is used later server-side to access the configuration dynamically. We make no assumptions here about the ability to do this kind of trick – any such mechanisms (discussed further below) would be an extension to the pattern for a particular server.
  4. An important goal of the pattern is that it should be testable, i.e. we use dependency injection to inject configuration into the classes that use it, (in our case via a service). This allows unit tests to inject test configuration as required.
  5. The pattern also supports offline use e.g. in an Ionic application. If, as some solutions advocate, the configuration was loaded via an HTTP service in javascript code, this would not work offline with Ionic. The challenge that remains is how and where the configuration would be located in an Ionic deployment, but that is a question for another day and further invesigation.

 

Solution Overview

  1. After a fair amount of investigation I settled on a fairly simple pattern as per this post here. This stackoverflow comment refers to the same technique.  I adapted the technique for my situation – the key points are as follows, and a code sample is shown below.
  2. The basic idea is to load configuration data as a Json object literal from a .js file. This is done by a <script> tag in index.html, prior to the loading of the webpack bundles, which loads the configuration typically as a file from a known location, where the src of the <script> tag is deemed not part of the build.
  3. Whilst this would typically be a location on the same server such as the root directory where index.html is located, it does not have to be, and could equally well be loaded from another server over http. The key point is that the precise src for the script tag is a contract between index.html and any deployment/configuration management software. For example, the configuration file might be dynamically created by Ansible or similar, or the configuration might not be a file at all, and could be dynamically created server side via a web service. These possibilities are all extensions to the basic pattern and depend on the particular web servers and deployment techniques in play.
  4. When running in development mode using ng serve, the bundles are created in memory and no files exist. Therefore this mechanism will not work as the <script> tag will not load anything. We therefore provide a means of having default configuration in the code which will be overridden by the above configuration file if present. If the configuration file is not present (ng serve in dev mode) you will get a 404 for the script tag on the console, which can be ignored.
  5. When running in development mode using files (e.g. via lite-server using  ng build –watch and running lite-server from the dist subdirectory), a config file can be used in the normal way to override the default config if desired.
  6. The default configuration object is exported as a constant from the appropriate environment file, e.g. environment.ts. Whilst there could be default settings in environment.prod.ts, the default is typically set to null in this case so that a configuration file is always used.
  7. The structure of the configuration data is typed via an AppConfig interface which is used everywhere the data is loaded or referenced. In simple cases the configuration data may be just properties of the root AppConfig object. However in more complex cases the object graph could be extensive.
  8. The configuration file loads the configuration object into a global variable which is then read by the AppConfigService, and if present overrides the default configuration. Object.freeze() is used (along with readonly properties on the AppConfig interface) to make the configuration data immutable. Whilst in theory it may be possible to inject the resulting configuration as a constant using a value provider, when I tried this (with the latest Angular 4) I hit a variety of intermittent compile/build errors citing ERROR in Error encountered resolving symbol values statically. In the end I went with an injected configuration service, which consuming classes set up as a getter to allow convenient access.
  9. The AppConfigService loads the global configuration variable via a casted reference to the window object. I tried to get this to work via a declare to reference it as an external object but again I hit compile errors when doing so.

 

Example Code Listings

 

index.html

<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <title>Places Admin</title>

<base href=”/”>

<meta name=”viewport” content=”width=device-width, initial-scale=1″>
<link rel=”icon” type=”image/x-icon” href=”favicon.ico”>

<script type=”text/javascript” src=”appConfig.js”></script>

</head>
<body>
<app-root>Loading…</app-root>
</body>
</html>

appConfig.js – external configuration file.

// Application Configuration
var salientSoft_appConfig = {
  appTitle: "The Places Guide",
  apiBase: "http://localhost:5984/places-guide/",
  messageTimeout: number = 3000
};

environment.ts

// The file contents for the current environment will overwrite these during build.
// The build system defaults to the dev environment which uses `environment.ts`, but if you do
// `ng build --env=prod` then `environment.prod.ts` will be used instead.
// The list of which env maps to which file can be found in `.angular-cli.json`.

import {AppConfig} from ‘../config/app-config’;

export const environment = {
production: false,
};

export const defaultAppConfig: AppConfig = {
appTitle: ‘The Places Guide’,
apiBase: ‘http://localhost:5984/places-guide/’,
messageTimeout: 3000
};

environment.prod.ts

import {AppConfig} from '../config/app-config';

export const environment = {
production: true
};

export const defaultAppConfig: AppConfig = null;

appConfig.ts – config file interface

/* Root application configuration object
   This encapsulates the global config json properties.
   These must match what is in the actual config file, normally /appConfig.js.
 */
export interface AppConfig {
    readonly appTitle: string;
    readonly apiBase: string;
    readonly messageTimeout: number;
}

app-config.service.ts

import {Injectable} from '@angular/core';
import {AppConfig} from './app-config';
import {defaultAppConfig} from '../environments/environment';

@Injectable()
export class AppConfigService {
private _appConfig: AppConfig;
buildConfig(): AppConfig {
const appConfig: AppConfig = (<any>window).salientSoft_appConfig || defaultAppConfig;
return Object.freeze(appConfig);
}
constructor() {
this._appConfig = this.buildConfig();
}
get(): AppConfig {
return this._appConfig;
}
}

app.component.ts – example configuration consumer

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {

constructor(
private appConfigService: AppConfigService
) {}

public get appConfig(): AppConfig {return this.appConfigService.get(); }

ngOnInit(): void {
document.title = this.appConfig.appTitle;
}

No Comments »

May 18th, 2017
11:22 am
Webstorm IDE–Oragnising Imports Tips/Gotchas

Posted under TypeScript & WebStorm
Tags ,

1/ Organising imports does not seem to happen automatically when it should. To organise imports for a given folder or the project, select the appropriate level in the navigator then hit Code/Optimise Imports or ctrl+alt+0 as detailed here.

2/ TSLint gives errors about double quotes in imports as it prefers single quotes in all the code (double quotes for html), but Webstorm uses double quotes by default when organising imports. You can change Webstorm to use single quotes as per this post here.

No Comments »

May 15th, 2017
4:07 pm
Angular–using parent component references

Posted under Angular & TypeScript
Tags ,

I had a need to do this initially when implementing my own tab container component.

I wanted each tab to hold a parent reference to the container so that it could get the container’s active tab, to see if ‘it’ was the active tab.

There is significant discussion about this online. The parent can be injected directly via its class name, but this introduces tight coupling which is undesirable.

Another option discussed was the use of @Host in conjunction with injection, as per the angular docs here. In this case, the traversal of the injector  tree stops at the parent otherwise another compatible component could conceivably be injected from elsewhere.

My solution initially was to define parent and child interfaces which defined the interaction between them, as one would in Java. This decoupled them from each other. However, in Angular you cannot inject interfaces directly as they are not available at runtime. As I was already iterating the content children (the tabs) in the parent container, in the ngAfterContentInit() lifecycle hook (as this is the earliest point at which the content children are available), it was straightforward to manually assign the parent reference to each child via a method in the child interface. In this way the parent and children were decoupled – any parent supporting the container interface (used by the child) could host any children which used the child interface (used by the parent).

In the end I dumped the idea as it ended up a tad unwieldy – I only needed the child to detect if it was the active one and it was having to call a parent method to compare itself with the currently active tab to see if it was the one. This child element property was then used to ensure that only the content of the active tab was actually rendered. I finally just added a boolean active property on the child interface, and in the parent, when switching tabs, I cleared the active flag on the old active tab (if there was one) and set the active flag on the new active tab, as well as assigning a parent property for the currently active tab. Whilst this meant I was having to track the active flags correctly, I no longer had to handle parent references and parent method calls. This seemed less messy but it was a close call and either pattern in reality would be ok.

One point of note is that in Typescript, an interface can contain properties (as in fields in Java) as well as methods, so for example my interface for the child could contain active and default properties directly rather than having to additionally define isActive() and isDefault() methods.

No Comments »

May 1st, 2017
4:03 pm
Typescript duck typing error when casting a reference.

Posted under Angular & TypeScript
Tags , , ,

The following code fragment in a dao gave an error when casting the response json. The cause is not clear, but 2 code variations were tried:-

//API calls
getFeatures(): Observable<FeaturesSearchResult> {
return this.http.get(this.queryFeatures())
      // *** This version originally failed citing that property total_rows did not exist
.map(response => {return <FeaturesSearchResult>(response.json())});
}

I then tried using the “as” operator for the cast instead of the <> syntax. This worked. Strangely, right at the end I managed to get an example with the”<>” syntax working too, but the reason and differences were not at all clear.
However, I noted that the latter is also the preferred syntax now, as unlike the “<>” syntax it is also compatible with JSX/.tsx syntax (JSX is an embeddable React xml-like syntax which transforms into JavaScript)
See here for details on JSX/.tsx, and on the React wikipedia entry. The working code fragment follows:-

//API calls
getFeatures(): Observable<FeaturesSearchResult> {
return this.http.get(this.queryFeatures())// *** This version originally failed citing that property total_rows did not exist
      // *** This version worked by using the “as …” syntax for casting rather than <>
.map(response => {return response.json() as FeaturesSearchResult});
}

The full listing of the code for the working version (less imports for brevity) follows:-

features-dao.ts
@Injectable()
export class FeaturesDao {
constructor(private appConfig: AppConfig,
private http: Http) {}

//API calls
getFeatures(): Observable<FeaturesSearchResult> {
return this.http.get(this.queryFeatures())
.map(response => {return response.json() as FeaturesSearchResult});
}

//API query definitions
queryFeatures(): string {
return `${this.appConfig.apiBase}_design/places/_view/featuresByOrdinal?include_docs=true`;
}

}
export interface FeaturesSearchResult extends CouchDBResult<FeaturesSearchResultRow> {}
export interface FeaturesSearchResultRow extends CouchDBResultRow<number[], any, FeatureDoc> {}
couchdb-result.ts
export interface CouchDBResult<R extends CouchDBResultRow<any,any,any>> {
total_rows: number;
offset: number;
rows: R[];
}

export interface CouchDBResultRow<K,V,D> {
id: string;
key: K;
value: V;
doc?: D;
}
feature-doc.ts
export interface FeatureDoc extends CouchDBDoc {
ordinal: number;
name: string;
description: string;
searchable: boolean;
rateable: boolean;
}

No Comments »

April 22nd, 2017
3:54 pm
Typescript typing for unknown/arbitrary types– any vs Object vs {}?

Posted under TypeScript
Tags ,

I was looking into this in order to find the best type for an arbitrary stream of Json.

This comment post here has an interesting discussion on this, and concludes that any is the most flexible, and there aren’t strong reasons for using the Object or {} (which are similar, as {} extends Object).

This post describes how to use TypeScript interfaces to describe the Json.

http://json2ts.com is a site that automatically creates the interfaces from the json for you, however in my case it did nothing when I gave it a returned CouchDB Json stream.

This stackoverflow post also discusses the issue and mentions json2ts.com – the poster evidently had more luck with it than I did.

No Comments »