Angular dynamic forms created with JSON data - json

Dear reader. I am trying to make dynamic forms with Json data that has been red. The dynamic form is based on the example of Angular as seen here: https://angular.io/guide/dynamic-form
The edits I made are that I read data from an external Json file and try to load those instead of the hardcoded one in the file 'question.service.ts' as seen in the link.
This is how my Json file looks like:
{
"formInfo": {
"name": "test"
},
"fields": [
{
"controlType": "textbox",
"key": "firstName",
"label": "Voornaam",
"required": true,
"value": "Mark",
"order": 1
},
{
"controlType": "textbox",
"key": "surName",
"label": "Achternaam",
"required": true,
"order": 2
},
{
"controlType": "textbox",
"key": "emailAddress",
"label": "Email",
"required": false,
"order": 3
},
{
"controlType": "dropdown",
"key": "brave",
"label": "Beoordeling",
"required": "",
"order": 4,
"options": {
"solid": "Solid",
"great": "Great",
"good": "Good",
"unproven": "Unproven"
}
}
]
}
And my function to retrieve the data and return as observable (in question.service.ts) looks like:
getQuestions2() : Observable<QuestionBase<any>[]> {
let questions: QuestionBase<any>[] = [];
const exampleObservable = new Observable<QuestionBase<any>[]>((observer) =>
{
let url = "../assets/exampleData.json"
this.http.get(url).subscribe((data) => {
for (let x of data['fields']){
if (x.controlType == "textbox"){
let textboxItem = new TextboxQuestion({
key: x.key,
label: x.label,
value: x.value,
order: x.order
})
questions.push(textboxItem);
}
else if (x.controlType == "dropdown"){
let dropDownItem = new DropdownQuestion({
key: x.key,
label: x.label,
value: x.value,
options: x.options,
order: x.order
})
questions.push(dropDownItem);
}
}
})
observer.next(questions.sort((a, b) => a.order - b.order));
})
return exampleObservable;
}
and the code that connects the service with the frontend looks like this:
export class AppComponent implements OnInit {
questions: any[];
constructor(private service: QuestionService) {
this.getaSyncData();
//this.questions = this.service.getQuestions();
//console.log(this.questions);
}
getaSyncData(){
this.service.getQuestions2()
.subscribe((data) => this.questions = data);
console.log(this.questions);
}

I solved this finally for those who will have similar issues in the future
I was not able to load forms into the html even though I was correctly reading the data out of the JSON file and printing it in the console. I added a *ngIf in the div where you load in your data. In the example of Angular.io its in the template on App.component.html. Yes, it was this simple.

Related

How to refer sibling element in JSON Javascript?

I have a json object for chart like below:
{
"results": [
{
"dataSets": {
"One": {
"label": "testLabel",
"labels": "test",
"data": [
"10",
"58"
]
}
},
"chart": [
{
"key": "test",
"label": "chart-1",
"chartType": "bar",
"order": "1",
"dataSets": [
{
"style": "line",
"key": "One"
},
]
}
]
}
]
}
I want to get dataSets values like label, labels, data of “one” in chart’s dataSets by providing “one” as key.
Is it possible to do in javascript or vue?
Yes, it is possible. But you will need to make a series of Array.map() to achieve this.
const results = [{
dataSets: {
One: {
label: "testLabel",
labels: "test",
data: ["10", "58"]
}
},
chart: [{
key: "test",
label: "chart-1",
chartType: "bar",
order: "1",
dataSets: [{
style: "line",
key: "One"
}]
}]
}];
const modifiedResult = results.map(result => {
const outerDataSets = result.dataSets;
result.chart = result.chart.map(chart =>
chart.dataSets.map(innerDataSet => ({
...innerDataSet,
...outerDataSets[innerDataSet.key]
}))
);
return result;
});
console.log(modifiedResult);
Also if you are working with Vue, I think its best to put the modification of result on the computed so it will always try to add those dataSets additional data to the chart's dataSets.
Here a sample demo for implementation in Vue.

Access nested JSON in React table

I want to display nested JSON data in a react-table.
I tried it like this:
render() {
const columns = [{
//Not Displaying
Header: 'Owner ID',
id: 'ownerid',
accessor: '_links.customer.href.ownerid', // <- i think this is wrong
Cell: this.renderEditable
},
{
//Displaying
Header: 'Price',
accessor: 'price',
Cell: this.renderEditable
}, {
The data i am getting back and have bound to the table is structured as follows:
[
{
"id": 1,
"date": "20.07.2019",
"price": 3.2,
"customer": {
"ownerid": 1,
"firstname": "John",
"lastname": "Johnson"
}
}
]
Here i am using the columns array:
import ReactTable from "react-table";
<ReactTable data={this.state.offers} columns={columns}
filterable={true} pageSize={10}/>
Binding the data:
fetchOffers = () => {
const token = sessionStorage.getItem("jwt");
fetch(SERVER_URL + 'api/offers',
{
headers : {'Authorization':token}
})
.then((response) => response.json())
.then((responsteData) => {
this.setState({
offers: responsteData._embedded.offers,
});
console.log(this.state);
})
.catch(err=> console.error(err));
}
The data i am using after binding:
Check the Accessors documentation. It has several examples for complex data structure.
I don't see _links or href in your sample data. So I think that you need just:
accessor: 'customer.ownerid'
The data structure from the console screenshot doesn't match your sample data. And it doesn't seem to contain ownerid. Try accessor: '_links.customer.href' to check whether it outputs anything to the table.
I figured it out.
I called the endpoint "localhost:8080/api/offers" and saved the following response:
"offers": [
{
"date": "20.07.2019",
"price": 3.2,
"_links": {
"self": {
"href": "http://localhost:8080/api/offers/1"
},
"offer": {
"href": "http://localhost:8080/api/offers/1"
},
"customer": {
"href": "http://localhost:8080/api/offers/1/customer"
}
}
}
]
there is no customer object
But when i call "localhost:8080/offers" i get:
[
{
"id": 1,
"date": "20.07.2019",
"price": 3.2,
"customer": {
"ownerid": 1,
"firstname": "John",
"lastname": "Johnson"
}
}
]
i changed the URI in my project and now the number is displaying.
I still don't know why i get data from "../api/offers" but i will research.
I had to access a nested object and display it with some styling, and this ended up working for me:
(Note: I was using typescript, so some of the typing might not be necessary)
{
Header: 'Thing To Display',
accessor: 'nested.thing.to.display',
Cell: ({ row }: { row: Row }) => (
<p>{row.values['nested.thing.to.display']}</p>
),
}

Using JSON API Serializer to create more complicated JSON

The examples here don't go nearly far enough in explaining how to produce a more complicated structure...
If I want to end up with something like:
{
"data": {
"type": "mobile_screens",
"id": "1",
"attributes": {
"title": "Watch"
},
"relationships": {
"mobile_screen_components": {
"data": [
{
"id": "1_1",
"type": "mobile_screen_components"
},
{
"id": "1_2",
"type": "mobile_screen_components"
},
...
]
}
}
},
"included": [
{
"id": "1_1",
"type": "mobile_screen_components",
"attributes": {
"title": "Featured Playlist",
"display_type": "shelf"
},
"relationships": {
"playlist": {
"data": {
"id": "938973798001",
"type": "playlists"
}
}
}
},
{
"id": "938973798001",
"type": "playlists",
"relationships": {
"videos": {
"data": [
{
"id": "5536725488001",
"type": "videos"
},
{
"id": "5535943875001",
"type": "videos"
}
]
}
}
},
{
"id": "5536725488001",
"type": "videos",
"attributes": {
"duration": 78321,
"live_stream": false,
"thumbnail": {
"width": 1280,
"url":
"http://xxx.jpg?pubId=694940094001",
"height": 720
},
"last_published_date": "2017-08-09T18:26:04.899Z",
"streams": [
{
"url":
"http://xxx.m3u8",
"mime_type": "MP4"
}
],
"last_modified_date": "2017-08-09T18:26:27.621Z",
"description": "xxx",
"fn__media_tags": [
"weather",
"personality"
],
"created_date": "2017-08-09T18:23:16.830Z",
"title": "NOAA predicts most active hurricane season since 2010",
"fn__tve_authentication_required": false
}
},
...,
]
}
what is the most simple data structure and serializer I can set up?
I get stumped after something like:
const mobile_screen_components = responses.map((currentValue, index) => {
id[`id_${index}`];
});
const dataSet = {
id: 1,
title: 'Watch',
mobile_screen_components,
};
const ScreenSerializer = new JSONAPISerializer('mobile_screens', {
attributes: ['title', 'mobile_screen_components'],
mobile_screen_components: {
ref: 'id',
}
});
Which only gives me:
{
"data": {
"type": "mobile_screens",
"id": "1",
"attributes": { "title": "Watch" },
"relationships": {
"mobile-screen-components": {
"data": [
{ "type": "mobile_screen_components", "id": "1_0" },
{ "type": "mobile_screen_components", "id": "1_1" },
{ "type": "mobile_screen_components", "id": "1_2" },
{ "type": "mobile_screen_components", "id": "1_3" },
{ "type": "mobile_screen_components", "id": "1_4" },
{ "type": "mobile_screen_components", "id": "1_5" }
]
}
}
}
}
I have no idea how to get the "included" sibling to "data." etc.
So, the question is:
what is the most simple data structure and serializer I can set up?
Below is the simplest object that can be converted to JSON similar to JSON in the question using jsonapi-serializer:
let dataSet = {
id: '1',
title: 'Watch',
mobile_screen_components: [
{
id: '1_1',
title: 'Featured Playlists',
display_type: 'shelf',
playlists: {
id: 938973798001,
videos: [
{
id: 5536725488001,
duration: 78321,
live_stream: false
},
{
id: 5535943875001,
duration: 52621,
live_stream: true
}
]
}
}
]
};
To serialize this object to JSON API, I used the following code:
let json = new JSONAPISerializer('mobile_screen', {
attributes: ['id', 'title', 'mobile_screen_components'],
mobile_screen_components: {
ref: 'id',
attributes: ['id', 'title', 'display_type', 'playlists'],
playlists: {
ref: 'id',
attributes: ['id', 'videos'],
videos: {
ref: 'id',
attributes: ['id', 'duration', 'live_stream']
}
}
}
}).serialize(dataSet);
console.log(JSON.stringify(json, null, 2));
The first parameter of JSONAPISerializer constructor is the resource type.
The second parameter is the serialization options.
Each level of the options equals to the level of the nested object in serialized object.
ref - if present, it's considered as a relationships.
attributes - an array of attributes to show.
Introduction
First of all we have to understand the JSON API document data structure
[0.1] Refering to the top level (object root keys) :
A document MUST contain at least one of the following top-level
members:
data: the document’s “primary data”
errors: an array of error objects
meta: a meta object that contains non-standard meta-information.
A document MAY contain any of these top-level members:
jsonapi: an object describing the server’s implementation
links: a links object related to the primary data.
included: an array of resource objects that are related to the primary data and/or each other (“included resources”).
[0.2]
The document’s “primary data” is a representation of the resource or
collection of resources targeted by a request.
Primary data MUST be either:
a single resource identifier object, or
null, for requests that target single resources
an array of resource identifier
objects, or an empty array ([]), for reqs. that target
collections
Example
The following primary data is a single resource object:
{
"data": {
"type": "articles",
"id": "1",
"attributes": {
// ... this article's attributes
},
"relationships": {
// ... this article's relationships
}
}
}
In the (jsonapi-serializer) documentation : Available serialization option (opts argument)
So in order to add the included (top-level member) I performed the following test :
var JsonApiSerializer = require('jsonapi-serializer').Serializer;
const DATASET = {
id:23,title:'Lifestyle',slug:'lifestyle',
subcategories: [
{description:'Practices for becoming 31337.',id:1337,title:'Elite'},
{description:'Practices for health.',id:69,title:'Vitality'}
]
}
const TEMPLATE = {
topLevelLinks:{self:'http://example.com'},
dataLinks:{self:function(collection){return 'http://example.com/'+collection.id}},
attributes:['title','slug','subcategories'],
subcategories:{ref:'id',attributes:['id','title','description']}
}
let SERIALIZER = new JsonApiSerializer('pratices', DATASET, TEMPLATE)
console.log(SERIALIZER)
With the following output :
{ links: { self: 'http://example.com' },
included:
[ { type: 'subcategories', id: '1337', attributes: [Object] },
{ type: 'subcategories', id: '69', attributes: [Object] } ],
data:
{ type: 'pratices',
id: '23',
links: { self: 'http://example.com/23' },
attributes: { title: 'Lifestyle', slug: 'lifestyle' },
relationships: { subcategories: [Object] } } }
As you may observe, the included is correctly populated.
NOTE : If you need more help with your dataSet, edit your question with the original data.

JSON response Object with changing keys/values

I have tried looking on many sites and browsed through many posts here, but still can't find what I am looking for, or at least could not implement it work. I have an API response where depending on the request parameters it either returns an object with an array of objects (which I am able to deal with), or an object with several objects that contain arrays within them. I was able to get the data from the simple form, but the multi-object containing object is kicking my butt. I am also doing this in Angular 4, just in case that makes a difference. The response is from the holiday api.
Below is the full response with no filtering params, minus a few objects, to not beat a dead horse.
{ "status": 200, "holidays": {
"2016-01-01": [
{
"name": "Durin's Day",
"date": "2016-01-01",
"observed": "2016-01-01",
"public": true
}
],
"2016-02-23": [
{
"name": "Founder's Day",
"date": "2016-02-23",
"observed": "2016-02-23",
"public": true
}
],
"2016-02-29": [
{
"name": "Leap Day",
"date": "2016-02-29",
"observed": "2016-02-29",
"public": false
}
],
"2016-03-20": [
{
"name": "Weasel Stomping Day",
"date": "2016-03-20",
"observed": "2016-03-20",
"public": false
}
],
"2016-04-05": [
{
"name": "First Contact Day",
"date": "2016-04-05",
"observed": "2016-04-05",
"public": false
}
],
"2016-04-06": [
{
"name": "Second Contact Day",
"date": "2016-04-06",
"observed": "2016-04-06",
"public": false
}
],
"2016-05-10": [
{
"name": "Whacking Day",
"date": "2016-05-10",
"observed": "2016-05-10",
"public": false
}
],
"2016-10-31": [
{
"name": "Harry Potter Day",
"date": "2016-10-31",
"observed": "2016-10-31",
"public": false
}
],
"2016-11-24": [
{
"name": "Hogswatch",
"date": "2016-11-24",
"observed": "2016-11-24",
"public": false
}
],
"2016-12-23": [
{
"name": "Festivus",
"date": "2016-12-23",
"observed": "2016-12-23",
"public": true
}
],
"2016-12-25": [
{
"name": "Decemberween",
"date": "2016-12-25",
"observed": "2016-12-25",
"public": false
},
{
"name": "Winter Veil",
"date": "2016-12-25",
"observed": "2016-12-26",
"public": true
}
]
} }
Here is the code used:
import { Component, OnInit, Input } from '#angular/core';
import { HolidayService } from '../holiday.service';
#Component({
selector: 'app-holiday',
templateUrl: './holiday.component.html',
styleUrls: ['./holiday.component.css']
})
export class HolidayComponent implements OnInit {
constructor(private _holiday: HolidayService) {
}
holidaysObj: any;
holidayArr: Array<{key: string, value: string}>;
ngOnInit() {
}
holidayParams(country,month){
this._holiday.getHolidays(country.value,month.value)
.subscribe(responseDa
ta => {
this.holidaysObj = responseData;
console.log(responseData);
});
this.convertObj(this.holidaysObj);
}
convertObj(obj : any){
for(const prop in obj){
if(obj.hasOwnProperty(prop)){
this.holidayArr.push(obj[prop]);
}
}
}
}
It works just fine when the response is called with filtering params, like 'month' and returns something like this:
{
"status": 200,
"holidays": [
{
"name": "Festivus",
"date": "2016-12-23",
"observed": "2016-12-23",
"public": true
},
{
"name": "Decemberween",
"date": "2016-12-25",
"observed": "2016-12-25",
"public": false
},
{
"name": "Winter Veil",
"date": "2016-12-25",
"observed": "2016-12-26",
"public": true
}
]
}
You can use a custom pipe to iterate your Objects, you could also extract the data from holidays from your response like:
.map(res => res.json().holidays)
but here I won't do it.
So let's create the custom pipe:
#Pipe({
name: 'keys'
})
export class KeysPipe implements PipeTransform {
transform(value: any, args?: any[]): any[] {
// check there is value to iterate
if(value) {
// create instance vars to store keys and final output
let keyArr: any[] = Object.keys(value),
dataArr = [];
// loop through the object,
// pushing values to the return array
keyArr.forEach((key: any) => {
dataArr.push(value[key]);
});
// return the resulting array
return dataArr;
}
}
}
and then you can use it in the template like:
<div *ngFor="let d of data?.holidays | keys">
<div *ngFor="let a of d">
{{a.name}}
{{a.date}}
<!-- rest of the properties -->
</div>
</div>
Here's a
Demo
UPDATE:
Alternatively, if you want to make your data to the same format as the other data you are receiving, you can manipulate the response. Like you mentioned, you need an if else statement first to check in which format the data is. In case the data is in the format like presented in question, you can do the following to reach the desired result:
.subscribe(data => {
// add statuscode
this.data = {status:data.status,holidays:[]}
let keyArr: any[] = Object.keys(data.holidays);
keyArr.forEach((key: any) => {
// push values of each holiday
this.data.holidays.push(data.holidays[key][0]);
});
})
Demo

