Reputation: 4559
See update below
Still playing with Angular2 Beta, I'm trying to implement a scenario where an "editor" component template contains a directive wrapping the Ace editor. Thus, the "editor" component is the parent of the Ace wrapper directive, and I want to get the code from the directive or set the code into it.
While the directive alone works fine, when I include it in this component I get nothing displayed; yet no errors are shown in the browser's console. You can find a repro at this Plunker: http://plnkr.co/edit/kzclJLIX6hRMWa14A0Pb.
In my implementation, the ace.directive
directive wrapping the Ace editor has a text
property and a textChanged
event.
import {Component,Directive,EventEmitter,ElementRef} from 'angular2/core';
declare var ace: any;
@Directive({
selector: "ace-editor",
inputs: [
"text"
],
outputs: [
"textChanged"
]
})
export class AceDirective {
private editor : any;
private settingText : boolean;
public textChanged: EventEmitter<string>;
set text(s: string) {
let sOld = this.editor.getValue();
if (sOld === s) return;
this.settingText = true;
this.editor.setValue(s);
this.editor.clearSelection();
this.editor.focus();
this.settingText = false;
}
constructor(elementRef: ElementRef) {
var dir = this;
this.textChanged = new EventEmitter<string>();
let el = elementRef.nativeElement;
this.editor = ace.edit(el);
this.editor.on("change", (e) => {
if (dir.settingText) return;
dir.textChanged.next(dir.editor.getValue());
});
}
}
The editor.component
component uses the directive: it has an xml
property representing the XML code being edited. Its template contains the directive as follows:
<ace-editor id="editor" [text]="xml" (textChanged)="onXmlChanged()"></ace-editor>
i.e. the directive's text
property is bound to the parent component's xml
property, and the directive's textChanged
event is handled by the parent's
onXmlChanged
function.
This being a 2-way databinding, AFAIK I could also try:
<ace-editor id="editor" [(ngModel)]="xml"></ace-editor>
Here is the editor's code:
import {Component,EventEmitter} from "angular2/core";
import {AceDirective} from "./ace.directive";
@Component({
selector: "mit-editor",
directives: [AceDirective],
template: `<div>
<ace-editor id="editor" [text]="xml" (textChanged)="onXmlChanged()"></ace-editor>
</div>
`,
inputs: [
"xml"
]
})
export class EditorComponent {
public xml: string;
constructor() {
this.xml = "";
}
public onXmlChanged(xml: string) {
this.xml = xml;
}
}
Update #1 For some reason, the Plunker does not transpile and load my .ts files other than the preexisting ones, so I continued troubleshooting locally.
As for the question, I found I have to add the $event
argument to the call in the template (see my comment). My directive is now:
import {Component,Directive,EventEmitter,ElementRef} from 'angular2/core';
declare var ace: any;
@Directive({
selector: "ace-editor",
inputs: [
"text"
],
outputs: [
"textChanged"
]
})
export class AceDirective {
private editor : any;
public textChanged: EventEmitter<string>;
set text(s: string) {
if (s === undefined) return;
let sOld = this.editor.getValue();
if (sOld === s) return;
this.editor.setValue(s);
this.editor.clearSelection();
this.editor.focus();
}
get text() {
return this.editor.getValue();
}
constructor(elementRef: ElementRef) {
var dir = this;
this.textChanged = new EventEmitter<string>();
let el = elementRef.nativeElement;
this.editor = ace.edit(el);
let session = this.editor.getSession();
session.setMode("ace/mode/xml");
session.setUseWrapMode(true);
this.editor.on("change", (e) => {
let s = dir.editor.getValue();
dir.textChanged.next(s);
});
}
}
The editor component template contains the directive like this:
<ace-editor id="editor" [text]="xml" (textChanged)="onXmlChanged($event)"></ace-editor>
Anyway, note that if I now try to programmatically set the editor's component xml
property, Angular starts and endless loop as the change triggers the event on the ace editor which emits the event which sets the xml
property again, and so forth. Probably it's just me doing something wrong, but currently I had to use this hack in the code, and of course I don't like it:):
// ...
export class EditorComponent {
private _xml: string;
private _changeFrozenCount: number;
public set xml(s: string) {
this._xml = s;
}
public get xml() : string {
return this._xml;
}
constructor(private editorService: EditorService, private xmlService: XmlService) {
this._xml = "";
}
public onXmlChanged(xml: string) {
if (this._changeFrozenCount > 0) {
this._changeFrozenCount--;
return;
}
this._xml = xml;
}
public changeXml() {
this._changeFrozenCount = 1;
this._xml = "<sample>Hello</sample>"
}
}
Upvotes: 2
Views: 798
Reputation: 1392
You forgot to add
directives: [EditorComponent]
in your app.ts file. Currently the directives
array is empty ([]
) in your plunker. Just make this change and it works, and if you set onXmlChanged($event)
it won't even error out when you type inside the editor :)
Upvotes: 3