Skip to content

Commit 2227122

Browse files
committed
Fixes problem adding recursive $ref items.
1 parent 6939623 commit 2227122

15 files changed

+443
-285
lines changed

.npmignore

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,23 @@ docs
44
dist/playground
55
src/playground
66

7-
# TypeScript
8-
tsconfig.json
7+
# Config
8+
.editorconfig
9+
.gitignore
910
tslint.json
1011
typedoc.json
11-
typings.json
12-
*.ts
13-
!*.d.ts
1412

1513
# Node
1614
node_modules
1715
npm-debug.log
1816

19-
# IDE
17+
# IDE & OS
2018
.idea
2119
.project
2220
.settings
2321
.idea/*
2422
*.iml
2523
*.swp
26-
27-
# OS
2824
Thumbs.db
2925
Desktop.ini
3026
.DS_Store

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,7 @@ The two built-in frameworks (both in the `/src/frameworks` folder) demonstrate d
341341

342342
If you find this library useful, I'd love to hear from you. If you have any trouble with it or would like to request a feature, please [post an issue on GitHub](https://github.com/dschnelldavis/angular2-json-schema-form/issues). If you've made any improvements, please [make a pull request](https://github.com/dschnelldavis/angular2-json-schema-form/pulls), so I can share your improvements with everyone else who uses this library.
343343

344-
I wrote this library because I needed a JSON Schema Form builder to use in a large Angular 2 project I am currently working on. Though I found excellent libraries for Angular 1, React, and jQuery (all linked above), I could not find anything similar for Angular 2—so I wrote this library to fill that gap. The current version is mostly functional, and even includes a few enhancements not available in some other libraries, such as supporting less common JSON Schema features like `oneOf`, `allOf`, and `$ref` links (including circular links). However, it still has many small bugs, such as not dynamically enabling and disabling conditionally required fields inside objects, and is fragile because it does not yet include any testing framework. Hopefully all these issues will get fixed eventually, but as I'm just a single busy programmer, I can't guarantee how long it will take. In the meantime, I hope you find it useful, and if you do, please send me your feedback.
344+
I wrote this library because I needed a JSON Schema Form builder to use in a large Angular 2 project I am currently working on. Though I found excellent libraries for Angular 1, React, and jQuery (all linked above), I could not find anything similar for Angular 2—so I wrote this library to fill that gap. The current version is mostly functional, and even includes a few enhancements not available in some other libraries, such as supporting less common JSON Schema features like `oneOf`, `allOf`, and `$ref` links (including recursive links). However, it still has many small bugs, such as not dynamically enabling and disabling conditionally required fields inside objects, and is fragile because it does not yet include any testing framework. Hopefully all these issues will get fixed eventually, but as I'm just a single busy programmer, I can't guarantee how long it will take. In the meantime, I hope you find it useful, and if you do, please send me your feedback.
345345

346346
Thanks! I hope you enjoy using this library as much as I enjoyed writing it. :-)
347347

package.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "angular2-json-schema-form",
3-
"version": "0.3.0-alpha.19",
3+
"version": "0.3.0-alpha.20",
44
"author": {
55
"name": "dschnelldavis",
66
"email": "[email protected]"
@@ -59,9 +59,9 @@
5959
"@angular/platform-server": "^2.4.1",
6060
"@angular/router": "^3.4.1",
6161
"@types/ace": "^0.0.32",
62-
"@types/jasmine": "^2.5.38",
63-
"@types/lodash": "^4.14.42",
64-
"@types/node": "^6.0.53",
62+
"@types/jasmine": "^2.5.40",
63+
"@types/lodash": "^4.14.45",
64+
"@types/node": "^6.0.55",
6565
"brace": "^0.9.1",
6666
"buffer": "^5.0.0",
6767
"canonical-path": "^0.0.2",
@@ -78,14 +78,14 @@
7878
"karma-jasmine": "^1.0.2",
7979
"karma-jasmine-html-reporter": "^0.2.2",
8080
"lite-server": "^2.2.2",
81-
"onchange": "^3.2.0",
81+
"onchange": "^3.2.1",
8282
"protractor": "^4.0.13",
8383
"reflect-metadata": "^0.1.8",
8484
"rimraf": "^2.5.4",
8585
"rxjs": "^5.0.2",
8686
"systemjs": "^0.19.41",
8787
"tslint": "^4.2.0",
88-
"typedoc": "^0.5.1",
88+
"typedoc": "^0.5.3",
8989
"typescript": "^2.1.4",
9090
"typings": "^2.1.0",
9191
"zone.js": "^0.7.2"

src/frameworks/material-design/material-add-reference.component.ts

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Component, Input, OnChanges, OnInit } from '@angular/core';
1+
import { Component, DoCheck, Input, OnInit } from '@angular/core';
22
import { FormGroup } from '@angular/forms';
33

44
import { JsonSchemaFormService } from '../../library/json-schema-form.service';
@@ -17,10 +17,12 @@ import { JsonSchemaFormService } from '../../library/json-schema-form.service';
1717
</button>
1818
</section>`,
1919
})
20-
export class MaterialAddReferenceComponent implements OnInit, OnChanges {
20+
export class MaterialAddReferenceComponent implements OnInit, DoCheck {
2121
private options: any;
2222
private itemCount: number;
2323
private showAddButton: boolean = true;
24+
private previousLayoutIndex: number[];
25+
private previousDataIndex: number[];
2426
@Input() formID: number;
2527
@Input() layoutNode: any;
2628
@Input() layoutIndex: number[];
@@ -32,12 +34,15 @@ export class MaterialAddReferenceComponent implements OnInit, OnChanges {
3234

3335
ngOnInit() {
3436
this.options = this.layoutNode.options;
35-
this.itemCount = this.layoutIndex[this.layoutIndex.length - 1];
3637
this.updateControl();
3738
}
3839

39-
ngOnChanges() {
40-
this.updateControl();
40+
ngDoCheck() {
41+
if (this.previousLayoutIndex !== this.layoutIndex ||
42+
this.previousDataIndex !== this.dataIndex
43+
) {
44+
this.updateControl();
45+
}
4146
}
4247

4348
private addItem(event) {
@@ -48,8 +53,11 @@ export class MaterialAddReferenceComponent implements OnInit, OnChanges {
4853
}
4954

5055
private updateControl() {
56+
this.itemCount = this.layoutIndex[this.layoutIndex.length - 1];
57+
this.previousLayoutIndex = this.layoutIndex;
58+
this.previousDataIndex = this.dataIndex;
5159
this.showAddButton = this.layoutNode.arrayItem &&
52-
this.itemCount <= (this.options.maxItems || 1000000);
60+
this.itemCount < (this.options.maxItems || 1000000);
5361
}
5462

5563
private setTitle(): string {

src/frameworks/material-design/material-button.component.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { JsonSchemaFormService } from '../../library/json-schema-form.service';
2222
<span *ngIf="options?.title" [innerHTML]="options?.title"></span>
2323
</button>
2424
</section>`,
25+
styles: [`button { margin-top: 4px; }`],
2526
})
2627
export class MaterialButtonComponent implements OnInit {
2728
private formControl: AbstractControl;

src/frameworks/material-design/material-input.component.ts

Lines changed: 29 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,32 +6,35 @@ import { JsonSchemaFormService } from '../../library/json-schema-form.service';
66
@Component({
77
selector: 'material-input-widget',
88
template: `
9-
<md-input #inputControl
10-
[attr.aria-describedby]="'control' + layoutNode?._id + 'Status'"
11-
[attr.list]="'control' + layoutNode?._id + 'Autocomplete'"
12-
[attr.maxlength]="options?.maxLength"
13-
[attr.minlength]="options?.minLength"
14-
[attr.pattern]="options?.pattern"
15-
[attr.required]="options?.required"
16-
[class]="options?.fieldHtmlClass"
17-
[disabled]="controlDisabled"
18-
[id]="'control' + layoutNode?._id"
19-
[name]="controlName"
20-
[placeholder]="options?.title"
21-
[readonly]="options?.readonly ? 'readonly' : null"
22-
[style.width]="'100%'"
23-
[type]="layoutNode?.type"
24-
[value]="controlValue"
25-
(input)="updateValue($event)">
26-
<span *ngIf="options?.fieldAddonLeft"
27-
md-prefix>{{options?.fieldAddonLeft}}</span>
28-
<span *ngIf="options?.fieldAddonRight"
29-
md-suffix>{{options?.fieldAddonRight}}</span>
30-
<md-hint *ngIf="options?.description && !(options?.placeholder && !formControl?.dirty)"
31-
align="end">{{options?.description}}</md-hint>
32-
<md-hint *ngIf="!options?.description && options?.placeholder && !formControl?.dirty"
33-
align="end">{{options?.placeholder}}</md-hint>
34-
</md-input>`,
9+
<section [class]="options?.htmlClass">
10+
<md-input #inputControl
11+
[attr.aria-describedby]="'control' + layoutNode?._id + 'Status'"
12+
[attr.list]="'control' + layoutNode?._id + 'Autocomplete'"
13+
[attr.maxlength]="options?.maxLength"
14+
[attr.minlength]="options?.minLength"
15+
[attr.pattern]="options?.pattern"
16+
[required]="options?.required"
17+
[class]="options?.fieldHtmlClass"
18+
[disabled]="controlDisabled"
19+
[id]="'control' + layoutNode?._id"
20+
[name]="controlName"
21+
[placeholder]="options?.title"
22+
[readonly]="options?.readonly ? 'readonly' : null"
23+
[style.width]="'100%'"
24+
[type]="layoutNode?.type"
25+
[value]="controlValue"
26+
(input)="updateValue($event)">
27+
<span *ngIf="options?.fieldAddonLeft"
28+
md-prefix>{{options?.fieldAddonLeft}}</span>
29+
<span *ngIf="options?.fieldAddonRight"
30+
md-suffix>{{options?.fieldAddonRight}}</span>
31+
<md-hint *ngIf="options?.description && !(options?.placeholder && !formControl?.dirty)"
32+
align="end">{{options?.description}}</md-hint>
33+
<md-hint *ngIf="!options?.description && options?.placeholder && !formControl?.dirty"
34+
align="end">{{options?.placeholder}}</md-hint>
35+
</md-input>
36+
</section>`,
37+
styles: [`md-input { margin-top: 6px; }`],
3538
})
3639
export class MaterialInputComponent implements OnInit {
3740
private formControl: AbstractControl;

src/frameworks/material-design/material-number.component.ts

Lines changed: 34 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -7,37 +7,40 @@ import { getControl, inArray, isDefined } from '../../library/utilities/index';
77
@Component({
88
selector: 'material-number-widget',
99
template: `
10-
<md-input #inputControl
11-
[attr.aria-describedby]="'control' + layoutNode?._id + 'Status'"
12-
[attr.max]="options?.maximum"
13-
[attr.min]="options?.minimum"
14-
[attr.placeholder]="options?.placeholder"
15-
[attr.required]="options?.required"
16-
[attr.readonly]="options?.readonly ? 'readonly' : null"
17-
[attr.step]="options?.multipleOf || options?.step || 'any'"
18-
[class]="options?.fieldHtmlClass"
19-
[disabled]="controlDisabled"
20-
[id]="'control' + layoutNode?._id"
21-
[name]="controlName"
22-
[placeholder]="options?.title"
23-
[readonly]="options?.readonly ? 'readonly' : null"
24-
[style.width]="'100%'"
25-
[title]="lastValidNumber"
26-
[type]="layoutNode?.type === 'range' ? 'range' : 'number'"
27-
[value]="controlValue"
28-
(input)="updateValue($event)"
29-
(keydown)="validateInput($event)"
30-
(keyup)="validateNumber($event)">
31-
<span *ngIf="options?.fieldAddonLeft"
32-
md-prefix>{{options?.fieldAddonLeft}}</span>
33-
<span *ngIf="options?.fieldAddonRight"
34-
md-suffix>{{options?.fieldAddonRight}}</span>
35-
<md-hint *ngIf="options?.description && !(options?.placeholder && !formControl?.dirty)"
36-
align="end">{{options?.description}}</md-hint>
37-
<md-hint *ngIf="!options?.description && options?.placeholder && !formControl?.dirty"
38-
align="end">{{options?.placeholder}}</md-hint>
39-
</md-input>
40-
{{layoutNode?.type === 'range' ? controlValue : ''}}`,
10+
<section [class]="options?.htmlClass">
11+
<md-input #inputControl
12+
[attr.aria-describedby]="'control' + layoutNode?._id + 'Status'"
13+
[attr.max]="options?.maximum"
14+
[attr.min]="options?.minimum"
15+
[attr.placeholder]="options?.placeholder"
16+
[required]="options?.required"
17+
[attr.readonly]="options?.readonly ? 'readonly' : null"
18+
[attr.step]="options?.multipleOf || options?.step || 'any'"
19+
[class]="options?.fieldHtmlClass"
20+
[disabled]="controlDisabled"
21+
[id]="'control' + layoutNode?._id"
22+
[name]="controlName"
23+
[placeholder]="options?.title"
24+
[readonly]="options?.readonly ? 'readonly' : null"
25+
[style.width]="'100%'"
26+
[title]="lastValidNumber"
27+
[type]="layoutNode?.type === 'range' ? 'range' : 'number'"
28+
[value]="controlValue"
29+
(input)="updateValue($event)"
30+
(keydown)="validateInput($event)"
31+
(keyup)="validateNumber($event)">
32+
<span *ngIf="options?.fieldAddonLeft"
33+
md-prefix>{{options?.fieldAddonLeft}}</span>
34+
<span *ngIf="options?.fieldAddonRight"
35+
md-suffix>{{options?.fieldAddonRight}}</span>
36+
<md-hint *ngIf="options?.description && !(options?.placeholder && !formControl?.dirty)"
37+
align="end">{{options?.description}}</md-hint>
38+
<md-hint *ngIf="!options?.description && options?.placeholder && !formControl?.dirty"
39+
align="end">{{options?.placeholder}}</md-hint>
40+
</md-input>
41+
{{layoutNode?.type === 'range' ? controlValue : ''}}
42+
</section>`,
43+
styles: [`md-input { margin-top: 6px; }`],
4144
})
4245
export class MaterialNumberComponent implements OnInit {
4346
private formControl: AbstractControl;

src/frameworks/material-design/material-select.component.ts

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,24 @@ import { buildTitleMap } from '../../library/utilities/index';
77
@Component({
88
selector: 'material-select-widget',
99
template: `
10-
<label *ngIf="options?.title"
11-
[attr.for]="'control' + layoutNode?._id"
12-
[class]="options?.labelHtmlClass"
13-
[class.sr-only]="options?.notitle"
14-
[innerHTML]="options?.title"></label>
15-
<select
16-
[attr.aria-describedby]="'control' + layoutNode?._id + 'Status'"
17-
[attr.readonly]="options?.readonly ? 'readonly' : null"
18-
[attr.required]="options?.required"
19-
[class]="options?.fieldHtmlClass"
20-
[disabled]="controlDisabled"
21-
[id]="'control' + layoutNode?._id"
22-
[name]="controlName"
23-
(input)="updateValue($event)">
24-
<option *ngFor="let selectItem of selectList"
25-
[value]="selectItem.value"
26-
[selected]="selectItem.value === controlValue">{{selectItem.name}}</option>
27-
</select>`,
10+
<section [class]="options?.htmlClass">
11+
<md-select #inputControl
12+
[(ngModel)]="controlValue"
13+
[attr.aria-describedby]="'control' + layoutNode?._id + 'Status'"
14+
[attr.name]="controlName"
15+
[attr.readonly]="options?.readonly ? 'readonly' : null"
16+
[class]="options?.fieldHtmlClass"
17+
[disabled]="controlDisabled"
18+
[id]="'control' + layoutNode?._id"
19+
[placeholder]="options?.title"
20+
[required]="options?.required"
21+
(onClose)="updateValue($event)">
22+
<md-option *ngFor="let selectItem of selectList"
23+
[value]="selectItem.value"
24+
[attr.selected]="selectItem.value === controlValue">{{selectItem.name}}</md-option>
25+
</md-select>
26+
</section>`,
27+
styles: [`md-select { margin-top: 18px; }`]
2828
})
2929
export class MaterialSelectComponent implements OnInit {
3030
private formControl: AbstractControl;
@@ -54,6 +54,6 @@ export class MaterialSelectComponent implements OnInit {
5454
}
5555

5656
private updateValue(event) {
57-
this.jsf.updateValue(this, event.target.value);
57+
this.jsf.updateValue(this, this.controlValue);
5858
}
5959
}

0 commit comments

Comments
 (0)