ngx-monaco-editor: ModelService: Cannot add model because it already exists

ERROR Error: Uncaught (in promise): Error: ModelService: Cannot add model because it already exists!

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 2
  • Comments: 15

Most upvoted comments

@CoderPoet Write URI as Unique names then it will fix ngx-monaco editor can t add modal error

I found the same error.

I create an editor in one of my components, when navigate away and come back, I see this in the console.

export class SomeComponent implements OnInit { 
  editorOptions = {
    theme: 'vs', 
    language: 'json',
  }; 

  model: NgxEditorModel = {
    value: '{}',
    language: 'json',
    uri: 'foo.json'
  };

And in my template:

<ngx-monaco-editor [options]="editorOptions" [model]="model"></ngx-monaco-editor>

As mentioned by @viccsjain, I’m not currently disposing of the model. Unfortunately I was not able to find the right way to do it in the documentation. What would it be the correct way of doing it?

@CoderPoet this error mostly comes when we dispose the editor without disposing model. Could you please provide more details ? Are you disposing editor and then trying to create again ?

@ajaybabum this was my problem as well. I was initializing multiple instances and the uris were not unique. Thanks.

@xocoatzin For our situtation (not multiple editors, but routing back and forth to an editor), I’ve done a couple of “hacks” to get around it.

The main one is to simply force a browser refresh if it fails to load. Not very elegant because then the user has to download your whole app again:

ngOnInit(): void {
  setTimeout(() => {
    const editorContainers = document.getElementsByClassName('editor-container');
    const editorFailedToLoad =
      editorContainers.length > 0 && editorContainers[0].innerHTML === '';

    if (editorFailedToLoad) location.reload();
  }, 1000);
}

The second fix reduces the amount of times this is necessary by using “sticky routing” (when a user leaves a route, the components aren’t destroyed and are re-used when they return - this means the editor doesn’t get recreated):

A new class, CustomRouteReuseStrategy:

import {
  RouteReuseStrategy,
  DetachedRouteHandle,
  ActivatedRouteSnapshot
} from '@angular/router';

export class CustomRouteReuseStrategy implements RouteReuseStrategy {
  handlers: { [key: string]: DetachedRouteHandle } = {};

  shouldDetach(route: ActivatedRouteSnapshot): boolean {
    return true;
  }

  store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
    this.handlers[route.routeConfig.path] = handle;
  }

  shouldAttach(route: ActivatedRouteSnapshot): boolean {
    return !!route.routeConfig && !!this.handlers[route.routeConfig.path];
  }

  retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle {
    if (!route.routeConfig) return null;
    return this.handlers[route.routeConfig.path];
  }

  shouldReuseRoute(
    future: ActivatedRouteSnapshot,
    current: ActivatedRouteSnapshot
  ): boolean {
    return future.routeConfig === current.routeConfig;
  }
}

You can obviously adjust this class to fit your needs. This example uses sticky routing for every route.

And then in your app.module.ts:

...
providers: [
  { provide: RouteReuseStrategy, useClass: CustomRouteReuseStrategy }
]
...

This seems to fix the majority of navigation failures, and, when it doesn’t, the first “hack” kicks in to refresh the page.

Not ideal at all, but manageable.