Loading JSON Without an HTTP Request - json

I am working on a project using Angular 4, NPM, Node.js, and the Angular CLI.
I have a rather unusual need to load JSON into an Angular service (using an #Injectable) without an HTTP request, i.e. it will always be loaded locally as part of the package, and not retrieved from a server.
Everything I've found so far indicates that you either have to modify the project's typings.d.ts file or use an HTTP request to retrieve it from the /assets folder or similar, neither of which is an option for me.
What I am trying to accomplish is this. Given the following directory structure:
/app
/services
/my-service
/my.service.ts
/myJson.json
I need the my.service.ts service, which is using #Injectable, to load the JSON file myJson.json. For my particular case, there will be multiple JSON files sitting next to the my.service.ts file that will all need to be loaded.
To clarify, the following approaches will not work for me:
Using an HTTP Service to Load JSON File From Assets
URL: https://stackoverflow.com/a/43759870/1096637
Excerpt:
// Get users from the API
return this.http.get('assets/ordersummary.json')//, options)
.map((response: Response) => {
console.log("mock data" + response.json());
return response.json();
}
)
.catch(this.handleError);
Modifying typings.d.ts To Allow Loading JSON Files
URL: https://hackernoon.com/import-json-into-typescript-8d465beded79
Excerpt:
Solution: Using Wildcard Module Name
In TypeScript version 2 +, we can use wildcard character in module name. In your TS definition file, e.g. typings.d.ts, you can add this line:
declare module "*.json" {
const value: any;
export default value;
}
Then, your code will work like charm!
// TypeScript
// app.ts
import * as data from './example.json';
const word = (<any>data).name;
console.log(word); // output 'testing'
The Question
Does anyone else have any ideas for getting these files loaded into my service without the need for either of these approaches?

You will get an error if you call json directly, but a simple workaround is to declare typings for all json files.
typings.d.ts
declare module "*.json" {
const value: any;
export default value;
}
comp.ts
import * as data from './data.json';

The solution I found to this was using RequireJS, which was available to me via the Angular CLI framework.
I had to declare require as a variable globally:
declare var require: any;
And then I could use require.context to get all of the files in a folder I created to hold on the types at ../types.
Please find below the entire completed service that loads all of the JSON files (each of which is a type) into the service variable types.
The result is an object of types, where the key for the type is the file name, and the related value is the JSON from the file.
Example Result loading files type1.json, type2.json, and type3.json from the folder ../types:
{
type1: {
class: "myClass1",
property1: "myProperty1"
},
type2: {
class: "myClass2",
property1: "myProperty2"
},
type3: {
class: "myClass3",
property1: "myProperty3"
}
}
The Final Service File
import { Injectable } from '#angular/core';
declare var require: any;
#Injectable()
export class TypeService {
constructor(){
this.init()
};
types: any;
init: Function = () => {
// Get all of the types of branding available in the types folder
this.types = (context => {
// Get the keys from the context returned by require
let keys = context.keys();
// Get the values from the context using the keys
let values = keys.map(context);
// Reduce the keys array to create the types object
return keys.reduce(
(types, key, i) => {
// Update the key name by removing "./" from the begining and ".json" from the end.
key = key.replace(/^\.\/([^\.]+)\.json/, (a, b)=> { return b; });
// Set the object to the types array using the new key and the value at the current index
types[key] = values[i].data;
// Return the new types array
return types;
}, {}
);
})(require.context('../types', true, /.json/));
}
}

You can directly access variables in services from their object that is defined in the constructor.
...So say your constructor loads the service like this
constructor(private someService:SomeService){}
You can just do someService.theJsonObject to access it.
Just be careful not to do this before it gets loaded by the service function that loads it. You'd then get a null value.
You can assign variables to your service files the same way you do in component files.
Just declare them in the service
public JsonObject:any;
And (easiest way) is to let the function that called your service assign the JSON object for you.
So say you called the service like this
this.serviceObject.function().subscribe
(
resp =>
{
this.serviceObject.JsonObject = resp;
}
);
After this is done once, other components can access that JSON content using someService.theJsonObject as discussed earlier.
In your case I think all you need to do is embed your JSON object in your code. Maybe you can use const. That's not bad code or anything.

Related

NextJs Webpack asset/source returns JSON as a string

Looking for some help to understand what is going on here.
The Problem
We are using a translation service that requires creating JSON resource files of copy, and within these resource files, we need to add some specific keys that the service understands so it knows what should and should not be translated.
To do this as simple as possible I want to import JSON files into my code without them being tree shaken and minified. I just need the plain JSON file included in my bundle as a JSON object.
The Solution - or so I thought
The developers at the translation service have instructed me to create a webpack rule with a type of assets/source to prevent tree shaking and modification.
This almost works but the strange thing is that the JSON gets added to the bundle as a string like so
module.exports = "{\n \"sl_translate\": \"sl_all\",\n \"title\": \"Page Title\",\n \"subtitle\": \"Page Subtitle\"\n}\n";
This of course means that when I try and reference the JSON values in my JSX it fails.
Test Repo
https://github.com/lukehillonline/nextjs-json-demo
NextJs 12
Webpack 5
SSR
Steps To Reproduce
Download the test repo and install packages
Run yarn build and wait for it to complete
Open /.next/server/pages/index.js to see the SSR page
On line 62 you'll find the JSON object as a string
Open .next/static/chunks/pages/index-{HASH}.js to see the Client Side page
If you format the code you'll find the JSON object as a string on line 39
Help!
If anyone can help me understand what is going wrong or how I can improve the webpack rule to return a JSON object rather than a string that would be a massive help.
Cheers!
The Code
next.config.js
module.exports = {
trailingSlash: true,
productionBrowserSourceMaps: true,
webpack: function (config) {
config.module.rules.push({
test: /\.content.json$/,
type: "asset/source",
});
return config;
},
};
Title.content.json
{
"sl_translate": "sl_all",
"title": "Page Title",
"subtitle": "Page Subtitle"
}
Title.jsx
import content from "./Title.content.json";
export function Title() {
return <h1>{content.title}</h1>;
}
pages/index.js
import { Title } from "../components/Title/Title";
function Home({ dummytext }) {
return (
<div>
<Title />
<p>{dummytext}</p>
</div>
);
}
export const getServerSideProps = async () => {
const dummytext = "So we can activate SSR";
return {
props: {
dummytext,
},
};
};
export default Home;

Reading from a non-static JSON file in an Angular4 app

I have a basic Angular web app which reads from a JSON file located on the same server as the app and parses through the JSON file in order to set certain values on objects which drive certain behavior in my app (applies css classes, etc.)
I am not able to find online and/or figure out myself how to set up the controller to read from the JSON file in a way that allows the file to be changed and Angular to dynamically reload the file once it has been changed without reloading the entire page. The JSON file is local on the server where the app is deployed, and I wanted to avoid standing up a web service just to serve a file that already exists on the same server the app is deployed.
Here is what I am doing now:
ngOnInit(): void {
// Make the HTTP request:
this.http.get('../assets/applicationLogs.json').subscribe(data => {
// Read the result field from the JSON response.
this.node_a_status= data.nodes[0].status;
this.node_b_status= data.nodes[1].status;
this.node_c_status= data.nodes[2].status;
});
}
And here is a what my JSON file looks like:
{
"nodes":[
{ "node":"Node A", "status":"processing", "errors":null },
{ "node":"Node B", "status":"processing", "errors":null },
{ "node":"Node C", "status":"inactive", "errors":null }
]
}
First, I know I will probably need to move this get logic out of ngOnInit(), but I am a little lost on how I should go about achieving the desired behavior I have described with typescript.
You're using an http request method on the file so "Poll it"... same way you would any other http JSON service. Here's a ready made poller for you to import: https://www.npmjs.com/package/rx-polling
Best thing you can do is create a service out of it and call it in ngOnInit method and use the response the same way you've shown.
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/dom/ajax';
import 'rxjs/add/operator/map';
import polling from 'rx-polling';
// Example of an Observable which requests some JSON data
const request$ = Observable.ajax({
url: '../assets/applicationLogs.json',
crossDomain: true
}).map(response => response.response || [])
.map(response => response.slice(0, 10)); // Take only first 10 comments
polling(request$, { interval: 5000 }).subscribe((comments) => {
console.log(comments);
}, (error) => {
// The Observable will throw if it's not able to recover after N attempts
// By default it will attempts 9 times with exponential delay between each other.
console.error(error);
});

vuejs: the correct path of local json file for axios get request

In my Vue project, I have mocked some data for next step development. I already save the test data in a json file. And my vue project is typical one created with Vue-Cli, and the structure for my project goes as following:
My_project
build
config
data
service_general_info.json
node_modules
src
components
component-A
component-A.vue
as you can see, all the folders are created by the vue-cli originally. And I make a new folder data and place the test data json file inside.
And I want to read in the data by axios library in an event handling function inside the component of component-A as following:
methods: {
addData() {
console.log('add json data...');
axios.get('./../../data/service_general_info.json');
},
},
I use relative path to locate the target file.But get 404 error back. So how to set the path correctly? Currently I am running the dev mode in local host.
The error message is: GET http://localhost:8080/data/service_general_info.json 404 (Not Found)
In Vue-cli project, axios can't get data from custom folder.
You should use static folder to save test json file.
So you should change axios call like this:
axios.get('/static/service_general_info.json');
This will get data from json.
If you are doing just for sake of testing then you can save it in public folder and access it directly on http root.
e.g. I have the file results.json in public folder then I can access it using http://localhost:8080/results.json
For me it didn't work using static folder. I had to put it in public folder.
I put json folder in public & then accessed it like below.
getCountries() {
return axios.get('json/country-by-abbreviation.json', { baseURL: window.location.origin })
.then((response) => { return response.data; })
.catch((error) => {
throw error.response.data;
});
}
When the http call is made from the server, axios has no idea that you're on http://localhost:8080, you have to give the full url.
Like this:
methods: {
addData() {
console.log('add json data...');
axios.get('http://localhost:8080/data/service_general_info.json');
},
},
I had this same issue, only the above solutions wouldn't work as it is being uploaded to a subdirectory. I found I needed to put it in the public/assets folder and use:
axios.get(process.env.BASE_URL+'assets/file.json')
While in vue.config.js I have set the local and live paths
module.exports = {
publicPath: process.env.NODE_ENV === 'production'
? '/path/to/app/'
: '/'
}
You can simply read a static JSON file using import. Then assign in data.
import ServiceInfo from './../../data/service_general_info.json';
export default{
data(){
return {
ServiceInfo
}
}
}

Get json file for karma unit test

I want to get a JSON file in my unit test because I need to have it for my tests but I dont know how I can include the file
I run my test with karma and Jasmine. And my project is created with Angular 2.
The name of my JSON file is 'www/assets/mocks/emptyCalendarData.JSON'.
Does someone know how I can include a JSON file into a spec file?
Thanks
UPDATE
I tried to use HTTP get but then I got a system
let calendarData: Calendar;
http.get('www/assets/mocks/emptyCalendarData.json')
.map(res => res.json())
.subscribe(
data => calendarData = data,
err => console.log(JSON.stringify(err))
);
Then I get this error:
ERROR: Error{stack: null, originalErr: TypeError{stack: 'mergeOptions
get
eval code
eval#[native code]
__exec#http://localhost:9876/base/node_modules/systemjs/dist/system.src.js?18a094f61af9f2ec0577ca3a337760d97719b624:1482:16
execute#http://localhost:9876/base/node_modules/systemjs/dist/system.src.js?18a094f61af9f2ec0577ca3a337760d97719b624:3896:22
linkDynamicModule#http://localhost:9876/base/node_modules/systemjs/dist/system.src.js?18a094f61af9f2ec0577ca3a337760d97719b624:3222:36
link#http://localhost:9876/base/node_modules/systemjs/dist/system.src.js?18a094f61af9f2ec0577ca3a337760d97719b624:3065:28
execute#http://localhost:9876/base/node_modules/systemjs/dist/system.src.js?18a094f61af9f2ec0577ca3a337760d97719b624:3402:17
doDynamicExecute#http://localhost:9876/base/node_modules/systemjs/dist/system.src.js?18a094f61af9f2ec0577ca3a337760d97719b624:796:32
link#http://localhost:9876/base/node_modules/systemjs/dist/system.src.js?18a094f61af9f2ec0577ca3a337760d97719b624:998:36
doLink#http://localhost:9876/base/node_modules/systemjs/dist/system.src.js?18a094f61af9f2ec0577ca3a337760d97719b624:650:11
updateLinkSetOnLoad#http://localhost:9876/base/node_modules/systemjs/dist/system.src.js?18a094f61af9f2ec0577ca3a337760d97719b624:698:24
http://localhost:9876/base/node_modules/systemjs/dist/system.src.js?18a094f61af9f2ec0577ca3a337760d97719b624:510:30
invoke#http://localhost:9876/base/node_modules/zone.js/dist/zone.js?b9a84410301a475a439d6b7b4e7eff0954f5b925:364:34
run#http://localhost:9876/base/node_modules/zone.js/dist/zone.js?b9a84410301a475a439d6b7b4e7eff0954f5b925:257:50
http://localhost:9876/base/node_modules/zone.js/dist/zone.js?b9a84410301a475a439d6b7b4e7eff0954f5b925:609:61
invokeTask#http://localhost:9876/base/node_modules/zone.js/dist/zone.js?b9a84410301a475a439d6b7b4e7eff0954f5b925:397:43
runTask#http://localhost:9876/base/node_modules/zone.js/dist/zone.js?b9a84410301a475a439d6b7b4e7eff0954f5b925:294:58
drainMicroTaskQueue#http://localhost:9876/base/node_modules/zone.js/dist/zone.js?b9a84410301a475a439d6b7b4e7eff0954f5b925:515:43
invoke#http://localhost:9876/base/node_modules/zone.js/dist/zone.js?b9a84410301a475a439d6b7b4e7eff0954f5b925:467:41
http://localhost:9876/base/node_modules/zone.js/dist/zone.js?b9a84410301a475a439d6b7b4e7eff0954f5b925:92:33
invokeTask#http://localhost:9876/base/node_modules/zone.js/dist/zone.js?b9a84410301a475a439d6b7b4e7eff0954f5b925:397:43
runTask#http://localhost:9876/base/node_modules/zone.js/dist/zone.js?b9a84410301a475a439d6b7b4e7eff0954f5b925:294:58
invoke#http://localhost:9876/base/node_modules/zone.js/dist/zone.js?b9a84410301a475a439d6b7b4e7eff0954f5b925:464:41', line: 38}, line: 821, sourceURL: 'http://localhost:9876/base/node_modules/systemjs/dist/system.src.js?18a094f61af9f2ec0577ca3a337760d97719b624'}
There are many ways to do that:
import json if it is inside of your app: import * as json from './test'; //will import test.json
download file using Http and do map(res=>res.json())
for webpack use json-loader plugin: var json = require('./my.json')
for gulp/grunt etc. you could write code generator
I stash my Mock JSON data into a ts file like a variable. i.e
export var getMockResponseJSON = { 'key': value };
Later when I want to reach this variable, I simply import it like:
import {getMockResponseJSON} from "../JSONs";
and use it in my class
expect(getMockResponseJSON.key).not.toEqual(1);
You could leverage the Http class of Angular2 within your tests to do that.
Here is a sample within a beforeAll function:
beforeAll((done) => {
let injector = Injector.resolveAndCreate([ HTTP_PROVIDERS ]);
let http = injector.get(Http);
http.get('app/data.json').subscribe(
(data) => {
this.data = data.json();
done();
}
);
});
See this plunkr: https://plnkr.co/edit/k6jxHf?p=preview.

Angular 2 - how to fill a local variable with JSON data from an external file

The Angular 2 tutorials I have read place variables directly in the app.component.ts file. For example var BAR below, which pulls data though the {Foo} interface.
import {Component} from 'angular2/core';
import {Foo} from './foo';
#Component({
etc.
});
export class AppComponent {
bar = BAR;
}
var BAR: Foo[] = [
{ 'id': 1 },
{ 'id': 2 }
];
However, I have the data for BAR in a local JSON file. I don't believe {HTTP_PROVIDER} is necessary. How would I go about getting the JSON data from the external file?
Create a file with this content
export const BAR= [
{ 'id': 1 },
{ 'id': 2 }
];
save it as BarConfig.ts or some like
later use it as follows
import { BAR } from './BarConfig';
let bar= BAR;
or even better, use BAR directly where you needed
HTTP_PROVIDER is needed if you want to load a file using http.
Here is an example of how to load a local json file over http:
this.result = {friends:[]};
this.http.get('./friends.json').map((res: Response) => res.json()).subscribe(res => this.result = res);
More details here: http://www.syntaxsuccess.com/viewarticle/angular-2.0-and-http
Juste put your .json file in your static folder (/assets if you are using the angular cli) and it should work.
Best option is to create json file and store json details inside file and call that file using like below.
If you using angular-cli Keep the json file inside Assets folder (parallel to app dir) directory
return this.http.get('<json file path inside assets folder>.json'))
.map((response: Response) => {
console.log("mock data" + response.json());
return response.json();
}
)
.catch(this.handleError);
}
Note: here you only need to give path inside assets folder like assets/json/oldjson.json then you need to write path like /json/oldjson.json
If you using webpack then you need to follow above same structure inside public folder its similar like assets folder.
You can use the Http provider in angular2
Make sure you place your local json file in the www folder.
getLocalFile(){
return this.http.get('./localFileName.json').
map(res => res.json());
}
This will return you the local JSON file.