Integrating CodeMirror with Angular2 (typescript)

☆樱花仙子☆ 提交于 2019-12-08 00:39:00

问题


I'm currently working on adding a CodeMirror editor to a project, an Angular2 project more precisely. But Im' having trouble doing it. The instantiation of my editor doesn't seem to work correctly. My code is the following:

editor.component.ts

import {Component} from 'angular2/core'
import {BrowserDomAdapter} from 'angular2/platform/browser'

declare var CodeMirror: any;

@Component({
    selector: 'editor',
    templateUrl: './meang2app/partials/editor.html'
})

export class Editor{
    dom: BrowserDomAdapter;
    editor: any;
    constructor(){
        this.dom = new BrowserDomAdapter();
        this.editor = new CodeMirror.fromTextArea(this.dom.query("textarea"), {lineNumbers: true, mode: {name: "javascript", globalVars: true}});
    }
}

editor.html

<textarea id="code" name="code">
    <!-- Where a CodeMirror instance should be rendered -->
</textarea>

In my index.html file, I do something like this:

<html>
<head>

    <title>Angular 2</title>

    <script src="node_modules/es6-shim/es6-shim.min.js"></script>
    <script src="node_modules/systemjs/dist/system-polyfills.js"></script>
    <script src="node_modules/angular2/bundles/angular2-polyfills.js"></script>
    <script src="node_modules/systemjs/dist/system.src.js"></script>
    <script src="node_modules/rxjs/bundles/Rx.js"></script>
    <script src="node_modules/angular2/bundles/angular2.dev.js"></script>
    <script src="node_modules/angular2/bundles/http.dev.js"></script>
    <script src="node_modules/angular2/bundles/router.dev.js"></script>
    <script src="codemirror/lib/codemirror.js"></script>
    <script src="codemirror/addon/hint/show-hint.js"></script>
    <script src="codemirror/addon/hint/javascript-hint.js"></script>
    <script src="codemirror/mode/javascript/javascript.js"></script>

    <script>
        System.config({
            packages: {        
                app: {
                    format: 'register',
                    defaultExtension: 'js'
                }
            }
        });
        System.import('app/dist/boot')
            .then(null, console.error.bind(console));
    </script>

</head>

<body>
        <app>Loading</app>
</body>

</html>

I'm bootstrapping my app this way, but I don't think that's where the issue comes from.

boot.ts

import {bootstrap} from 'angular2/platform/browser'
import {AppComponent} from './app.component'

bootstrap(AppComponent);

When I try and run the app, firefox tells me that 'EXCEPTION: Error during instantiation of Editor!.', and that 'ORIGINAL EXCEPTION: TypeError: textarea is null' as well. Any idea about what's going on, and how to fix this?

With all my gratitude


回答1:


Thank you so much for your help, I followed some of the tips given in your link and made it by using a directive. I went through some issues after that too, but no big deal: I just didn't know loading a link tag via a component wasn't possible. If you're reading this and encountered with a similar problem, here was my solution:

editor.component.ts

import {Component, ElementRef} from 'angular2/core'
import {EditorDirective} from './editor.directive'

@Component({
    selector: 'editor',
    template: `
    <textarea editor id="code" name="code">
        // Some content
    </textarea>
`,
    directives: [EditorDirective]
})


export class EditorComponent{
    constructor(){}
}

editor.directive.ts

import {Directive, ElementRef, Renderer} from 'angular2/core'

declare var CodeMirror: any;

@Directive({
    selector: '[editor]'
})

export class EditorDirective {
    editor: any;
    constructor(public element: ElementRef, public renderer: Renderer){
        this.editor = new CodeMirror.fromTextArea(element.nativeElement, {lineNumbers: true, mode: {name: "javascript", globalVars: true}});
    }
}

And to finish with, index.html:

<html>
    <head>

    <title>Angular 2 and CodeMirror</title>

    <script src="node_modules/es6-shim/es6-shim.min.js"></script>
    <script src="node_modules/systemjs/dist/system-polyfills.js"></script>

    <script src="node_modules/angular2/bundles/angular2-polyfills.js"></script>
    <script src="node_modules/systemjs/dist/system.src.js"></script>
    <script src="node_modules/rxjs/bundles/Rx.js"></script>
    <script src="node_modules/angular2/bundles/angular2.dev.js"></script>
    <script src="node_modules/angular2/bundles/http.dev.js"></script>
    <script src="node_modules/angular2/bundles/router.dev.js"></script>
    <script src="codemirror-5.14.2/lib/codemirror.js"></script>
    <script src="codemirror-5.14.2/addon/hint/show-hint.js"></script>
    <script src="codemirror-5.14.2/addon/hint/javascript-hint.js"></script>
    <script src="codemirror-5.14.2/mode/javascript/javascript.js"></script>
    <script src="codemirror-5.14.2/mode/markdown/markdown.js"></script>

    <link rel=stylesheet href="codemirror-5.14.2/doc/docs.css">
    <link rel="stylesheet" href="codemirror-5.14.2/lib/codemirror.css">
    <link rel="stylesheet" href="codemirror-5.14.2/addon/hint/show-hint.css">

    <script>
        System.config({
            packages: {        
                meang2app: {
                format: 'register',
                defaultExtension: 'js'
                }
            }
        });
      System.import('meang2app/dist/editor.component')
          .then(null, console.error.bind(console));
    </script>

    </head>

  <body>
      <app>Loading</app>
  </body>

</html>



回答2:


You should refrain from using ElementRef as per angular 2 documentation. Particularly, you should avoid:

tight coupling between your application and rendering layers which will make it impossible to separate the two and deploy your application into a web worker.

Use Renderer2 instead:

editor.directive.ts

@Directive({
  selector: '[editor]'
})
export default class EditorDirective {
  editor: any;

  constructor(private _renderer: Renderer) {}

  ngAfterViewInit() {
    this.editor = CodeMirror.fromTextArea(
      this._renderer.selectRootElement('[editor]'),
      {
        lineNumbers: true, 
        mode: {name: "javascript", globalVars: true}
      }
    );
  }
}



回答3:


The problem might be that you don't fetch the correct DOM element through the BrowserDomAdapter (I guess it will look up the textarea from the html root and not from the current component context, which isn't known to it) and thereby the textbox gets null.

Here are some ways that describe how you can get a reference to an element inside the template and use that inside your component code (e.g. in order to instantiate codemirror): Angular2: What is the best way to get a reference of a template element



来源:https://stackoverflow.com/questions/37092142/integrating-codemirror-with-angular2-typescript

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!