diff --git a/.travis.yml b/.travis.yml
index 4e33fe0..91162e2 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -31,5 +31,6 @@ deploy:
skip_cleanup: true
script:
- npx semantic-release
+ - npm run publish:demo
on:
branch: master
diff --git a/README.md b/README.md
index edbcdd2..0ad7ba1 100644
--- a/README.md
+++ b/README.md
@@ -15,6 +15,13 @@ A [JSON Schema](http://json-schema.org) Form builder for Angular 7+, similar to
Note: This project attemtps to take over where its predecesor left off.
It's based off of the above project, but rewritten from the ground up.
+Note: This project is not ready for consumption. There is still a lot to do to
+bring parity with [Angular JSON Schema Form](https://github.com/angular2-json-schema-form)
+
+## Check out the live demo and play with the examples
+
+[Check out some examples here.](https://jscharett.github.io/ngx-json-schema-form/)
+
### To install from NPM
```shell
diff --git a/angular.json b/angular.json
index bc1063c..96a3b02 100644
--- a/angular.json
+++ b/angular.json
@@ -60,9 +60,11 @@
"projects/demo/src/assets"
],
"styles": [
- "projects/demo/src/styles.css"
+ "projects/demo/src/styles.scss"
],
- "scripts": []
+ "scripts": [
+ "node_modules/ace-builds/src-min/ace.js"
+ ]
},
"configurations": {
"production": {
@@ -116,7 +118,7 @@
"tsConfig": "projects/demo/tsconfig.spec.json",
"karmaConfig": "projects/demo/karma.conf.js",
"styles": [
- "projects/demo/src/styles.css"
+ "projects/demo/src/styles.scss"
],
"scripts": [],
"assets": [
@@ -172,5 +174,10 @@
}
}
},
- "defaultProject": "ngx-json-schema-form"
+ "defaultProject": "ngx-json-schema-form",
+ "schematics": {
+ "@schematics/angular:component": {
+ "styleext": "scss"
+ }
+ }
}
diff --git a/package-lock.json b/package-lock.json
index 21006d6..76936cd 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -276,6 +276,23 @@
"tslib": "1.9.3"
}
},
+ "@angular/cdk": {
+ "version": "7.3.7",
+ "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-7.3.7.tgz",
+ "integrity": "sha512-xbXxhHHKGkVuW6K7pzPmvpJXIwpl0ykBnvA2g+/7Sgy5Pd35wCC+UtHD9RYczDM/mkygNxMQtagyCErwFnDtQA==",
+ "requires": {
+ "parse5": "5.1.0",
+ "tslib": "1.9.3"
+ },
+ "dependencies": {
+ "parse5": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz",
+ "integrity": "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==",
+ "optional": true
+ }
+ }
+ },
"@angular/cli": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/@angular/cli/-/cli-7.2.3.tgz",
@@ -475,6 +492,14 @@
"tslib": "1.9.3"
}
},
+ "@angular/flex-layout": {
+ "version": "7.0.0-beta.24",
+ "resolved": "https://registry.npmjs.org/@angular/flex-layout/-/flex-layout-7.0.0-beta.24.tgz",
+ "integrity": "sha512-ll6sK0nLGxqI/f5+z4jbd+pve1QITzgehv2AuGvfSDgIjPMeqUDB5YZqQmIGM/dQRk/vIio5KCW5LQPJWzMMYQ==",
+ "requires": {
+ "tslib": "1.9.3"
+ }
+ },
"@angular/forms": {
"version": "7.2.2",
"resolved": "https://registry.npmjs.org/@angular/forms/-/forms-7.2.2.tgz",
@@ -483,12 +508,28 @@
"tslib": "1.9.3"
}
},
+ "@angular/http": {
+ "version": "7.2.15",
+ "resolved": "https://registry.npmjs.org/@angular/http/-/http-7.2.15.tgz",
+ "integrity": "sha512-TR7PEdmLWNIre3Zn8lvyb4lSrvPUJhKLystLnp4hBMcWsJqq5iK8S3bnlR4viZ9HMlf7bW7+Hm4SI6aB3tdUtw==",
+ "requires": {
+ "tslib": "1.9.3"
+ }
+ },
"@angular/language-service": {
"version": "7.2.2",
"resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-7.2.2.tgz",
"integrity": "sha512-qkyY5nUT3J/vBvTTZCTFy3isijQseBFGd6gM08o4ycwbTuOOnnC0XUFuv7o8eeu0jd32MGbaK0gikF+OQOCGNQ==",
"dev": true
},
+ "@angular/material": {
+ "version": "7.2.2",
+ "resolved": "https://registry.npmjs.org/@angular/material/-/material-7.2.2.tgz",
+ "integrity": "sha512-HTtDhK5XkDvP6GPg4WTTa0HbeeagTfVRooTfw0TA+IuTAMBhXt5h1yzuGpFyMap8/PUVZN1D04g2CLhBSzoDxg==",
+ "requires": {
+ "tslib": "1.9.3"
+ }
+ },
"@angular/platform-browser": {
"version": "7.2.2",
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-7.2.2.tgz",
@@ -1103,6 +1144,11 @@
}
}
},
+ "@types/ace": {
+ "version": "0.0.36",
+ "resolved": "https://registry.npmjs.org/@types/ace/-/ace-0.0.36.tgz",
+ "integrity": "sha512-VBsvNc48suYohki9BSSsRDxwM0RL9Rgi3/GimQ11UMxbbXRjvMSdTvch2aZXfZQ4cyq7PsoHeLvIpsOGXoKNmA=="
+ },
"@types/estree": {
"version": "0.0.39",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz",
@@ -1417,6 +1463,11 @@
"negotiator": "0.6.1"
}
},
+ "ace-builds": {
+ "version": "1.4.5",
+ "resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.4.5.tgz",
+ "integrity": "sha512-wotVzxv5YClvwOjiuXNyGm4j/CnKoFIoTnnXNmi1nTHjr7hXMMjQeytcnbFua4thaJ5vvpVEDv0utmjqsrp3Jw=="
+ },
"acorn": {
"version": "5.7.3",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz",
@@ -2282,6 +2333,11 @@
"widest-line": "2.0.1"
}
},
+ "brace": {
+ "version": "0.11.1",
+ "resolved": "https://registry.npmjs.org/brace/-/brace-0.11.1.tgz",
+ "integrity": "sha1-SJb8ydVE7vRfS7dmDbMg07N5/lg="
+ },
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -7348,6 +7404,15 @@
"integrity": "sha512-HU/YxV4i6GcmiH4duATwAbJQMlE0MsDIR5XmSVxURxKHn3aGAdbY1/ZJFmVRbKtnLwIxxMJD7gYaPsypcbYimg==",
"dev": true
},
+ "jasmine-marbles": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/jasmine-marbles/-/jasmine-marbles-0.6.0.tgz",
+ "integrity": "sha512-1uzgjEesEeCb+r+v46qn5x326TiGqk5SUZa+A3O+XnMCjG/pGcUOhL9Xsg5L7gLC6RFHyWGTkB5fei4rcvIOiQ==",
+ "dev": true,
+ "requires": {
+ "lodash": "4.17.11"
+ }
+ },
"jasmine-spec-reporter": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/jasmine-spec-reporter/-/jasmine-spec-reporter-4.2.1.tgz",
@@ -8692,6 +8757,15 @@
}
}
},
+ "ng2-ace-editor": {
+ "version": "0.3.9",
+ "resolved": "https://registry.npmjs.org/ng2-ace-editor/-/ng2-ace-editor-0.3.9.tgz",
+ "integrity": "sha512-e8Q4YCirlL/OEiekewmzupG+zV3prYsiYmQnRzQzd0wNgsPjOLOdb0it7cCbzFfIXKGyIIHKTW5584WxPr2LnQ==",
+ "requires": {
+ "ace-builds": "1.4.5",
+ "brace": "0.11.1"
+ }
+ },
"nice-try": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
diff --git a/package.json b/package.json
index 62fbf83..10cc772 100644
--- a/package.json
+++ b/package.json
@@ -4,9 +4,15 @@
"scripts": {
"ng": "ng",
"start": "ng serve",
- "build": "ng build",
+ "build": "npm run build:lib && npm run build:demo",
+ "build:lib": "ng build ngx-json-schema-form",
+ "build:demo": "ng build demo --prod --base-href=https://jscharett.github.io/ngx-json-schema-form/",
"test": "ng test",
- "test-ci": "ng test --code-coverage --watch=false",
+ "test:lib": "ng test ngx-json-schema",
+ "test:lib-ci": "ng test ngx-json-schema-form --code-coverage --watch=false",
+ "test:demo": "ng test demo",
+ "test:demo-ci": "ng test demo --code-coverage --watch=false",
+ "test-ci": "npm run test:lib-ci && npm run test:demo-ci",
"lint": "ng lint",
"e2e": "ng e2e",
"copy-license": "copy .\\LICENSE .\\dist\\ngx-json-schema-form",
@@ -17,22 +23,30 @@
"preversion": "npm run lint && npm run test-ci",
"version": "npm run build && git add -A dist",
"postversion": "git push && git push --tags && rm -rf build/temp",
- "semantic-release": "semantic-release"
+ "semantic-release": "semantic-release",
+ "publish:demo": "npx angular-cli-ghpages --dir=./dist/demo"
},
"private": false,
"dependencies": {
"@angular/animations": "~7.2.0",
+ "@angular/cdk": "^7.2.0",
"@angular/common": "~7.2.0",
"@angular/compiler": "~7.2.0",
"@angular/core": "~7.2.0",
+ "@angular/flex-layout": "7.0.0-beta.24",
"@angular/forms": "~7.2.0",
+ "@angular/http": "~7.2.0",
+ "@angular/material": "~7.2.0",
"@angular/platform-browser": "~7.2.0",
"@angular/platform-browser-dynamic": "~7.2.0",
"@angular/router": "~7.2.0",
+ "@types/ace": "0.0.36",
"@types/json-schema": "^7.0.3",
"ajv": "^6.10.0",
+ "brace": "^0.11.0",
"core-js": "^2.5.4",
"lodash": "^4.17.11",
+ "ng2-ace-editor": "0.3.9",
"rxjs": "~6.5.2",
"tslib": "^1.9.0",
"zone.js": "~0.9.1"
@@ -48,6 +62,7 @@
"@types/node": "~8.9.4",
"codelyzer": "~5.1.0",
"jasmine-core": "~3.4.0",
+ "jasmine-marbles": "^0.6.0",
"jasmine-spec-reporter": "~4.2.1",
"karma": "~4.1.0",
"karma-chrome-launcher": "~2.2.0",
diff --git a/projects/demo/src/app/app.component.html b/projects/demo/src/app/app.component.html
index 5226d57..8fbd53b 100644
--- a/projects/demo/src/app/app.component.html
+++ b/projects/demo/src/app/app.component.html
@@ -1,20 +1,217 @@
-
-
-
- Welcome to {{ title }}!
-
-
+
+
+
+
+
+
+ Input JSON Schema and Form Layout
+
+ (loading form specification...)
+
+
+
+
+
+
+
+ Generated Form
+
+
+
+
+
-
Here are some links to help you start:
-
-
diff --git a/projects/demo/src/app/app.component.css b/projects/demo/src/app/app.component.scss
similarity index 100%
rename from projects/demo/src/app/app.component.css
rename to projects/demo/src/app/app.component.scss
diff --git a/projects/demo/src/app/app.component.spec.ts b/projects/demo/src/app/app.component.spec.ts
index 493bc12..a84adba 100644
--- a/projects/demo/src/app/app.component.spec.ts
+++ b/projects/demo/src/app/app.component.spec.ts
@@ -1,31 +1,87 @@
-import { TestBed } from '@angular/core/testing';
+import { Location } from '@angular/common';
+import { NO_ERRORS_SCHEMA } from '@angular/core';
+import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
+import { MatMenuModule } from '@angular/material';
+import { Router } from '@angular/router';
+import { RouterTestingModule } from '@angular/router/testing';
+
+import { cold, getTestScheduler } from 'jasmine-marbles';
+
+import examples from '../assets/examples/examples.json';
+import example from '../assets/examples/ngx/simple-array.json';
+
import { AppComponent } from './app.component';
+import { routes } from './app.routes';
+import { JsonLoaderService } from './json-loader.service';
describe('AppComponent', () => {
- beforeEach(async () => {
- return TestBed.configureTestingModule({
- declarations: [
- AppComponent
- ]
- }).compileComponents();
- });
-
- it('should create the app', () => {
- const fixture = TestBed.createComponent(AppComponent);
- const app: AppComponent = fixture.debugElement.componentInstance;
- expect(app).toBeTruthy();
- });
-
- it(`should have as title 'demo'`, () => {
- const fixture = TestBed.createComponent(AppComponent);
- const app: AppComponent = fixture.debugElement.componentInstance;
- expect(app.title).toEqual('demo');
- });
-
- it('should render title in a h1 tag', () => {
- const fixture = TestBed.createComponent(AppComponent);
- fixture.detectChanges();
- const compiled: HTMLElement = fixture.debugElement.nativeElement;
- expect(compiled.querySelector('h1').textContent).toContain('Welcome to demo!');
- });
+ let fixture: ComponentFixture
;
+ let app: AppComponent;
+
+ beforeEach(async () => {
+ const jsl: JsonLoaderService = jasmine.createSpyObj('JsonLoaderService', {
+ getExample: cold('a|', {a: JSON.stringify(example)})
+ });
+ Object.defineProperty(jsl, 'examples', {
+ enumerable: true,
+ get: () => cold('a|', {a: examples})
+ });
+
+ return TestBed.configureTestingModule({
+ declarations: [
+ AppComponent
+ ],
+ imports: [ MatMenuModule, RouterTestingModule.withRoutes(routes) ],
+ providers: [{
+ provide: JsonLoaderService,
+ useValue: jsl
+ }],
+ schemas: [ NO_ERRORS_SCHEMA]
+ }).compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(AppComponent);
+ app = fixture.debugElement.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create the app', () => {
+ expect(app).toBeTruthy();
+ });
+
+ it(`should not activate form when empty string`, () => {
+ expect(app.formActive).toBeFalsy();
+ app.generateForm('');
+ expect(app.formActive).toBeFalsy();
+ });
+
+ it(`should activate form`, () => {
+ expect(app.formActive).toBeFalsy();
+ app.generateForm('{}');
+ expect(app.formActive).toBeTruthy();
+ });
+
+ it(`should not activate form when bad json`, () => {
+ expect(app.formActive).toBeFalsy();
+ app.generateForm('{a:}');
+ expect(app.formActive).toBeFalsy();
+ expect(app.jsonFormStatusMessage).toContain('JavaScript parser returned');
+ });
+
+ it('should fetch example and generate form', () => {
+ spyOn(app, 'generateForm');
+ app.loadSelectedExample();
+ getTestScheduler().flush();
+ expect(app.generateForm).toHaveBeenCalledWith(JSON.stringify(example));
+ });
+
+ it('should navigate to the loaded example', fakeAsync(() => {
+ const router: Router = TestBed.get(Router);
+ const location: Location = TestBed.get(Location);
+ router.initialNavigation();
+ app.loadSelectedExample('ngx', 'b', 'c', 'd');
+ tick();
+ expect(location.path()).toBe('/?set=ngx&example=c');
+ }));
});
diff --git a/projects/demo/src/app/app.component.ts b/projects/demo/src/app/app.component.ts
index ba947c9..d3e88ed 100644
--- a/projects/demo/src/app/app.component.ts
+++ b/projects/demo/src/app/app.component.ts
@@ -1,10 +1,261 @@
-import { Component } from '@angular/core';
+import { animate, state, style, transition, trigger } from '@angular/animations';
+import { Component, OnInit, ViewChild } from '@angular/core';
+import { MatMenuTrigger } from '@angular/material';
+import { ActivatedRoute, Params, Router } from '@angular/router';
+
+import { Observable } from 'rxjs';
+
+import { JSONSchema7 } from 'json-schema';
+
+import { JsonLoaderService } from './json-loader.service';
+
+const sets = {
+ // asf: 'Angular Schema Form:',
+ // jsf: 'JSONForm:',
+ ngx: ''
+ // rsf: 'React Schema Form:'
+};
@Component({
- selector: 'app-root',
- styleUrls: ['./app.component.css'],
- templateUrl: './app.component.html'
+ animations: [
+ trigger('expandSection', [
+ state('in', style({ height: '*' })),
+ transition(':enter', [
+ style({ height: 0 }), animate('100ms')
+ ]),
+ transition(':leave', [
+ style({ height: '*' }),
+ animate('100ms', style({ height: 0 }))
+ ])
+ ])
+ ],
+ selector: 'app-demo',
+ styleUrls: ['./app.component.scss'],
+ templateUrl: './app.component.html'
})
-export class AppComponent {
- title = 'demo';
+export class AppComponent implements OnInit {
+ // examples: any = {ngx: {}};
+ // languageList: any = ['en', 'fr'];
+ // languages: any = {
+ // 'en': 'English',
+ // 'fr': 'French',
+ // };
+ // frameworkList: any = ['material-design', 'bootstrap-3', 'bootstrap-4', 'no-framework'];
+ // frameworks: any = {
+ // 'material-design': 'Material Design',
+ // 'bootstrap-3': 'Bootstrap 3',
+ // 'bootstrap-4': 'Bootstrap 4',
+ // 'no-framework': 'None (plain HTML)',
+ // };
+ example: string;
+ selectedSet = '';
+ selectedSetName = '';
+ selectedExample = '';
+ selectedExampleName = '';
+ // selectedFramework = 'material-design';
+ // selectedLanguage = 'en';
+
+ formActive = false;
+ jsonFormValid = false;
+ jsonFormSchema: JSONSchema7;
+ jsonFormStatusMessage = 'Loading form...';
+ // jsonFormObject: any;
+ // jsonFormOptions: any = {
+ // addSubmit: true, // Add a submit button if layout does not have one
+ // debug: false, // Don't show inline debugging information
+ // loadExternalAssets: true, // Load external css and JavaScript for frameworks
+ // returnEmptyFields: false, // Don't return values for empty input fields
+ // setSchemaDefaults: true, // Always use schema defaults for empty fields
+ // defaultWidgetOptions: { feedback: true }, // Show inline feedback icons
+ // };
+ // liveFormData: any = {};
+ // formValidationErrors: any;
+ // formIsValid = null;
+ // submittedFormData: any = null;
+ aceEditorOptions: any = {
+ autoScrollEditorIntoView: true,
+ highlightActiveLine: true,
+ maxLines: 1000,
+ printMargin: false
+ };
+ @ViewChild(MatMenuTrigger) menuTrigger: MatMenuTrigger;
+
+ public examplesObservable: Observable>;
+
+ constructor(
+ private readonly route: ActivatedRoute,
+ private readonly router: Router,
+ private readonly jsonLoader: JsonLoaderService
+ ) { }
+
+ ngOnInit() {
+ this.examplesObservable = this.jsonLoader.examples;
+ this.selectedSet = 'ngx';
+ this.selectedExample = 'simple-array';
+ this.selectedExampleName = 'Simple Array';
+ this.route.queryParams.subscribe((params: Params) => {
+ if (params.set) {
+ this.selectedSet = params.set;
+ this.selectedSetName = sets[this.selectedSet];
+ }
+ if (params.example) {
+ this.selectedExample = params.example;
+ this.jsonLoader.examples.subscribe((examples: Array) => {
+ this.selectedExampleName = examples
+ .find((data: any) => data.set === [this.selectedSet])
+ .find((data: any) => data.file === this.selectedExample).name;
+ });
+ }
+ // if (params['framework']) {
+ // this.selectedFramework = params['framework'];
+ // }
+ // if (params['language']) {
+ // this.selectedLanguage = params['language'];
+ // }
+ this.loadSelectedExample();
+ });
+ }
+
+ trackByFn(index) {
+ return index;
+ }
+
+ // onSubmit(data: any) {
+ // this.submittedFormData = data;
+ // }
+
+ // get prettySubmittedFormData() {
+ // return JSON.stringify(this.submittedFormData, null, 2);
+ // }
+
+ // onChanges(data: any) {
+ // this.liveFormData = data;
+ // }
+
+ // get prettyLiveFormData() {
+ // return JSON.stringify(this.liveFormData, null, 2);
+ // }
+
+ // isValid(isValid: boolean): void {
+ // this.formIsValid = isValid;
+ // }
+
+ // validationErrors(data: any): void {
+ // this.formValidationErrors = data;
+ // }
+
+ // get prettyValidationErrors() {
+ // if (!this.formValidationErrors) { return null; }
+ // let errorArray = [];
+ // for (let error of this.formValidationErrors) {
+ // let message = error.message;
+ // let dataPathArray = JsonPointer.parse(error.dataPath);
+ // if (dataPathArray.length) {
+ // let field = dataPathArray[0];
+ // for (let i = 1; i < dataPathArray.length; i++) {
+ // const key = dataPathArray[i];
+ // field += /^\d+$/.test(key) ? `[${key}]` : `.${key}`;
+ // }
+ // errorArray.push(`${field}: ${message}`);
+ // } else {
+ // errorArray.push(message);
+ // }
+ // }
+ // return errorArray.join('
');
+ // }
+
+ loadSelectedExample(
+ selectedSet: string = this.selectedSet,
+ selectedSetName: string = this.selectedSetName,
+ selectedExample: string = this.selectedExample,
+ selectedExampleName: string = this.selectedExampleName
+ ): void {
+ if (this.menuTrigger && this.menuTrigger.menuOpen) { this.menuTrigger.closeMenu(); }
+ if (selectedExample !== this.selectedExample) {
+ this.formActive = false;
+ this.selectedSet = selectedSet;
+ this.selectedSetName = selectedSetName;
+ this.selectedExample = selectedExample;
+ this.selectedExampleName = selectedExampleName;
+ // https://github.com/declandewet/common-tags#stripindent
+ this.router.navigateByUrl(`/?set=${selectedSet}&example=${selectedExample}`)
+ .catch((reason) => {
+ console.warn(reason);
+ });
+ // &framework=${this.selectedFramework}\
+ // &language=${this.selectedLanguage}\
+ // this.liveFormData = {};
+ // this.submittedFormData = null;
+ // this.formIsValid = null;
+ // this.formValidationErrors = null;
+ } else {
+ this.jsonLoader.getExample(this.selectedSet, this.selectedExample)
+ .subscribe((example: string) => {
+ this.generateForm(example);
+ });
+ }
+ }
+
+ // loadSelectedLanguage() {
+ // window.location.href =
+ // '/?set=' + this.selectedSet +
+ // '&example=' + this.selectedExample +
+ // '&framework=' + this.selectedFramework +
+ // '&language=' + this.selectedLanguage;
+ // }
+
+ // Display the form entered by the user
+ // (runs whenever the user changes the jsonform object in the ACE input field)
+ generateForm(newFormString: string): void {
+ if (!newFormString) { return; }
+ this.example = newFormString;
+ this.jsonFormStatusMessage = 'Loading form...';
+ this.formActive = false;
+ // this.liveFormData = {};
+ // this.submittedFormData = null;
+
+ // Most examples should be written in pure JSON,
+ // but if an example schema includes a function,
+ // it will be compiled it as Javascript instead
+ try {
+ // Parse entered content as JSON
+ const jsonFormObject = JSON.parse(newFormString);
+ this.jsonFormSchema = jsonFormObject.schema;
+ this.jsonFormValid = true;
+ this.formActive = true;
+ } catch (jsonError) {
+ // try {
+
+ // // If entered content is not valid JSON,
+ // // parse as JavaScript instead to include functions
+ // let newFormObject: any = null;
+ // /* tslint:disable */
+ // eval('newFormObject = ' + newFormString);
+ // /* tslint:enable */
+ // this.jsonFormObject = newFormObject;
+ // this.jsonFormValid = true;
+ // } catch (javascriptError) {
+
+ // // If entered content is not valid JSON or JavaScript, show error
+ // this.jsonFormValid = false;
+ this.jsonFormStatusMessage = `Entered content is not currently a valid JSON Form object.
+ As soon as it is, you will see your form here. So keep typing. :-)
+
+ JavaScript parser returned:
+
+ ${jsonError}`;
+ // return;
+ // }
+ }
+ }
+
+ // toggleFormOption(option: string) {
+ // if (option === 'feedback') {
+ // this.jsonFormOptions.defaultWidgetOptions.feedback =
+ // !this.jsonFormOptions.defaultWidgetOptions.feedback;
+ // } else {
+ // this.jsonFormOptions[option] = !this.jsonFormOptions[option];
+ // }
+ // this.generateForm(this.jsonFormSchema);
+ // }
}
diff --git a/projects/demo/src/app/app.module.ts b/projects/demo/src/app/app.module.ts
index 394278e..9960015 100644
--- a/projects/demo/src/app/app.module.ts
+++ b/projects/demo/src/app/app.module.ts
@@ -1,16 +1,38 @@
+import { HttpClientModule } from '@angular/common/http';
import { NgModule } from '@angular/core';
+import { FlexLayoutModule } from '@angular/flex-layout';
+import { FormsModule } from '@angular/forms';
+import {
+ MatButtonModule, MatCardModule, MatCheckboxModule, MatIconModule,
+ MatMenuModule, MatSelectModule, MatToolbarModule
+} from '@angular/material';
import { BrowserModule } from '@angular/platform-browser';
+import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+import { RouterModule } from '@angular/router';
+
+import { AceEditorModule } from 'ng2-ace-editor';
+
+import { JsonSchemaFormModule } from '../../../ngx-json-schema-form/src/public-api';
import { AppComponent } from './app.component';
+import { routes } from './app.routes';
+import { RootComponent } from './root.component';
@NgModule({
- bootstrap: [AppComponent],
- declarations: [
- AppComponent
- ],
- imports: [
- BrowserModule
- ],
- providers: []
+ bootstrap: [ RootComponent ],
+ declarations: [
+ AppComponent,
+ RootComponent
+ ],
+ imports: [
+ BrowserModule, BrowserAnimationsModule, FormsModule, HttpClientModule, FlexLayoutModule,
+ MatButtonModule, MatCardModule, MatCheckboxModule,
+ MatIconModule, MatMenuModule, MatSelectModule, MatToolbarModule,
+ RouterModule.forRoot(routes),
+
+ AceEditorModule,
+
+ JsonSchemaFormModule
+ ]
})
export class AppModule { }
diff --git a/projects/demo/src/app/app.routes.ts b/projects/demo/src/app/app.routes.ts
new file mode 100644
index 0000000..7143d57
--- /dev/null
+++ b/projects/demo/src/app/app.routes.ts
@@ -0,0 +1,8 @@
+import { Route } from '@angular/router';
+
+import { AppComponent } from './app.component';
+
+export const routes: Array = [
+ { path: '', component: AppComponent },
+ { path: '**', component: AppComponent }
+];
diff --git a/projects/demo/src/app/json-loader.service.spec.ts b/projects/demo/src/app/json-loader.service.spec.ts
new file mode 100644
index 0000000..b1948f1
--- /dev/null
+++ b/projects/demo/src/app/json-loader.service.spec.ts
@@ -0,0 +1,32 @@
+import { HttpClientTestingModule } from '@angular/common/http/testing';
+import { TestBed } from '@angular/core/testing';
+
+import { cold } from 'jasmine-marbles';
+
+import { JsonLoaderService } from './json-loader.service';
+
+describe('JsonLoaderService', () => {
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ imports: [ HttpClientTestingModule ],
+ providers: [
+ JsonLoaderService
+ ]
+ });
+ });
+
+ it('should be created', () => {
+ const service: JsonLoaderService = TestBed.get(JsonLoaderService);
+ expect(service).toBeTruthy();
+ });
+
+ it('should have observable examples', () => {
+ const service: JsonLoaderService = TestBed.get(JsonLoaderService);
+ expect(service.examples).toBeObservable(cold('-'));
+ });
+
+ it('should have observable example', () => {
+ const service: JsonLoaderService = TestBed.get(JsonLoaderService);
+ expect(service.getExample('ngx', 'basic')).toBeObservable(cold('-'));
+ });
+});
diff --git a/projects/demo/src/app/json-loader.service.ts b/projects/demo/src/app/json-loader.service.ts
new file mode 100644
index 0000000..c830c50
--- /dev/null
+++ b/projects/demo/src/app/json-loader.service.ts
@@ -0,0 +1,24 @@
+import { HttpClient } from '@angular/common/http';
+import { Injectable } from '@angular/core';
+
+import { Observable } from 'rxjs';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class JsonLoaderService {
+ private readonly examplesURL = 'assets/examples/examples.json';
+
+ constructor(private readonly httpClient: HttpClient) {}
+
+ get examples(): Observable {
+ return this.httpClient.get(this.examplesURL);
+ }
+
+ getExample(set: string, example: string): Observable {
+ const exampleURL = `assets/examples/${set}/${example}.json`;
+
+ return this.httpClient.get(exampleURL, { responseType: 'text' });
+ }
+
+}
diff --git a/projects/demo/src/app/root.component.ts b/projects/demo/src/app/root.component.ts
new file mode 100644
index 0000000..c23b495
--- /dev/null
+++ b/projects/demo/src/app/root.component.ts
@@ -0,0 +1,7 @@
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'app-root',
+ template: ``
+})
+export class RootComponent { }
diff --git a/projects/demo/src/assets/examples/examples.json b/projects/demo/src/assets/examples/examples.json
new file mode 100644
index 0000000..c559fa3
--- /dev/null
+++ b/projects/demo/src/assets/examples/examples.json
@@ -0,0 +1,13 @@
+[
+ {
+ "set": "ngx",
+ "label": "NGX Angular JSON Schema Form:",
+ "name": "NGX Angular JSON Schema Form examples",
+ "examples": [
+ {
+ "name": "Simple Array",
+ "file": "simple-array"
+ }
+ ]
+ }
+]
diff --git a/projects/demo/src/assets/examples/ngx/simple-array.json b/projects/demo/src/assets/examples/ngx/simple-array.json
new file mode 100644
index 0000000..b072993
--- /dev/null
+++ b/projects/demo/src/assets/examples/ngx/simple-array.json
@@ -0,0 +1,18 @@
+{
+ "schema": {
+ "type": "object",
+ "properties": {
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "title": "Item",
+ "default": "New Item"
+ }
+ }
+ }
+ },
+ "data": {
+ "items": [ "Item 1", "Item 2", "Item 3", "Item 4" ]
+ }
+}
diff --git a/projects/demo/src/index.html b/projects/demo/src/index.html
index 77e5ff2..befe662 100644
--- a/projects/demo/src/index.html
+++ b/projects/demo/src/index.html
@@ -1,14 +1,16 @@
-
- Demo
-
+
+ NGX Angular JSON Schema Form Demo
+
-
-
+
+
+
+
-
+
diff --git a/projects/demo/src/main.ts b/projects/demo/src/main.ts
index 3192888..2bc3b38 100644
--- a/projects/demo/src/main.ts
+++ b/projects/demo/src/main.ts
@@ -4,6 +4,8 @@ import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
+import 'brace/mode/json';
+
if (environment.production) {
enableProdMode();
}
diff --git a/projects/demo/src/styles.css b/projects/demo/src/styles.css
deleted file mode 100644
index 90d4ee0..0000000
--- a/projects/demo/src/styles.css
+++ /dev/null
@@ -1 +0,0 @@
-/* You can add global styles to this file, and also import other style files */
diff --git a/projects/demo/src/styles.scss b/projects/demo/src/styles.scss
new file mode 100644
index 0000000..eb3d55e
--- /dev/null
+++ b/projects/demo/src/styles.scss
@@ -0,0 +1,74 @@
+// * { border: 1px solid red !important; }
+@import '~@angular/material/theming';
+@include mat-core();
+$demo-app-primary: mat-palette($mat-blue);
+$demo-app-accent: mat-palette($mat-amber, A200, A100, A400);
+$demo-app-warn: mat-palette($mat-red);
+$demo-app-theme: mat-light-theme($demo-app-primary, $demo-app-accent, $demo-app-warn);
+@include angular-material-theme($demo-app-theme);
+
+$font-family: 'Roboto', 'Noto', 'Helvetica Neue', sans-serif;
+$row-height: 56px;
+
+mat-toolbar {
+ &.mat-medium {
+ min-height: $row-height;
+ mat-toolbar-row { height: $row-height; }
+ }
+}
+
+body {
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ background-color: rgb(250, 250, 250) !important;
+ display: flex;
+ flex: 1 1 auto;
+ flex-direction: column;
+ font-family: $font-family;
+ height: 100%;
+ margin: 0;
+ padding: 0;
+}
+
+.demo-page-header {
+ background-color: mat-color($demo-app-primary, lighter);
+ margin-bottom: 12px;
+ .header-content {
+ font-family: $font-family;
+ line-height: 1.4em;
+ padding: 12px;
+ .menu-label {
+ margin-right: 12px;
+ font-weight: bold;
+ }
+ }
+}
+
+.ace_active-line { background: none !important; }
+
+.data-good, .data-bad {
+ border-radius: 3px;
+ padding: 6px;
+ border: 1px solid #ccc !important;
+}
+
+.avoidwrap { display:inline-block; }
+
+.data-good { background-color: #dfd; }
+
+.data-bad { background-color: #fcc; }
+
+.check-row { margin-top: 8px; }
+
+.cdk-overlay-container .cdk-overlay-pane .mat-menu-panel { max-width: 560px; }
+
+.debug { border: 1px solid red !important; }
+
+.mat-input-container.mat-form-field { width: 100%; }
+
+details {
+ summary {
+ font-weight: bold;
+ margin: 0 0 1.33em;
+ }
+}
diff --git a/projects/demo/src/test.ts b/projects/demo/src/test.ts
index abc8165..279813f 100644
--- a/projects/demo/src/test.ts
+++ b/projects/demo/src/test.ts
@@ -1,13 +1,13 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
+import 'zone.js/dist/zone-testing';
+
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
-import 'zone.js/dist/zone-testing';
-
declare const require: any;
// First, initialize the Angular testing environment.
diff --git a/projects/demo/tsconfig.spec.json b/projects/demo/tsconfig.spec.json
index a809b0a..9ca5dde 100644
--- a/projects/demo/tsconfig.spec.json
+++ b/projects/demo/tsconfig.spec.json
@@ -5,7 +5,8 @@
"types": [
"jasmine",
"node"
- ]
+ ],
+ "resolveJsonModule": true
},
"files": [
"src/test.ts",
diff --git a/projects/ngx-json-schema-form/src/lib/widget-library/button/button.component.css b/projects/ngx-json-schema-form/src/lib/widget-library/button/button.component.scss
similarity index 100%
rename from projects/ngx-json-schema-form/src/lib/widget-library/button/button.component.css
rename to projects/ngx-json-schema-form/src/lib/widget-library/button/button.component.scss
diff --git a/projects/ngx-json-schema-form/src/lib/widget-library/button/button.component.ts b/projects/ngx-json-schema-form/src/lib/widget-library/button/button.component.ts
index f29b540..750b87d 100644
--- a/projects/ngx-json-schema-form/src/lib/widget-library/button/button.component.ts
+++ b/projects/ngx-json-schema-form/src/lib/widget-library/button/button.component.ts
@@ -6,7 +6,7 @@ import { Widget } from '../widget';
@Component({
selector: 'jsf-button',
- styleUrls: ['./button.component.css'],
+ styleUrls: ['./button.component.scss'],
templateUrl: './button.component.html'
})
export class ButtonComponent extends Widget {
diff --git a/tslint.json b/tslint.json
index 533555b..8a7ebe4 100644
--- a/tslint.json
+++ b/tslint.json
@@ -25,7 +25,7 @@
],
"no-any": false,
"no-empty-interface": true,
- "no-import-side-effect": [true, {"ignore-module": "(\\.html|\\.css)$|^(zone.js|core.js)"}],
+ "no-import-side-effect": [true, {"ignore-module": "(\\.html|\\.css)$|^(zone.js|core.js|brace)"}],
"no-inferrable-types": true,
"no-internal-module": true,
"no-magic-numbers": true,
@@ -136,7 +136,7 @@
"spaces",
4
],
- "linebreak-style": [true, "LF"],
+ "linebreak-style": [false, "LF"],
"max-classes-per-file": [true, 1],
"max-file-line-count": [true, 400],
"max-line-length": [
@@ -202,7 +202,7 @@
],
"one-variable-per-declaration": [true, "ignore-for-loop"],
"ordered-imports": true,
- "prefer-function-over-method": true,
+ "prefer-function-over-method": [true, "allow-public"],
"prefer-method-signature": true,
"prefer-switch": [true, {"min-cases": 2}],
"prefer-template": true,