Skip to content

Commit

Permalink
Make Angular component consumable multiple times
Browse files Browse the repository at this point in the history
The JSONForms Angular component needs the jsonforms service
to set the right data, schema, uischema etc.
The problem is, that the service is a singleton,
as it is registering as a root service.

This commit introduces a new root component `jsonforms`
which allows to pass all parameters into the component.
The component is now initializing the service internally.
Furthermore, the service is not registered as a root service anymore.
Now multiple jsonforms components can be used next to each other.

The angular tests were adapted to work with the behavior change.
  • Loading branch information
eneufeld committed Dec 2, 2020
1 parent a51e373 commit 5a0e291
Show file tree
Hide file tree
Showing 14 changed files with 471 additions and 284 deletions.
44 changes: 44 additions & 0 deletions MIGRATION.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,47 @@
# Migrating to JSON Forms 2.5 for Angular users
The JsonFormsAngularService is not provided in the root anymore.
To keep the old behavior, you need to provide it manually in the module.

The preferred way is using the new JsonForms Component though.
This component wraps the service and allows the user to interact with it using databinding.

Example:

```ts
import { Component } from '@angular/core';
import { angularMaterialRenderers } from '../../src/index';
export const schema = {
type: 'object',
properties: {
name: {
type: 'string'
}
},
required: ['name']
};
export const data = {name: 'Send email to Adrian'};

@Component({
selector: 'app-root',
template: `
<div>Data: {{ data | json }}</div>
<jsonforms
[data]="data"
[schema]="schema"
[renderers]="renderers"
(dataChange)="onDataChange($event)"
></jsonforms>
`
})
export class AppComponent {
readonly renderers = angularMaterialRenderers;
data: any;
onDataChange(data: any) {
this.data = data;
}
}
```

# Migrating to JSON Forms 2.5 for React users

In version 2.5 we made the `redux` dependency within the `react` package optional.
Expand Down
105 changes: 51 additions & 54 deletions packages/angular-material/example/app/app.component.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
The MIT License
Copyright (c) 2017-2019 EclipseSource Munich
Copyright (c) 2017-2020 EclipseSource Munich
https://github.com/eclipsesource/jsonforms
Permission is hereby granted, free of charge, to any person obtaining a copy
Expand All @@ -23,19 +23,31 @@
THE SOFTWARE.
*/
import { Component } from '@angular/core';
import { JsonFormsAngularService } from '@jsonforms/angular';
import { ExampleDescription, getExamples } from '@jsonforms/examples';
import {
Actions,
getData,
getUiSchema,
setLocale,
setReadonly,
unsetReadonly,
setConfig
UISchemaElement,
UISchemaTester
} from '@jsonforms/core';
import { getExamples } from '@jsonforms/examples';
import { of } from 'rxjs';
import { map } from 'rxjs/operators';
import { angularMaterialRenderers } from '../../src/index';
const uiSchema = {
type: 'HorizontalLayout',
elements: [
{
type: 'Control',
scope: '#/properties/buyer/properties/email'
},
{
type: 'Control',
scope: '#/properties/status'
}
]
};
const itemTester: UISchemaTester = (_schema, schemaPath, _path) => {
if (schemaPath === '#/properties/warehouseitems/items') {
return 10;
}
return -1;
};
@Component({
selector: 'app-root',
template: `
Expand All @@ -45,7 +57,7 @@ import { map } from 'rxjs/operators';
Example:
<select (change)="onChange($event)">
<option
*ngFor="let example of exampleData$ | async"
*ngFor="let example of examples"
value="{{ example.name }}"
label="{{ example.label }}"
>
Expand All @@ -57,65 +69,50 @@ import { map } from 'rxjs/operators';
<button (click)="changeLocale('de-DE')">Change locale to de-DE</button>
<button (click)="changeLocale('en-US')">Change locale to en-US</button>
Current locale: {{ currentLocale }}
<button (click)="setReadonly()">
<button (click)="toggleReadonly()">
{{ readonly ? 'Unset' : 'Set' }} Readonly
</button>
</div>
<jsonforms-outlet></jsonforms-outlet>
<jsonforms
[data]="selectedExample.data"
[schema]="selectedExample.schema"
[uischema]="selectedExample.uischema"
[renderers]="renderers"
(dataChange)="onDataChange($event)"
[locale]="currentLocale"
[uischemas]="uischemas"
[readonly]="readonly"
></jsonforms>
`
})
export class AppComponent {
readonly exampleData$ = of(getExamples());
readonly renderers = angularMaterialRenderers;
readonly examples = getExamples();
selectedExample: ExampleDescription;
currentLocale = 'en-US';
private readonly = false;
data: any;
uischemas: { tester: UISchemaTester; uischema: UISchemaElement; }[] = [
{ tester: itemTester, uischema: uiSchema }
];

constructor(private jsonformService: JsonFormsAngularService) {
const examples = getExamples();
const selectedExample = examples[0];
this.jsonformService.updateCore(
Actions.init(
selectedExample.data,
selectedExample.schema,
selectedExample.uischema
)
);

this.jsonformService.$state.pipe(
map(state => getData(state))
).subscribe(data => this.data = data);
constructor() {
this.selectedExample = this.examples[19];
}
examples() {
return getExamples();

onDataChange(data: any) {
this.data = data;
}

onChange = (ev: any) => {
const examples = getExamples();
const selectedExample = examples.find(e => e.name === ev.target.value);
this.jsonformService.updateCore(
Actions.init(
selectedExample.data,
selectedExample.schema,
selectedExample.uischema
)
);
this.jsonformService.updateConfig(setConfig(selectedExample.config));
this.jsonformService.updateLocale(setLocale(this.currentLocale));
};
onChange(ev: any) {
this.selectedExample = this.examples.find(e => e.name === ev.target.value);
}

changeLocale(locale: string) {
this.currentLocale = locale;
this.jsonformService.updateLocale(setLocale(locale));
}

setReadonly() {
const uischema = getUiSchema(this.jsonformService.getState());
if (this.readonly) {
unsetReadonly(uischema);
} else {
setReadonly(uischema);
}
toggleReadonly() {
this.readonly = !this.readonly;
this.jsonformService.updateCore(Actions.setUISchema(uischema));
}
}
42 changes: 1 addition & 41 deletions packages/angular-material/example/app/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
The MIT License
Copyright (c) 2017-2019 EclipseSource Munich
Copyright (c) 2017-2020 EclipseSource Munich
https://github.com/eclipsesource/jsonforms
Permission is hereby granted, free of charge, to any person obtaining a copy
Expand All @@ -25,13 +25,9 @@
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { Actions, UISchemaTester } from '@jsonforms/core';
import { AppComponent } from './app.component';
import { JsonFormsAngularMaterialModule } from '../../src/module';

import { initialState } from './store';
import { JsonFormsAngularService } from '@jsonforms/angular';

@NgModule({
declarations: [AppComponent],
imports: [
Expand All @@ -43,40 +39,4 @@ import { JsonFormsAngularService } from '@jsonforms/angular';
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class AppModule {
constructor(jsonformsService: JsonFormsAngularService) {

jsonformsService.init(initialState.jsonforms);
const example = initialState.examples.data[19];

jsonformsService.updateCore(
Actions.init(example.data, example.schema, example.uischema)
);

const uiSchema = {
type: 'HorizontalLayout',
elements: [
{
type: 'Control',
scope: '#/properties/buyer/properties/email'
},
{
type: 'Control',
scope: '#/properties/status'
}
]
};
const itemTester: UISchemaTester = (_schema, schemaPath, _path) => {
if (schemaPath === '#/properties/warehouseitems/items') {
return 10;
}
return -1;
};
jsonformsService.updateUiSchema(Actions.registerUISchema(itemTester, uiSchema));
}
}

/*
Copyright 2017-2018 Google Inc. All Rights Reserved.
Use of this source code is governed by an MIT-style license that
can be found in the LICENSE file at http://angular.io/license
*/
7 changes: 5 additions & 2 deletions packages/angular-material/test/autocomplete-control.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -304,9 +304,12 @@ describe('AutoComplete control Error Tests', () => {
setupMockStore(fixture, {
uischema,
schema,
data,
errors
data
});
const formsService = getJsonFormsService(component);
formsService.updateCore(Actions.updateErrors(errors));
formsService.refresh();

component.ngOnInit();
fixture.detectChanges();
const debugErrors: DebugElement[] = fixture.debugElement.queryAll(
Expand Down
27 changes: 15 additions & 12 deletions packages/angular-material/test/date-control.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,9 @@ describe('Date control Base Tests', () => {
it('id should be present in output', () => {
component.uischema = uischema;
component.id = 'myId';
getJsonFormsService(component).init();
getJsonFormsService(component).updateCore(Actions.init(data, schema));
getJsonFormsService(component).init(
{core: {data: data, schema: schema, uischema: uischema}}
);

component.ngOnInit();
fixture.detectChanges();
Expand Down Expand Up @@ -251,17 +252,19 @@ describe('Date control Error Tests', () => {
setupMockStore(fixture, {
uischema,
schema,
data,
errors: [
{
dataPath: 'foo',
message: 'Hi, this is me, test error!',
params: '',
keyword: '',
schemaPath: ''
}
]
data
});
const formsService = getJsonFormsService(component);
formsService.updateCore(Actions.updateErrors([
{
dataPath: 'foo',
message: 'Hi, this is me, test error!',
params: '',
keyword: '',
schemaPath: ''
}
]));
formsService.refresh();

component.ngOnInit();
fixture.detectChanges();
Expand Down
Loading

0 comments on commit 5a0e291

Please sign in to comment.