diff --git a/src/ngx-json-viewer/ngx-json-viewer.component.ts b/src/ngx-json-viewer/ngx-json-viewer.component.ts index 1005e40..bfe5970 100644 --- a/src/ngx-json-viewer/ngx-json-viewer.component.ts +++ b/src/ngx-json-viewer/ngx-json-viewer.component.ts @@ -1,12 +1,5 @@ import { Component, OnChanges, Input } from '@angular/core'; - -export interface Segment { - key: string; - value: any; - type: undefined | string; - description: string; - expanded: boolean; -} +import { Segment, isCyclic } from "./segment"; @Component({ selector: 'ngx-json-viewer', @@ -39,7 +32,8 @@ export class NgxJsonViewerComponent implements OnChanges { } isExpandable(segment: Segment) { - return segment.type === 'object' || segment.type === 'array'; + const { cyclic } = isCyclic(segment); + return segment.type === 'object' && !cyclic || segment.type === 'array'; } toggle(segment: Segment) { @@ -91,9 +85,9 @@ export class NgxJsonViewerComponent implements OnChanges { } else if (segment.value instanceof Date) { segment.type = 'date'; } else { + const { cyclic, err } = isCyclic(segment); segment.type = 'object'; - segment.description = 'Object ' + JSON.stringify(segment.value); - } + segment.description = cyclic ? err : 'Object ' + JSON.stringify(segment.value); } break; } } diff --git a/src/ngx-json-viewer/segment.ts b/src/ngx-json-viewer/segment.ts new file mode 100644 index 0000000..3f2192b --- /dev/null +++ b/src/ngx-json-viewer/segment.ts @@ -0,0 +1,43 @@ +export interface Segment { + key: string; + value: any; + type: undefined | string; + description: string; + expanded: boolean; +} + +// Inspiration from https://stackoverflow.com/a/46461300/4544386 +// Tackles the problem of cyclical objects. +export const isCyclic = (segment: Segment) : { cyclic: boolean, err: string } => { + let keys: string[] = [], stack: string[] = []; + let cyclic: boolean = false; + let err: string = ""; + + const detect = (obj: any, key: string) => { + if (typeof obj !== 'object') { + return; + } + + if (stack.indexOf(obj) > -1) { // it's cyclic! Print the object and its locations. + const oldIndex = stack.indexOf(obj); + const l1 = keys.join('.') + '.' + key; + const l2 = keys.slice(0, oldIndex + 1).join('.'); + err = '[Cyclic at: ' + l1 + ' = ' + l2 + ']'; + cyclic = true; + return; + } else { + keys.push(key); + stack.push(obj); + for (const k in obj) { // dive on the object's children + if (obj.hasOwnProperty(k)) { + detect(obj[k], k); + } + } + keys.pop(); + stack.pop(); + } + }; + + detect(segment.value, segment.key); + return { cyclic, err }; +};