Skip to content

Commit

Permalink
Roughed in some basic layout logic
Browse files Browse the repository at this point in the history
  • Loading branch information
jscharett committed Jun 6, 2019
1 parent 747fbf0 commit 20f79c5
Show file tree
Hide file tree
Showing 5 changed files with 160 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[ {
"key": "comment",
"type": "textarea"
} ]
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,7 @@ export class JsonSchemaFormComponent implements OnChanges, OnInit {
}

private initializeLayout() {
// TODO
return this;
this.layoutService.layout = cloneDeep(this.layout);
}

private initializeSchema(): void {
Expand Down
14 changes: 14 additions & 0 deletions projects/ngx-json-schema-form/src/lib/layout-item.data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export interface LayoutItem {
id: string;
options: any;
$ref?: any;
// arrayItem?;
// arrayItemType?;
// dataPointer?;
// dataType?;
items?: Array<any>;
key?: string;
// name?;
// recursiveReference?;
type?: string;
}
22 changes: 21 additions & 1 deletion projects/ngx-json-schema-form/src/lib/layout.service.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { TestBed } from '@angular/core/testing';
import { inject, TestBed } from '@angular/core/testing';

import basicJSONLayout from '../assests/example-layouts/jsf-layout-basic.json';

import { LayoutService } from './layout.service';

Expand All @@ -15,4 +17,22 @@ describe('LayoutService', () => {
const service: LayoutService = TestBed.get(LayoutService);
expect(service).toBeTruthy();
});

it('should augment layout with derived props', inject([LayoutService], (service: LayoutService) => {
service.layout = basicJSONLayout;
expect(service.layout).toEqual([{
id: jasmine.any(String),
key: basicJSONLayout[0].key,
options: {},
type: basicJSONLayout[0].type
}]);
}));

it('should remove unknown props from layout', inject([LayoutService], (service: LayoutService) => {
service.layout = [{cat: 1}];
expect(service.layout).toEqual([{
id: jasmine.any(String),
options: {}
}]);
}));
});
121 changes: 120 additions & 1 deletion projects/ngx-json-schema-form/src/lib/layout.service.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,131 @@
import { Injectable } from '@angular/core';

import { clone, pick, uniqueId } from 'lodash';

import { LayoutItem } from './layout-item.data';

@Injectable()
export class LayoutService {
private _layout: Array<any> = [];
set layout(value: Array<any>) {
this._layout = value;
this._layout = LayoutService.buildLayout(value);
}
get layout(): Array<any> {
return this._layout;
}

private static buildLayout(layout: Array<LayoutItem>): Array<LayoutItem> {
// let hasSubmitButton = !JsonPointer.get(jsf, '/formOptions/addSubmit');
const formLayout: Array<LayoutItem> = LayoutService.mapLayout(layout, (layoutItem, index, layoutPointer) => {
const newNode: LayoutItem = {
id: uniqueId(),
options: {},
...pick(layoutItem, ['key', 'type'])
};
// Dropped code to push invalid props into options
// Dropped code to convert widget to type
// Dropped code to convert options.legend to options.title

return newNode;
// if (isObject(layoutItem)) {

// if (!hasOwn(newNode.options, 'validationMessages')) {
// if (hasOwn(newNode.options, 'errorMessages')) {
// newNode.options.validationMessages = newNode.options.errorMessages;
// delete newNode.options.errorMessages;

// // Convert Angular Schema Form (AngularJS) 'validationMessage' to
// // Angular JSON Schema Form 'validationMessages'
// // TV4 codes from https://github.com/geraintluff/tv4/blob/master/source/api.js
// } else if (hasOwn(newNode.options, 'validationMessage')) {
// if (typeof newNode.options.validationMessage === 'string') {
// newNode.options.validationMessages = newNode.options.validationMessage;
// } else {
// newNode.options.validationMessages = {};
// Object.keys(newNode.options.validationMessage).forEach(key => {
// const code = key + '';
// const newKey =
// code === '0' ? 'type' :
// code === '1' ? 'enum' :
// code === '100' ? 'multipleOf' :
// code === '101' ? 'minimum' :
// code === '102' ? 'exclusiveMinimum' :
// code === '103' ? 'maximum' :
// code === '104' ? 'exclusiveMaximum' :
// code === '200' ? 'minLength' :
// code === '201' ? 'maxLength' :
// code === '202' ? 'pattern' :
// code === '300' ? 'minProperties' :
// code === '301' ? 'maxProperties' :
// code === '302' ? 'required' :
// code === '304' ? 'dependencies' :
// code === '400' ? 'minItems' :
// code === '401' ? 'maxItems' :
// code === '402' ? 'uniqueItems' :
// code === '500' ? 'format' : code + '';
// newNode.options.validationMessages[newKey] = newNode.options.validationMessage[key];
// });
// }
// delete newNode.options.validationMessage;
// }
// }
// } else if (JsonPointer.isJsonPointer(layoutItem)) {
// newNode.dataPointer = layoutItem;
// } else if (isString(layoutItem)) {
// newNode.key = layoutItem;
// } else {
// console.error('buildLayout error: Form layout element not recognized:');
// console.error(layoutItem);
// return null;
// }
});

return formLayout;
}

/**
* 'mapLayout' function
*
* Creates a new layout by running each element in an existing layout through
* an iteratee. Recursively maps within array elements 'items' and 'tabs'.
* The iteratee is invoked with four arguments: (value, index, layout, path)
*
* The returned layout may be longer (or shorter) then the source layout.
*
* If an item from the source layout returns multiple items (as '*' usually will),
* this function will keep all returned items in-line with the surrounding items.
*
* If an item from the source layout causes an error and returns null, it is
* skipped without error, and the function will still return all non-null items.
*
* @param layout - the layout to map
* @param function - the funciton to invoke on each element
* @param layoutPointer - the layoutPointer to layout, inside rootLayout
* @param rootLayout - the root layout, which conatins layout
* @return the mapped layout
*/
private static mapLayout(layout: Array<LayoutItem>, fn: (v: any, i?: number, l?: any, p?: any) => LayoutItem,
layoutPointer: string|Array<string> = '', rootLayout: Array<LayoutItem> = layout): Array<LayoutItem> {
const indexPad = 0;

return layout.reduce((mappedLayout: Array<LayoutItem>, item: LayoutItem, index: number) => {
const realIndex: number = +index + indexPad;
const newLayoutPointer = `${layoutPointer}/${realIndex}`;
let newNode: LayoutItem = clone(item);
let newLayout: Array<LayoutItem> = mappedLayout;
// Note: removed logic to convert tabs to items and items to [items]
// if (item.items) {
// newNode.items = LayoutService.mapLayout(item.items, fn, `${newLayoutPointer}/items`, rootLayout);
// }
newNode = fn(newNode, realIndex, newLayoutPointer, rootLayout);
// if (isNil(newNode)) {
// indexPad -= 1;
// } else {
// if (Array.isArray(newNode)) { indexPad += newNode.length - 1; }
newLayout = mappedLayout.concat(newNode);
// }

return newLayout;
}, []);
}
}

0 comments on commit 20f79c5

Please sign in to comment.