Skip to content
This repository was archived by the owner on Aug 25, 2020. It is now read-only.

Commit 821ef38

Browse files
committed
feat: simple json editor
1 parent 11a7345 commit 821ef38

File tree

7 files changed

+256
-3
lines changed

7 files changed

+256
-3
lines changed

dist/ngx-forms.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<ng-container [formGroup]="group">
2+
<json-control [formControlName]="field.name"></json-control>
3+
</ng-container>
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2+
import { FormJsonComponent } from './form-json.component'
3+
import { ReactiveFormsModule, FormsModule, FormBuilder } from '@angular/forms';
4+
import { APP_BASE_HREF } from '@angular/common';
5+
import { JsonControlValueAccessorComponent } from './json-control/json-control.component'
6+
7+
describe('FormJsonComponent', () => {
8+
let component: FormJsonComponent;
9+
let fixture: ComponentFixture<FormJsonComponent>;
10+
const formBuilder: FormBuilder = new FormBuilder();
11+
12+
beforeEach(async(() => {
13+
TestBed.configureTestingModule({
14+
imports: [
15+
FormsModule,
16+
ReactiveFormsModule
17+
],
18+
declarations: [FormJsonComponent, JsonControlValueAccessorComponent],
19+
providers: [
20+
{ provide: APP_BASE_HREF, useValue: '/' }
21+
]
22+
})
23+
.compileComponents();
24+
}));
25+
26+
beforeEach(() => {
27+
fixture = TestBed.createComponent(FormJsonComponent);
28+
component = fixture.componentInstance;
29+
component.group = formBuilder.group({ testName: formBuilder.control('') });
30+
component.field = {
31+
"type": "text",
32+
"label": "Explain",
33+
"name": "testName",
34+
"required": true
35+
};
36+
37+
fixture.detectChanges();
38+
});
39+
40+
it('should be created', () => {
41+
expect(component).toBeTruthy();
42+
});
43+
44+
});
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { Component } from '@angular/core';
2+
import { BaseFieldComponent } from '../base-field';
3+
4+
@Component({
5+
selector: 'form-json',
6+
template: require('./form-json.component.html')
7+
})
8+
9+
export class FormJsonComponent extends BaseFieldComponent { }
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
import { JsonControlValueAccessorComponent } from './json-control.component';
2+
import { ComponentFixture, TestBed } from '@angular/core/testing';
3+
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
4+
5+
describe('MulticheckboxControlComponent', () => {
6+
let component: JsonControlValueAccessorComponent;
7+
let fixture: ComponentFixture<JsonControlValueAccessorComponent>;
8+
9+
const model = { test: 1 };
10+
11+
beforeEach(() => {
12+
TestBed.configureTestingModule({
13+
imports: [
14+
ReactiveFormsModule,
15+
FormsModule,
16+
],
17+
declarations: [JsonControlValueAccessorComponent]
18+
}).compileComponents();
19+
});
20+
21+
beforeEach(() => {
22+
fixture = TestBed.createComponent(JsonControlValueAccessorComponent);
23+
component = fixture.componentInstance;
24+
fixture.detectChanges();
25+
});
26+
27+
it('should be created', () => {
28+
expect(component).toBeTruthy();
29+
});
30+
31+
it('should not throw error', () => {
32+
expect(() => { component.onModelTouched(123); }).not.toThrowError();
33+
});
34+
35+
describe('writeValue()', () => {
36+
it('should set value', () => {
37+
component.writeValue(model);
38+
expect(component.textarea.nativeElement.value).toEqual(JSON.stringify(model));
39+
});
40+
41+
it('should not set value', () => {
42+
const val = component.textarea.nativeElement.value
43+
component.writeValue(null);
44+
expect(component.textarea.nativeElement.value).toEqual(val);
45+
});
46+
});
47+
48+
describe('registerOnChange()', () => {
49+
it('should register onChange', () => {
50+
const foo = { log: (val) => { } };
51+
spyOn(foo, 'log');
52+
const x = (val) => foo.log(val);
53+
component.registerOnChange(x);
54+
component.onModelChange(123);
55+
expect(foo.log).toHaveBeenCalled();
56+
});
57+
});
58+
59+
describe('registerOnTouched()', () => {
60+
it('should register onTouch', () => {
61+
const foo = { log: (val) => { } }; spyOn(foo, 'log');
62+
const x = (val) => foo.log(val);
63+
component.registerOnTouched(x);
64+
component.onModelTouched(123);
65+
expect(foo.log).toHaveBeenCalled();
66+
});
67+
});
68+
69+
describe('setDisabledState()', () => {
70+
it('should set disalbed', () => {
71+
component.setDisabledState(true);
72+
expect(component.textarea.nativeElement.disabled).toBeTruthy();
73+
});
74+
75+
it('should set enabled', () => {
76+
component.setDisabledState(false);
77+
expect(component.textarea.nativeElement.disabled).toBeFalsy();
78+
});
79+
});
80+
81+
describe('validate()', () => {
82+
it('should call validate', () => {
83+
expect(component.validate()).toBeDefined();
84+
});
85+
86+
it('should return errors', () => {
87+
component.required = true;
88+
component.writeValue(null);
89+
const res = component.validate();
90+
expect(res.required).toBeTruthy();
91+
});
92+
93+
it('should not return errors when component is not required', () => {
94+
component.required = false;
95+
const mdl = []
96+
component.writeValue(mdl);
97+
const res = component.validate();
98+
expect(res).toBeFalsy();
99+
});
100+
101+
it('should not return required error', () => {
102+
const mdl = ['one']
103+
104+
component.writeValue(mdl);
105+
const res = component.validate();
106+
expect(res).toBeFalsy();
107+
});
108+
});
109+
110+
describe('onChange()', () => {
111+
it('should update value', () => {
112+
spyOn(component, 'onModelChange');
113+
component.onChange("{}");
114+
expect(component.valid).toBeTruthy();
115+
expect(component.onModelChange).toHaveBeenCalledWith({});
116+
});
117+
118+
it('should not update value when input is invalid', () => {
119+
spyOn(component, 'onModelChange');
120+
component.onChange("{1111}");
121+
expect(component.valid).toBeFalsy();
122+
expect(component.onModelChange).not.toHaveBeenCalled();
123+
});
124+
});
125+
126+
});
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { ElementRef, Component, ViewChild, Input } from '@angular/core';
2+
import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms';
3+
4+
@Component({
5+
providers: [
6+
{
7+
multi: true,
8+
provide: NG_VALUE_ACCESSOR,
9+
useExisting: JsonControlValueAccessorComponent
10+
}
11+
],
12+
selector: 'json-control',
13+
template: '<textarea class="form-control" rows="3" #textarea (input)="onChange($event.target.value)"></textarea>'
14+
})
15+
export class JsonControlValueAccessorComponent implements ControlValueAccessor {
16+
public disabled = false;
17+
public valid = true;
18+
19+
@ViewChild('textarea', { static: false }) textarea: ElementRef;
20+
@Input() required = false;
21+
onModelChange = (model: any) => { };
22+
onModelTouched = (model: any) => { };
23+
24+
writeValue(value: any) {
25+
if (value) {
26+
this.textarea.nativeElement.value = JSON.stringify(value);
27+
}
28+
}
29+
30+
registerOnChange(fn: any): void {
31+
this.onModelChange = fn;
32+
}
33+
34+
registerOnTouched(fn: any): void {
35+
this.onModelTouched = fn;
36+
}
37+
38+
setDisabledState(disabled: boolean): void {
39+
this.textarea.nativeElement.disabled = disabled;
40+
this.disabled = disabled;
41+
}
42+
43+
onChange(value): void {
44+
try {
45+
const json = JSON.parse(value);
46+
this.valid = true;
47+
this.onModelChange(json);
48+
} catch (e) {
49+
this.valid = false;
50+
}
51+
52+
}
53+
54+
validate() {
55+
const err: { required?: boolean } = {};
56+
let valid = true;
57+
58+
if (!this.valid || this.required && !this.textarea.nativeElement.value) {
59+
err.required = true;
60+
valid = false;
61+
}
62+
63+
return valid ? null : err;
64+
}
65+
66+
}