Validate Json Schema

I'm getting an error when using json-schema-validator API v4.
I try to do :
final JsonValidator validator = new JsonValidator(JsonLoader.fromPath("schema.json"));
ValidationReport report = validator.validate(data);
but every time I get an error : # [schema]: unknown keyword contacts
schema.json :
{
"contacts": {
"description": "The list of contacts",
"type": "array",
"optional": true,
"items": {
"description": "A contact",
"type": "object",
"properties": {
"givenName": {
"description": "Person's first name",
"type": "string",
"maxLength": 64,
"optional": true
},
"familyName": {
"description": "A person's last name",
"type": "string",
"maxLength": 64,
"optional": true
}
}
}
}
}
Regards
As far as I can intuit, your data looks like this-> json_data={"contacts":array}. If this is true, basically your outermost thing is an object (basically the full json object itself), for which you "might" need to define the schema starting from the "top level root" of your json as->
schema.json:
{
"description": "the outer json",
"type": "object",
"properties": {
"contacts": {
"description": "The list of contacts",
"type": "array",
"optional": true,
"items": {
"description": "A contact",
"type": "object",
"properties": {
"givenName": {
etc.....
Forgive me for rough indentations. Also, I have not tested this, please see if it works, if it does not, I would suggest you to provide your json_data (example at least) and the API's examples so that one can try to locate where what is wrong.
Use AVJ. Instead of having your data validation and sanitization logic written as lengthy code, you can declare the requirements to your data with concise, easy to read and cross-platform JSON Schema or JSON Type Definition specifications and validate the data as soon as it arrives to your application.
// validationSchema.js
import Ajv from "ajv";
import addFormats from "ajv-formats";
import ajvErrors from "ajv-errors";
const schemas = {
newUser: {
{
type: "object",
properties: {
lastName: {
type: "string",
minLength: 1,
maxLength: 255
},
firstName: {
type: "string",
minLength: 1,
maxLength: 255
},
description: {
type: "string"
},
birthday: {
type: "string",
format: "date-time"
},
status: {
type: "string",
enum: ["ACTIVE", "DELETED"]
},
},
required: ["name"]
}
}
};
const ajv = new Ajv({ allErrors: true });
addFormats(ajv);
ajvErrors(ajv /*, {singleError: true} */);
const mapErrors = (errorsEntry = []) => {
const errors = errorsEntry.reduce(
(
acc,
{ instancePath = "", message = "", params: { missingProperty = "" } = {} }
) => {
const key = (instancePath || missingProperty).replace("/", "");
if (!acc[key]) {
acc[key] = [];
}
acc[key].push(`${key} ${message}`);
return acc;
},
[]
);
return errors;
};
const validate = (schemaName, data) => {
const v = ajv.compile(schemas[schemaName]);
let valid = false,
errors = [];
valid = v(data);
if (!valid) {
errors = mapErrors(v.errors);
}
return { valid, errors };
};
export default { validate };
You can validate it like this:
import validationSchema from "your_path/validationSchema.js"
const user = {
firstName: "",
lastName: "",
....
};
const { valid, errors = [] } = validationSchema.validate("newUser", user);
if(valid){
console.log("Data is valid!");
} else {
console.log("Data is not valid!");
console.log(errors);
}