Angular2 2-way databinding между родительским компонентом и директивой


Смотрите обновление ниже

Все еще играя с Angular2 Beta, я пытаюсь реализовать сценарий, в котором шаблон компонента "редактор"содержит директиву, обертывающую редактор Ace. Таким образом, компонент" редактор " является родителем директивы Ace wrapper, и я хочу получить код из директивы или установить код в нее.

В то время как сама директива работает нормально, когда я включаю ее в этот компонент, я не получаю ничего отображаемого; однако никакие ошибки не отображаются в этом компоненте. консоль браузера. Вы можете найти упрек в этом Плунжере: http://plnkr.co/edit/kzclJLIX6hRMWa14A0Pb .

В моей реализации директива ace.directive, обертывающая редактор Ace, имеет свойство text и событие textChanged.

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());
        });
    }
}

Компонент editor.component использует директиву: он имеет свойство xml, представляющее редактируемый XML-код. Его шаблон содержит следующую директиву:

<ace-editor id="editor" [text]="xml" (textChanged)="onXmlChanged()"></ace-editor>

То есть свойство директивы text привязано к родительскому объекту. компонент xml свойство, и событие директивы textChanged обрабатывается родительским Функция onXmlChanged.

Это 2-полосная привязка данных, AFAIK я мог бы также попробовать:

<ace-editor id="editor" [(ngModel)]="xml"></ace-editor>

Вот код редактора:

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;
    }
}

Обновление #1 По какой-то причине плунжер не перемещается и не загружает мой .файлы ts, отличные от ранее существовавших, поэтому я продолжил устранение неполадок локально.

Что касается вопроса, я обнаружил, что должен добавить аргумент $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;
    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);
        });
    }
}

Шаблон компонента редактора содержит следующую директиву:

<ace-editor id="editor" [text]="xml" (textChanged)="onXmlChanged($event)"></ace-editor>

В любом случае, обратите внимание, что если я сейчас попытаюсь программно установить свойство компонента редактора xml, Angular запускается и бесконечный цикл, поскольку изменение запускает событие в Редакторе ace, который выдает событие, которое снова устанавливает свойство xml, и так далее. Возможно, это просто я делаю что-то не так, но в настоящее время я должен был использовать этот хак в код, и конечно же мне это не нравится:):

// ...
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>"
    }
}
1 2

1 ответ:

Вы забыли добавить

directives: [EditorComponent]

В вашем приложении.файл ts. В настоящее время массив directives пуст ([]) в вашем плунжере. Просто внесите это изменение, и оно работает, и если вы установите onXmlChanged($event) , он даже не будет ошибаться при вводе в Редакторе:)