Using JSON in Angular

bob.ts - Aug 9 '21 - - Dev Community

Being able to import and process JSON files provides a whole host of configuration options, as well as providing a means of capturing data locally for development purposes.

When I started using this pattern, I was looking for a means to provide basic configuration (i.e. Feature Flags). The process quickly evolved into ...

  • Authorization
  • Configuration (Feature Flags)
  • Language
  • Host Names
  • Core Structures (think titles matching data keys)

But first, I needed to be able to read these files.

Importing and Reading JSON files

In the tsconfig.json file I added the "resolveJsonModule": true configuration option under the compilerOptions key:

{
  "compileOnSave": false,
  "compilerOptions": {
    ...
    "resolveJsonModule": true,
    ...
}
Enter fullscreen mode Exit fullscreen mode

Additionally, I learned that I can read a JSON file that is in the assets folder via an http.get request. This provided a whole host of options for development purposes.

Now, let's look at some code.

Authorization

Basically, I built several keys and associated information for ...

  • localhost
  • Development
  • Stage
  • Production

The JSON file looks something like this ...

{
  "localhost": {
    "issuer": "https://identity ....",
    "clientId": "..."
  },
  "application.apps.cf.np.gc ...": {
    "issuer": "https://identity ...",
    "clientId": "..."
  },
  "application-stg.apps.cf.gc ...": {
    "issuer": "https://identity ...",
    "clientId": "..."
  },
  "application-ui.apps.cf.gc ...": {
    "issuer": "https://identity ...",
    "clientId": "..."
  }
}
Enter fullscreen mode Exit fullscreen mode

Additionally, I added code to look at the base URL and determine which of these "keys" to use.

In the app-routing.module.ts, the code added looks like this ...

import authnames from '@core/constants/auth.json';

const hostnameService = new HostnameService();
const hostname = hostnameService.get(window);

if (authnames[hostname] === undefined) {
  console.error('Issue with auth.json, hostname: ', hostname);
}

const authConfig = {
  issuer: authnames[hostname].issuer,
  clientId: authnames[hostname].clientId,
  ...
};
Enter fullscreen mode Exit fullscreen mode

Configuration

Using the following in a config.json file ...

{
  "features": {
    "enableKonami": true,
    "enableTheme": true,
    ...
    "useLocalJSON": false
  }
}
Enter fullscreen mode Exit fullscreen mode

The file can then be imported like this ...

import config from '@core/constants/config.json';
Enter fullscreen mode Exit fullscreen mode

And used like this ...

if (config.features.useLocalJSON === true) {
  ...
}
Enter fullscreen mode Exit fullscreen mode

config in this sense can be included in a component, as such, to be used in HTML.

@Component({
  ...
})
export class CardFunctionalityComponent implements OnInit {
  ...
  features: any = config.features;
  ...
}
Enter fullscreen mode Exit fullscreen mode

Language

Using the following in a language.json file allows for quick switching between languages, and puts all strings in one location for easy management ...

{
  "english": {
    "config": "Configuration",
    "header": "Header",
    ...
  }
}
Enter fullscreen mode Exit fullscreen mode

The file can then be imported like this ...

import language from '@core/constants/language.json';
Enter fullscreen mode Exit fullscreen mode

And used like this ...

@Component({
  ...
})
export class CardFunctionalityComponent implements OnInit {
  ...
  selectedLanguage: string = 'english';
  language: any = language[selectedLanguage];
  ...
}
Enter fullscreen mode Exit fullscreen mode

Host Names

getURL = (key: string): string => {
  if (this.useLocalJSON) {
    return hostnames.localJSON.BASE + hostnames.localJSON[key];
  } else {
    let hostname: string = this.hostnameService.get(window);
    return hostnames.HOSTNAMES[hostname] + hostnames.ROUTES[key];
  }
};
Enter fullscreen mode Exit fullscreen mode

The hostnameService.get does something like this ...

get = (_window) => _window?.location?.hostname;
Enter fullscreen mode Exit fullscreen mode

I did this simply to make the code more testable.

Core Structures

Core structures are a but harder to define.

I use these to define things like:

  • Headers for table columns that match the expected data.
  • Order of the columns.
  • Visibility of individual columns (allowing them to be displayed or not).

Let's look at some code to see where this is going.

{
  "ORDER": [
    "selected",
    "id",
    "lastname",
    "firstname"
    ...
  ],

  "SORTING": {
    "id": "number",
    "lastname": "string",
    "firstname": "string",
    ...
  },

  "COLUMNS": [
    { "id": "selected", "name": "Selected", "isActive": false },
    { "id": "id", "name": "Event Id", "isActive": true },
    { "id": "lastname", "name": "Last", "isActive": true },
    { "id": "firstname", "name": "First", "isActive": true },
    ...
  ],

  "DEFAULT": {
    "defaultSortParams": [ "lastname", "firstname" ],
    "defaultSortDirs": [ "asc", "asc" ]
  }
}
Enter fullscreen mode Exit fullscreen mode

Now, looking at this it is clear that if we have data coming in such as ...

[
  { "selected": false, "id": "1", "lastname": "Fornal", "firstname": "Bob" },
  ...
]
Enter fullscreen mode Exit fullscreen mode

We can ...

  1. Set the order when displayed on a table
  2. Enable proper sorting of the data
  3. Display proper column names
  4. Decide whether to display the column or not
  5. Enable default sorting options

This is a lot of functionality that can be packed into a structure file.

Summary

Being able to import JSON files can make development easier, more efficient, and overall a better experience when used correctly.

Being able to import and process JSON files can make development easier, more efficient, and overall a better experience when used correctly. It provides a whole host of configuration options, as well as providing a means of capturing data locally for development purposes.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .