I am making a custom renderer, and using jsonforms-outlet to display an “inner” form alongside the custom renderer. Using this, I keep getting No applicable renderer found! on renderers that are included in the parent component’s renderer list.
The instance of my custom renderer has a jsonFormsService object that does contain the renderers, but for some reason it doesn’t get passed into the jsonforms-outlet.
One workaround that I tried was the use jsonforms rather than the outlet, but this kept causing Maximum call stack size exceeded whenever trying to load the jsonform.
Custom renderer:
import { ChangeDetectorRef, Component } from "@angular/core";
import { JsonFormsAngularService, JsonFormsControl } from "@jsonforms/angular";
import { JsonSchema, resolveSchema } from "@jsonforms/core";
@Component({
selector: "oneOf-renderer",
template: `
<div>
<label>{{ label }}</label>
<select (change)="onOptionChange($event)">
<option *ngFor="let option of options" [value]="option.title">
{{ option.title }}
</option>
</select>
<jsonforms-outlet
*ngIf="shouldRenderJsonForms"
[schema]="this.selectedOption"
></jsonforms-outlet>
</div>
`,
})
export class oneOfRenderer extends JsonFormsControl {
options: any;
selectedOption: any;
shouldRenderJsonForms = false; // Used to mount/unmount the json form html because it doesn't refresh when a new schema is provided
constructor(jsonFormsService: JsonFormsAngularService, private cdr: ChangeDetectorRef) {
super(jsonFormsService);
}
ngOnInit() {
this.options = this.extractOptions(this.schema);
if (this.options.length > 0) {
this.selectedOption = this.options[0];
}
this.shouldRenderJsonForms = true;
}
onOptionChange(event: any) {
this.shouldRenderJsonForms = false;
const selectedTitle = event.target.value;
const newSelectedOption = this.options.find(
(option) => option.title === selectedTitle
);
if (newSelectedOption !== this.selectedOption) {
this.selectedOption = newSelectedOption;
this.cdr.detectChanges();
}
this.shouldRenderJsonForms = true;
}
extractOptions(schema: JsonSchema) {
const options = [];
for (const key in schema.properties) {
const property = schema.properties[key];
if (property.oneOf) {
property.oneOf.forEach((option: any) => {
options.push(resolveSchema(schema, option["$ref"], schema));
});
break;
}
}
return options;
}
}
Parent component:
import { Component } from "@angular/core";
import { angularMaterialRenderers } from "@jsonforms/angular-material";
import { rankWith, schemaMatches } from "@jsonforms/core";
import schemaAsset from "../assets/schema.json";
import { oneOfRenderer } from "./oneOf-renderer";
@Component({
selector: "app-root",
templateUrl: `<div style="text-align:center">
</div>
<jsonforms
[data]="data"
[schema]="schema"
[renderers]="renderers"
></jsonforms>
`,
styleUrls: ["./app.component.css"],
})
export class AppComponent {
renderers = [
...angularMaterialRenderers,
{
renderer: oneOfRenderer,
tester: rankWith(
3,
schemaMatches((schema) => schema.hasOwnProperty("oneOf"))
),
},
];
schema = schemaAsset;
data = {};
constructor() {}
}