Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Integrate the ng2-ace library into a freshly created angular-cli (angular2) project using SystemJS

I just created an angular2 project with the latest angular-cli tool. I now want to get the ace editor up and running using the ng2-ace library. I want to do it in a clean approach using SystemJS as the module loader.

I did

npm install --save ng2-ace

then I added the following two lines to angular-cli-builds.js to the vendorNpmFiles array

'ng2-ace/index.js',
'brace/**/*.js

then I added the following to system-config.ts

 const map: any = {
   'ng2-ace': 'vendor/ng2-ace',
   'brace': 'vendor/brace'
 };

 /** User packages configuration. */
 const packages: any = {
   'brace': {
     format: 'cjs',
     defaultExtension: 'js',
     main: 'index.js'
   },
   'ng2-ace': {
     format: 'cjs',
     defaultExtension: 'js',
     main: 'index.js'
   }
 };

Now I tried importing the directive from a component

import { AceEditorDirective } from 'ng2-ace';

This makes the compiler ng serve aborting with the following error:

The Broccoli Plugin: [BroccoliTypeScriptCompiler] failed with:
Error: Typescript found the following errors:
Cannot find module 'ng2-ace'.

I tried to follow the Readme from angular-cli and got the google material design library working. However, I don't know what I do wrong when trying to load the ng2-ace library.

like image 341
Jerry Penson Avatar asked Oct 19 '22 06:10

Jerry Penson


1 Answers

I think the reason this is so hard is that there's no typings library provided. I was able to get the rough equivalent of this working by adding a few things. My version has a pretty static configuration but you can enhance it.

system-config needs this:

const map:any = {
  'brace': 'vendor/brace',
  'w3c-blob': 'vendor/w3c-blob',
  'buffer': 'vendor/buffer-shims'
};

it may also need:

const packages:any = {
  'w3c-blob': {
    format: 'cjs',
    defaultExtension: 'js',
    main: 'index.js'
  },

  'brace': {
    format: 'cjs',
    defaultExtension: 'js',
    main: 'index.js'
  },

  'buffer': {
    format: 'cjs',
    defaultExtension: 'js',
    main: 'index.js'
  }
};

Then you also need to add these things as npm dependencies in angular-cli-build.js:

module.exports = function(defaults) {
  return new Angular2App(defaults, {
    vendorNpmFiles: [
      /* your stuff goes here, then add: */
      'brace/**/*.js',
      'w3c-blob/index.js',
      'buffer-shims/index.js'
 ]
});

That pretty much gets you everything you need as far as dependencies are concerned. At this point I added my own directive. The important parts are here:

import { Directive, ElementRef, EventEmitter } from '@angular/core';

Now import brace itself plus whatever modes and themes you will be using:

import 'brace';
declare let ace;

import 'vendor/brace/mode/javascript';
import 'vendor/brace/theme/monokai';

The 'declare let ace' lets you have access to brace even though there's no typings and it's not a real typescript module.

I named my directive 'js-editor' and you attach it to a tag of an appropriate height and width. The docs for brace say to apply a 'block' style to the div it as well. Then declare the directive:

@Directive({
  selector: '[js-editor]',
  inputs: ['text'],
  outputs: ['textChanged', 'editorRef']
})
export class JsEditor {

  editor : any;
  textChanged : EventEmitter<any>;
  editorRef : EventEmitter<any>;
  value : string;

  set text(value) {
    // if(value === this.oldVal) return;
    // this.editor.setValue(value);
    // this.editor.clearSelection();
    this.editor.focus();
  }

  constructor(elementRef : ElementRef) {
    this.textChanged = new EventEmitter();
    this.editorRef = new EventEmitter();

    const el = elementRef.nativeElement;
    el.classList.add('editor');

Setting the base path is the key element to brace being able to find modes and themes. This is really the wrong place to set it - it should be done globally, and ONLY ONCE ... but this was just an experiment to see if it would work so I did it here:

    ace.config.set('basePath', 'vendor/brace');

Finally, create the editor:

    this.editor = ace.edit(el);

and then set your mode and theme. Note that these modes/themes LOOK like paths, but they really are not. Ace (or perhaps Brace) will use these strings to generate the path using the basePath above:

    this.editor.getSession().setMode('ace/mode/javascript');
    this.editor.setTheme('ace/theme/monokai');

    setTimeout(() => {
      this.editorRef.next(this.editor);
    });

    this.editor.on('change', () => {
        /* do whatever you want here */
    });
  }
}

That's the general idea. It really needs to be wrapped up into a nice configurable directive along the lines of ng2-ace but I'm not the right guy to do that, I just wanted to get you headed in the right direction.

--Chris

like image 115
wz2b Avatar answered Nov 01 '22 19:11

wz2b