# Incorrect schema passed into custom renderer when type is array - Angular

**URL:** <https://jsonforms.discourse.group/t/incorrect-schema-passed-into-custom-renderer-when-type-is-array-angular/2500>\
**Category:** General\
**Created:** [December 9, 2024, 5:29pm UTC](https://jsonforms.discourse.group/t/incorrect-schema-passed-into-custom-renderer-when-type-is-array-angular/2500 "2024-12-09T17:29:22Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![anasisma](https://yyz2.discourse-cdn.com/free1/user_avatar/jsonforms.discourse.group/anasisma/32/1173_2.png) [@anasisma](https://jsonforms.discourse.group/u/anasisma)\
**Post date:** [December 9, 2024, 5:29pm UTC](https://jsonforms.discourse.group/t/incorrect-schema-passed-into-custom-renderer-when-type-is-array-angular/2500/1 "2024-12-09T17:29:22Z")

</div>

I am making a custom angular renderer for oneOf, and it works fine with a single oneOf object. However, if I make the schema contain an array of oneOf objects, the schema that gets passed into the renderer becomes incorrect.

For example, with the following schema, the definitions section just disappears, and then the schema is unable to parse the references in the oneOf. When I look into the renderer instance, I can see that it has a jsonFormsService object that contains the correct schema, but for some reason the instance is using its own version of the schema.

Schema:

```auto
{
  "definitions": {
    "address": {
      "type": "object",
      "title": "Address",
      "properties": {
        "street_address": {
          "type": "string"
        },
        "city": {
          "type": "string"
        },
        "state": {
          "type": "string"
        }
      },
      "required": ["street_address", "city", "state"]
    },
    "user": {
      "type": "object",
      "title": "User",
      "properties": {
        "name": {
          "type": "string"
        },
        "mail": {
          "type": "string"
        }
      },
      "required": ["name", "mail"]
    }
  },
  "type": "array",
  "items": {
    "type": "object",
    "properties": {
      "addressOrUser": {
        "oneOf": [
          {
            "$ref": "#/definitions/address"
          },
          {
            "$ref": "#/definitions/user"
          }
        ]
      }
    }
  }
}

```

Here is the renderer:

```auto
import { ChangeDetectorRef, Component } from "@angular/core";
import { JsonFormsAngularService, JsonFormsControl } from "@jsonforms/angular";
import { angularMaterialRenderers } from "@jsonforms/angular-material";
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
        *ngIf="shouldRenderJsonForms"
        [data]="data"
        [schema]="selectedOption"
        [renderers]="this.renderers"
      ></jsonforms>
    </div>
  `,
})
export class oneOfRenderer extends JsonFormsControl {
  renderers = [...angularMaterialRenderers];

  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() {
    console.log(this);
    // 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;

    this.selectedOption = this.options.find(
      (option) => option.title === event.target.value
    );

    console.log(event.target);

    this.cdr.detectChanges();
    this.shouldRenderJsonForms = true;
  }

  extractOptions(schema: JsonSchema) {
    console.log(schema);
    for (const key in schema.properties) {
      if (schema.properties.hasOwnProperty(key)) {
        const property = schema.properties[key];

        if (property.oneOf) {
          const refs = property.oneOf.map((option: any) => option);
          console.log(refs);

          const options = [];

          for (const key in refs) {
            console.log(refs[key]["$ref"]);
            options.push(resolveSchema(schema, refs[key]["$ref"], schema));
          }

          console.log(options);
          return options;
        }
      }
    }
  }
}

```

---

<div class="post-metadata">

**Author:** ![sdirix](https://yyz2.discourse-cdn.com/free1/user_avatar/jsonforms.discourse.group/sdirix/32/1073_2.png) [@sdirix](https://jsonforms.discourse.group/u/sdirix)\
**Post date:** [December 10, 2024, 8:12am UTC](https://jsonforms.discourse.group/t/incorrect-schema-passed-into-custom-renderer-when-type-is-array-angular/2500/2 "2024-12-10T08:12:03Z")

</div>

Hi @anasisma,

We always hand over the resolved (sub)schema as `schema` to the renderers as usually this contains sufficient information for rendering. In case a renderer encounters a `$ref` and they want to resolve it, they can use either the form-wide whole schema from the service, as you discovered yourself, or just [access the (same) `rootSchema` which we also hand over](https://github.com/eclipsesource/jsonforms/blob/12a82eb2fb2d34a01bb7c3058a2d5ae98a1a3172/packages/core/src/mappers/renderer.ts#L655-L656).

---

<div class="post-metadata">

**Author:** ![anasisma](https://yyz2.discourse-cdn.com/free1/user_avatar/jsonforms.discourse.group/anasisma/32/1173_2.png) [@anasisma](https://jsonforms.discourse.group/u/anasisma)\
**Post date:** [December 10, 2024, 9:14pm UTC](https://jsonforms.discourse.group/t/incorrect-schema-passed-into-custom-renderer-when-type-is-array-angular/2500/3 "2024-12-10T21:14:40Z")

</div>

Hey @sdirix

I was trying to implement your suggestion, but I’m finding that my renderer instance doesn’t receive the data, path, rootSchema and many other properties from the parent. I’m guessing I set something up wrong, but not sure what. Any help would be appreciated.

Here is the updated renderer:

```auto
import { ChangeDetectorRef, Component } from "@angular/core";
import { JsonFormsAngularService, JsonFormsControl } from "@jsonforms/angular";
import { Generate, 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
          [schema]="this.selectedOption"
          [uischema]="this.selectedUi"
		  [path]="path"
        ></jsonforms-outlet>
    </div>
  `,
})
export class oneOfRenderer extends JsonFormsControl {
  options: any[];
  allUiSchemas: { [key: string]: any } = {};
  selectedOption: JsonSchema;
  selectedUi: JsonSchema;

  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.selectedUi = Generate.uiSchema(this.selectedOption);
    }
	console.log(this)
  }

  onOptionChange(event: any) {

    const selectedTitle = event.target.value;

    const newSelectedOption = this.options.find(
      (option) => option.title === selectedTitle
    );

    if (newSelectedOption !== this.selectedOption) {

      this.selectedOption = newSelectedOption;

      if (!this.allUiSchemas[selectedTitle]) {
        this.allUiSchemas[selectedTitle] = Generate.uiSchema(this.selectedOption);
      }

      this.selectedUi = this.allUiSchemas[selectedTitle];

      this.cdr.detectChanges();
	  this.jsonFormsService.refresh()
    }
  }

  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;
  }
}

```

Here is a screenshot of an example instance of the renderer:

 ![image](https://global.discourse-cdn.com/free1/uploads/jsonforms/original/2X/9/9fb25bb46f1c568bebbcd4e1ed488ebe5f12a108.png)

---

<div class="post-metadata">

**Author:** ![sdirix](https://yyz2.discourse-cdn.com/free1/user_avatar/jsonforms.discourse.group/sdirix/32/1073_2.png) [@sdirix](https://jsonforms.discourse.group/u/sdirix)\
**Post date:** [December 12, 2024, 7:29am UTC](https://jsonforms.discourse.group/t/incorrect-schema-passed-into-custom-renderer-when-type-is-array-angular/2500/4 "2024-12-12T07:29:56Z")

</div>

Hi @anasisma,

The mapper logic is executed in `ngOnInit`, see [here](https://github.com/eclipsesource/jsonforms/blob/21400a824fff65f8b1fbf13b6eef497f77a18f26/packages/angular/src/library/abstract-control.ts#L100-L141).

You are overriding `ngOnInit` without calling `super()`, therefore this is then lost for you.
