How to create multiple pages from single json files in Gatsby - json

I am new to node.js and react but I love gatsby.js. I have followed all the tutorials that I can find and it's such a great tool.
However one of the main reasons why I want to use it is I have a file json with 1000 different records in and I would like to generate a new page for each record.
I believe I have come to the conclusion that I need to learn more about the gatsby-node.js file and am aware of the following resource but are there any tutorials or other examples on this topic that maybe a little easier to follow:
https://www.gatsbyjs.org/docs/creating-and-modifying-pages/#creating-pages-in-gatsby-nodejs

The example you were referring to should already give you a good idea. The basic concept is to import the JSON file, loop over it and run createPage for each of the items in your JSON source. So given an example source file like:
pages.json
[{
"page": "name-1"
}, {
"page": "name-2"
}]
You can then use the Node API to create pages for each:
gatsby-node.js
const path = require('path');
const data = require('./pages.json');
exports.createPages = ({ boundActionCreators }) => {
const { createPage } = boundActionCreators;
// Your component that should be rendered for every item in JSON.
const template = path.resolve(`src/template.js`);
// Create pages for each JSON entry.
data.forEach(({ page }) => {
const path = page;
createPage({
path,
component: template,
// Send additional data to page from JSON (or query inside template)
context: {
path
}
});
});
};

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;

Add information to List in Angular

I'm learning Angular 6 and I have a List shown on my site. Now, i need to give Users of my site the possibility to add entries to that list. There's a form with 4 fields and a submit button, when Submit is clicked, the values should be stored anywhere and all the entries should be shown on the site, permanently, not just in the active session.
How can i achieve this? Do i need to include some sort of database? Or is it possible to append the new dataset to a JSON file?
Thank you in advance
EDIT: This is a training project and will only be available through the Intranet of the Company i work at, so security concerns about missing Captchas or similar things are not a factor
If you are going to use this project for long time and if number of entries is higher and you have alot of users, then you should use some data base. And if there is limited number of users and you need this app temporary then using json file is also good. Using json file will save you from database logics etc if you are not familiar with them
To SAVE some data anywhere you HAVE TO use some kind of database.
Angular is JavaScript framework. It helps to write applications. But it does nothing with server side (except, of course, CLI and other stuff which NodeJS people likes to do).
JSON is not the only way to communicate between browser and the server. But in Angular it's easiest way.
You'll need something on the server (I suppose PHP script) which will receives data from your Angular app and will send back some feedback. In the case with PHP you'd learn how to receive JSON POST ($_POST and $_REQUEST will not work)
What I advise you in terms "how to learn Angular" is go to this step-by-step tutorial https://angular.io/tutorial
Run it twice or three times and you'll understand how works Promises, Observables, communications, templates, services and all other stuff.
It is possible to append the data to the new dataset to the JSON file create a service to read that JSON file using that service so to give you the basics of reading that JSON file
Config.service.ts
#Injectable()
export class ConfigService {
private static _config: any = {}
constructor(private _http: Http) { }
load() {
return new Promise((resolve, reject) => {
this._http.get('../assets/' + 'data.json')
.map(res => res.json())
.subscribe((data) => {
console.log("inside http get of the new service");
console.log(data);
ConfigService._config = data;
resolve(true);
},
(error: any) => {
console.error(error);
return Observable.throw(error.json().error || 'Server error');
});
});
}
// Gets a value of specified property in the configuration file
get(key: any) {
console.log("tell me the base :" + ConfigService._config['BASE_URL']);
return ConfigService._config[key];
}
}
export function ConfigFactory(config: ConfigService) {
return () => config.load();
}
export function init() {
return {
provide: APP_INITIALIZER,
useFactory: ConfigFactory,
deps: [ConfigService],
multi: true
}
}
const ConfigModule = {
init: init
}
export { ConfigModule };
add these lines in your main module
app.module.ts
import { CommonModule } from '#angular/common';
import { ConfigModule, ConfigService } from './config-service';
providers:[
ConfigService,
ConfigModule.init(),
]
Then, you can inject this service on any component or service that wants the data
Also, you have to add an assets folder under your app folder and place the data.json there.

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);
});

Loading JSON Without an HTTP Request

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.

Vue js external data object

I'm just starting out and have gone through the Vue guide. I've got some basic grasp on imports and exports of ES6 but wanted to know the ideal way of doing this.
I'll have several components that need an original single source of data that i'll need to individually manipulate from there. Ideally I want that data to be in a separate file so that others can manipulate it.
I can figure out to do this via jquery ( seen below ) but I don't really need to make a call as the json file will be internal:
module.exports = {
data: function () {
return {
msg: 'hello',
whatever : 'hi'
}
},
created : function() {
this.fetchData();
},
methods : {
fetchData : function() {
console.log("WORKING");
var self = this;
$.get( apiURL, function( data ) {
self.items = data;
console.log(data);
});
}
}
}
But I also don't want to have all the data be in the App.vue file. I need it somewhere else and then need it to replace my data.
Is the best way to do this to create another Vue file with no template or styling and just create it's own module.exports data object? Say mydata.vue:
module.exports = {
data: function () {
_mydata = {
items : [
{case:'caseone'},
{case:'casetwo'}
],
otheritems : [
{case:'caseone'},
{case:'casetwo'}
]
}
}
}
And then somehow replacing this data object in mydata.vue with the data object in app.vue with imports ( main. js ) ?
import Vue from 'vue'
import App from './App.vue'
import Data from './SiteData.vue'
Just wanted to check if this was the ideal way/i'm on the right path...or if there is an easier way to have a central data object in a different file for all my components?
What I have done is to manage my data in a json file. I think that the approach of use separate files for initial data is cool for small apps. Bigger apps need something more usefull like Vuex.
I don't think it is a good idea to manage a .vue file, as those files are meant to be handled by some module budled system, with the correspondind vue transformation, not the case of the data object.
My approach was this: I had a data.json file
data.json
{
"component1": {
"someData": "someValue",
...
},
...
"componentN": {
"someOtherData": "someOtherValue"
}
}
And then I import that data in the corresponding component
componentN.vue
<template>
</template>
<script>
import { componentN } from './data.json'
export default {
data () {
return componentN
}
}
</script>
Note that:
I used a single file for manage data, however you can split it in a file for every component, for example.
As you can see, this approach can become a mess with medium apps, I don't want to even imagin it in big apps, so be careful.