src/app/fields/index.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import { FormLabelComponent } from './form-label/form-label.component';
1010
import { FormDateComponent } from './form-date/form-date.component';
1111
import { FieldDictionary } from '../../types';
1212
import { MulticheckboxControlComponent } from './form-multicheckbox/multicheckbox-control/multicheckbox-control.component';
13+
import { FormJsonComponent } from './form-json/form-json.component';
14+
import { JsonControlValueAccessorComponent } from './form-json/json-control/json-control.component';
1315

1416
export const Fields: FieldDictionary = {
1517
text: FormInputComponent,
@@ -21,7 +23,8 @@ export const Fields: FieldDictionary = {
2123
radio: FormRadioComponent,
2224
checkbox: FormCheckboxComponent,
2325
label: FormLabelComponent,
24-
date: FormDateComponent
26+
date: FormDateComponent,
27+
json: FormJsonComponent
2528
};
2629

2730
export const FieldComponents = [
@@ -35,8 +38,10 @@ export const FieldComponents = [
3538
FormCheckboxComponent,
3639
FormLabelComponent,
3740
FormDateComponent,
41+
FormJsonComponent
3842
];
3943

4044
export const CustomInputs = [
41-
MulticheckboxControlComponent
45+
MulticheckboxControlComponent,
46+
JsonControlValueAccessorComponent
4247
];

0 commit comments

Comments
 (0)