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,
...
}
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": "..."
}
}
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,
...
};
Configuration
Using the following in a config.json
file ...
{
"features": {
"enableKonami": true,
"enableTheme": true,
...
"useLocalJSON": false
}
}
The file can then be imported like this ...
import config from '@core/constants/config.json';
And used like this ...
if (config.features.useLocalJSON === true) {
...
}
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;
...
}
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",
...
}
}
The file can then be imported like this ...
import language from '@core/constants/language.json';
And used like this ...
@Component({
...
})
export class CardFunctionalityComponent implements OnInit {
...
selectedLanguage: string = 'english';
language: any = language[selectedLanguage];
...
}
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];
}
};
The hostnameService.get
does something like this ...
get = (_window) => _window?.location?.hostname;
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" ]
}
}
Now, looking at this it is clear that if we have data coming in such as ...
[
{ "selected": false, "id": "1", "lastname": "Fornal", "firstname": "Bob" },
...
]
We can ...
- Set the order when displayed on a table
- Enable proper sorting of the data
- Display proper column names
- Decide whether to display the column or not
- 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.